当前位置:网站首页>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 !
边栏推荐
- Grpc: adjust data transfer size limit
- What are the main functions of DNS? What are the benefits of IP address translation
- 2020 language and intelligent technology competition was launched, and Baidu provided the largest Chinese data set
- DB2 database generates HTML patrol Report
- Operation and maintenance platform tcapulusdb transaction management
- Pan micro reached cooperation with Tencent to help enterprises connect with banking services and support enterprise digital upgrading
- How does easynvr call the interface to modify a user-defined page?
- Leetcode969: pancake sorting (medium, dynamic programming)
- Buddha's foot before examination: the second play of leetcode
- Advanced BOM tool intelligent packaging function
猜你喜欢
随机推荐
How to formulate a domain name trademark registration scheme? What if the plan is rejected?
Where is the domain name filed? What materials are required for domain name filing?
[Tencent cloud double 12.12] from 56 yuan! New users of Tencent cloud buy for the first time, which is more cost-effective!
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
Interesting talk about decorator mode, so you will never forget it
Gin framework: RPC error code design
How does Tencent cloud server build the official version of remote desktop computer to realize remote
Buddha's foot before examination: the second play of leetcode
Leetcode838: push domino (medium)
Facebook was officially renamed meta, and the digital twin meta universe was applied!
[tcapulusdb knowledge base] manually view the online operation of tcapulusdb
Buddha's foot before examination: the third bullet of leetcode
Must the company domain name have a trademark registration? What if the registered domain name is rejected?
Does the cloud game server only provide one player? Cloud game server defense capability
A detailed explanation of the laser slam framework logo-loam
How to calculate the trademark registration fee? How much does it cost to register a trademark?
How to use annotations to record operation logs gracefully
Build your own cloud game server. What if the cloud game server is attacked
How to view the speech synthesis platform how to use the speech synthesis platform



