当前位置:网站首页>Implementing an ORM framework against SQL injection with builder mode
Implementing an ORM framework against SQL injection with builder mode
2022-06-24 02:34:00 【Tom bomb architecture】
This article is excerpted from 《 This is how design patterns should be learned 》
1 The chain style of builder's mode
Take building a course as an example , A complete course consists of PPT Courseware 、 Play back the video 、 Class notes 、 After class homework composition , But the setting order of these contents can be adjusted at will , Let's use the builder model to understand . First create a product class Course.
@Data
public class Course {
private String name;
private String ppt;
private String video;
private String note;
private String homework;
@Override
public String toString() {
return "CourseBuilder{" +
"name='" + name + '\'' +
", ppt='" + ppt + '\'' +
", video='" + video + '\'' +
", note='" + note + '\'' +
", homework='" + homework + '\'' +
'}';
}
}Then create the builder class CourseBuilder, Encapsulate the complex creation process , The creation steps are determined by the user .
public class CourseBuilder {
private Course course = new Course();
public CourseBuilder addName(String name){
course.setName(name);
return this;
}
public CourseBuilder addPpt(String ppt){
course.setPpt(ppt);
return this;
}
public CourseBuilder addVideo(String video){
course.setVideo(video);
return this;
}
public CourseBuilder addNote(String note){
course.setNote(note);
return this;
}
public CourseBuilder addHomework(String homework){
course.setHomework(homework);
return this;
}
public Course builder(){
return course;
}
}Finally, write client test code .
public static void main(String[] args) {
CourseBuilder builder = new CourseBuilder()
.addName(" Design patterns ")
.addPPT("【PPT Courseware 】")
.addVideo("【 Play back the video 】")
.addNote("【 Class notes 】")
.addHomework("【 Homework after class 】");
System.out.println(builder.build());
}Does this look familiar ? Later, when we analyze the application of builder pattern in the framework source code, we will understand . Let's look at the changes of class diagram , As shown in the figure below .
2 Use static inner classes to implement the builder pattern
in fact , In ordinary coding , We usually ignore the complexity of objects , Priority is given to using factory mode to create objects , Not the builder model . Because the function of factory pattern and builder pattern is to create a product object , The structure of factory mode is more concise and direct ( No, Builder and Director), So use more often .
In general , We are more used to using static inner classes to implement the builder pattern , That is, a product class automatically has a specific builder , It is responsible for the assembly and creation of the product , No longer need Builder and Director, such , The relationship between product representation and creation is closer , More compact structure , At the same time, it makes the form of builder mode more concise .
If the builder pattern is implemented in the form of static inner classes , Then the previous case can be rewritten as follows .
@Data
public class Course {
private String name;
private String ppt;
private String video;
private String note;
private String homework;
@Override
public String toString() {
return "Course{" +
"name='" + name + '\'' +
", ppt='" + ppt + '\'' +
", video='" + video + '\'' +
", note='" + note + '\'' +
", homework='" + homework + '\'' +
'}';
}
public static class Builder {
private Course course = new Course();
public Builder addName(String name){
course.setName(name);
return this;
}
public Builder addPpt(String ppt){
course.setPpt(ppt);
return this;
}
public Builder addVideo(String video){
course.setVideo(video);
return this;
}
public Builder addNote(String note){
course.setNote(note);
return this;
}
public Builder addHomework(String homework){
course.setHomework(homework);
return this;
}
public Course builder(){
return course;
}
}
}The client test code is as follows .
public static void main(String[] args) {
Course course = new Course.Builder()
.addName(" Design patterns ")
.addPpt("【PPT Courseware 】")
.addVideo("【 Video recording 】")
.builder();
System.out.println(course);
}
such , The code will also look more concise , It won't make people feel that there is another class .
3 Use the builder pattern to dynamically build SQL sentence
Let's take a practical case , This case refers to the open source framework JPA Of SQL Construction pattern . We're building SQL When you query the conditions , It needs to be spliced according to different conditions SQL character string . If the query conditions are complex , be SQL The process of stitching can also become very complicated , This brings great difficulties to code maintenance . therefore , We use the builder class QueryRuleSqlBuilder The complex SQL The construction process is encapsulated , use QueryRule Objects are kept exclusively SQL Query conditions , Finally, according to the query conditions , Automatic generation SQL sentence . First create QueryRule class , The code is as follows .
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* QueryRule, The main function is used to construct query conditions
*
* @author Tom
*/
public final class QueryRule implements Serializable
{
private static final long serialVersionUID = 1L;
public static final int ASC_ORDER = 101;
public static final int DESC_ORDER = 102;
public static final int LIKE = 1;
public static final int IN = 2;
public static final int NOTIN = 3;
public static final int BETWEEN = 4;
public static final int EQ = 5;
public static final int NOTEQ = 6;
public static final int GT = 7;
public static final int GE = 8;
public static final int LT = 9;
public static final int LE = 10;
public static final int ISNULL = 11;
public static final int ISNOTNULL = 12;
public static final int ISEMPTY = 13;
public static final int ISNOTEMPTY = 14;
public static final int AND = 201;
public static final int OR = 202;
private List<Rule> ruleList = new ArrayList<Rule>();
private List<QueryRule> queryRuleList = new ArrayList<QueryRule>();
private String propertyName;
private QueryRule() {}
private QueryRule(String propertyName) {
this.propertyName = propertyName;
}
public static QueryRule getInstance() {
return new QueryRule();
}
/**
* Add ascending rules
* @param propertyName
* @return
*/
public QueryRule addAscOrder(String propertyName) {
this.ruleList.add(new Rule(ASC_ORDER, propertyName));
return this;
}
/**
* Add descending rule
* @param propertyName
* @return
*/
public QueryRule addDescOrder(String propertyName) {
this.ruleList.add(new Rule(DESC_ORDER, propertyName));
return this;
}
public QueryRule andIsNull(String propertyName) {
this.ruleList.add(new Rule(ISNULL, propertyName).setAndOr(AND));
return this;
}
public QueryRule andIsNotNull(String propertyName) {
this.ruleList.add(new Rule(ISNOTNULL, propertyName).setAndOr(AND));
return this;
}
public QueryRule andIsEmpty(String propertyName) {
this.ruleList.add(new Rule(ISEMPTY, propertyName).setAndOr(AND));
return this;
}
public QueryRule andIsNotEmpty(String propertyName) {
this.ruleList.add(new Rule(ISNOTEMPTY, propertyName).setAndOr(AND));
return this;
}
public QueryRule andLike(String propertyName, Object value) {
this.ruleList.add(new Rule(LIKE, propertyName, new Object[] { value }).setAndOr(AND));
return this;
}
public QueryRule andEqual(String propertyName, Object value) {
this.ruleList.add(new Rule(EQ, propertyName, new Object[] { value }).setAndOr(AND));
return this;
}
public QueryRule andBetween(String propertyName, Object... values) {
this.ruleList.add(new Rule(BETWEEN, propertyName, values).setAndOr(AND));
return this;
}
public QueryRule andIn(String propertyName, List<Object> values) {
this.ruleList.add(new Rule(IN, propertyName, new Object[] { values }).setAndOr(AND));
return this;
}
public QueryRule andIn(String propertyName, Object... values) {
this.ruleList.add(new Rule(IN, propertyName, values).setAndOr(AND));
return this;
}
public QueryRule andNotIn(String propertyName, List<Object> values) {
this.ruleList.add(new Rule(NOTIN,
propertyName,
new Object[] { values }).setAndOr(AND));
return this;
}
// Part of the code is omitted here
public List<Rule> getRuleList() {
return this.ruleList;
}
public List<QueryRule> getQueryRuleList() {
return this.queryRuleList;
}
public String getPropertyName() {
return this.propertyName;
}
protected class Rule implements Serializable {
private static final long serialVersionUID = 1L;
private int type; // Type of rule
private String property_name;
private Object[] values;
private int andOr = AND;
public Rule(int paramInt, String paramString) {
this.property_name = paramString;
this.type = paramInt;
}
public Rule(int paramInt, String paramString,
Object[] paramArrayOfObject) {
this.property_name = paramString;
this.values = paramArrayOfObject;
this.type = paramInt;
}
public Rule setAndOr(int andOr){
this.andOr = andOr;
return this;
}
public int getAndOr(){
return this.andOr;
}
public Object[] getValues() {
return this.values;
}
public int getType() {
return this.type;
}
public String getPropertyName() {
return this.property_name;
}
}
}Then create QueryRuleSqlBuilder class .
package com.tom.vip.pattern.builder.sql;
/**
* according to QueryRule An automated build SQL sentence
* @author Tom
*
*/
public class QueryRuleSqlBuilder {
private int CURR_INDEX = 0; // Record the location of the parameter
private List<String> properties; // Save the list of column names
private List<Object> values; // Save the parameter value list
private List<Order> orders; // Save the collation list
private String whereSql = "";
private String orderSql = "";
private Object [] valueArr = new Object[]{};
private Map<Object,Object> valueMap = new HashMap<Object,Object>();
/**
* Get query conditions
* @return
*/
private String getWhereSql(){
return this.whereSql;
}
/**
* Get sort criteria
* @return
*/
private String getOrderSql(){
return this.orderSql;
}
/**
* Get a list of parameter values
* @return
*/
public Object [] getValues(){
return this.valueArr;
}
/**
* Get a list of parameters
* @return
*/
private Map<Object,Object> getValueMap(){
return this.valueMap;
}
/**
* establish SQL Constructors
* @param queryRule
*/
public QueryRuleSqlBuilder(QueryRule queryRule) {
CURR_INDEX = 0;
properties = new ArrayList<String>();
values = new ArrayList<Object>();
orders = new ArrayList<Order>();
for (QueryRule.Rule rule : queryRule.getRuleList()) {
switch (rule.getType()) {
case QueryRule.BETWEEN:
processBetween(rule);
break;
case QueryRule.EQ:
processEqual(rule);
break;
case QueryRule.LIKE:
processLike(rule);
break;
case QueryRule.NOTEQ:
processNotEqual(rule);
break;
case QueryRule.GT:
processGreaterThen(rule);
break;
case QueryRule.GE:
processGreaterEqual(rule);
break;
case QueryRule.LT:
processLessThen(rule);
break;
case QueryRule.LE:
processLessEqual(rule);
break;
case QueryRule.IN:
processIN(rule);
break;
case QueryRule.NOTIN:
processNotIN(rule);
break;
case QueryRule.ISNULL:
processIsNull(rule);
break;
case QueryRule.ISNOTNULL:
processIsNotNull(rule);
break;
case QueryRule.ISEMPTY:
processIsEmpty(rule);
break;
case QueryRule.ISNOTEMPTY:
processIsNotEmpty(rule);
break;
case QueryRule.ASC_ORDER:
processOrder(rule);
break;
case QueryRule.DESC_ORDER:
processOrder(rule);
break;
default:
throw new IllegalArgumentException("type"+rule.getType()+"not supported.");
}
}
// assemble where sentence
appendWhereSql();
// Assemble sort statements
appendOrderSql();
// Assembly parameter value
appendValues();
}
/**
* Get rid of order
*
* @param sql
* @return
*/
private String removeOrders(String sql) {
Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(sql);
StringBuffer sb = new StringBuffer();
while (m.find()) {
m.appendReplacement(sb, "");
}
m.appendTail(sb);
return sb.toString();
}
/**
* Get rid of select
*
* @param sql
* @return
*/
private String removeSelect(String sql) {
if(sql.toLowerCase().matches("from\\s+")){
int beginPos = sql.toLowerCase().indexOf("from");
return sql.substring(beginPos);
}else{
return sql;
}
}
/**
* Handle like
* @param rule
*/
private void processLike(QueryRule.Rule rule) {
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
Object obj = rule.getValues()[0];
if (obj != null) {
String value = obj.toString();
if (!StringUtils.isEmpty(value)) {
value = value.replace('*', '%');
obj = value;
}
}
add(rule.getAndOr(),rule.getPropertyName(),"like","%"+rule.getValues()[0]+"%");
}
/**
* Handle between
* @param rule
*/
private void processBetween(QueryRule.Rule rule) {
if ((ArrayUtils.isEmpty(rule.getValues()))
|| (rule.getValues().length < 2)) {
return;
}
add(rule.getAndOr(),rule.getPropertyName(),"","between",rule.getValues()[0],"and");
add(0,"","","",rule.getValues()[1],"");
}
// Part of the code is omitted here
/**
* Join in SQL Query rule queue
* @param andOr and perhaps or
* @param key Name
* @param split Interval between column name and value
* @param value value
*/
private void add(int andOr,String key,String split ,Object value){
add(andOr,key,split,"",value,"");
}
/**
* Join in SQL Query rule queue
* @param andOr and perhaps or
* @param key Name
* @param split Interval between column name and value
* @param prefix Value prefix
* @param value value
* @param suffix Value suffix
*/
private void add(int andOr,String key,String split ,String prefix,Object value,String suffix){
String andOrStr = (0 == andOr ? "" :(QueryRule.AND == andOr ? " and " : " or "));
properties.add(CURR_INDEX,
andOrStr + key + " " + split + prefix + (null != value ? " ? " : " ") + suffix);
if(null != value){
values.add(CURR_INDEX,value);
CURR_INDEX ++;
}
}
/**
* assemble where sentence
*/
private void appendWhereSql(){
StringBuffer whereSql = new StringBuffer();
for (String p : properties) {
whereSql.append(p);
}
this.whereSql = removeSelect(removeOrders(whereSql.toString()));
}
/**
* Assemble sort statements
*/
private void appendOrderSql(){
StringBuffer orderSql = new StringBuffer();
for (int i = 0 ; i < orders.size(); i ++) {
if(i > 0 && i < orders.size()){
orderSql.append(",");
}
orderSql.append(orders.get(i).toString());
}
this.orderSql = removeSelect(removeOrders(orderSql.toString()));
}
/**
* Assembly parameter value
*/
private void appendValues(){
Object [] val = new Object[values.size()];
for (int i = 0; i < values.size(); i ++) {
val[i] = values.get(i);
valueMap.put(i, values.get(i));
}
this.valueArr = val;
}
public String builder(String tableName){
String ws = removeFirstAnd(this.getWhereSql());
String whereSql = ("".equals(ws) ? ws : (" where " + ws));
String sql = "select * from " + tableName + whereSql;
Object [] values = this.getValues();
String orderSql = this.getOrderSql();
orderSql = (StringUtils.isEmpty(orderSql) ? " " : (" order by " + orderSql));
sql += orderSql;
return sql;
}
private String removeFirstAnd(String sql){
if(StringUtils.isEmpty(sql)){return sql;}
return sql.trim().toLowerCase().replaceAll("^\\s*and", "") + " ";
}
}Then create a Order class .
/**
* SQL Sort components
* @author Tom
*/
public class Order {
private boolean ascending; // Ascending or descending
private String propertyName; // Which field is in ascending order , Which field is in descending order
public String toString() {
return propertyName + ' ' + (ascending ? "asc" : "desc");
}
/**
* Constructor for Order.
*/
protected Order(String propertyName, boolean ascending) {
this.propertyName = propertyName;
this.ascending = ascending;
}
/**
* Ascending order
*
* @param propertyName
* @return Order
*/
public static Order asc(String propertyName) {
return new Order(propertyName, true);
}
/**
* Descending order
*
* @param propertyName
* @return Order
*/
public static Order desc(String propertyName) {
return new Order(propertyName, false);
}
}Finally, write client test code .
public static void main(String[] args) {
QueryRule queryRule = QueryRule.getInstance();
queryRule.addAscOrder("age");
queryRule.andEqual("addr","Changsha");
queryRule.andLike("name","Tom");
QueryRuleSqlBuilder builder = new QueryRuleSqlBuilder(queryRule);
System.out.println(builder.builder("t_member"));
System.out.println("Params: " + Arrays.toString(builder.getValues()));
}thus , The client code is very clear , The results are shown in the following figure .
Pay attention to WeChat public number 『 Tom Bomb architecture 』 reply “ Design patterns ” The complete source code is available .
This paper is about “Tom Bomb architecture ” original , Reprint please indicate the source . Technology is about sharing , I share my happiness ! If this article helps you , Welcome to pay attention and like ; If you have any suggestions, you can also leave comments or private letters , Your support is the driving force for me to adhere to my creation . Pay attention to WeChat public number 『 Tom Bomb architecture 』 More technical dry goods are available !
边栏推荐
- Which is a good human voice synthesis platform? What are the human voice synthesis application scenarios
- The United States offered 10million yuan to hunt down blackmail hackers and the energy industry became the "hardest hit" of phishing attacks | global network security hotspot
- How to build an enterprise website? Is it difficult?
- How to protect your code - ollvm (1)
- Coding -- the leader of R & D tools in the cloud native Era
- Release of the first batch of bay area group standards! Tencent security takes the lead in developing trusted digital identity standards
- In PHP, use recursive depth to merge multiple arrays
- The difference between classless routing and classless routing
- Efficient Internet access and systematic learning
- How to enable IPv6 network access for personal broadband
猜你喜欢
随机推荐
Advanced BOM tool intelligent packaging function
What is the industrial Internet? What is the industrial Internet platform?
How about Tencent cloud game server? Can the cloud game server play games
[supply chain • case] Tianneng group: the key to understanding the leading battery manufacturer to achieve the first profit fault
What are the main functions of DNS? What are the benefits of IP address translation
How to design and make ppt gradient effect?
What is the large bandwidth of IDC machine room?
How to batch output ean 13 code to pictures
Tencent cloud won the first place in the cloud natural language understanding classification task
5g Gigabit router dual band industrial grade
Can cloud computing scale flexibly? What are the characteristics of elasticity?
Wwise + GME game voice scheme: unlock more voice playing methods and let players "sound in their environment"
The same set of code returns normally sometimes and reports an error sometimes. Signature error authfailure SignatureFailure
Leetcode969: pancake sorting (medium, dynamic programming)
How to view the speech synthesis platform how to use the speech synthesis platform
In PHP, use recursive depth to merge multiple arrays
How to build video websites? What are the types of video websites?
Tidb HTAP Getting Started Guide - how to add a copy of tiflash
S2b2c platform of fresh food industry reshapes the ecosystem of agricultural products and builds a mobile fresh food comprehensive mall
The easydss on demand file upload interface calls postman to report an error. Failed to upload the file?



