文章目錄
2、Mybatis中的設計模式
“模式只是指導方針,實際工作中,可以改變模式來適應實際問題。”
2.1、Builder建造者模式
Builder模式的定義是“將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。”
此模式主要用於將複雜對象的構造過程與其表示分離。它將複雜對象的創建過程劃分爲簡單的步驟,並屏蔽複雜對象內部的特定構造細節.
2.1.1、Mybatis源碼中使用到的案例有:
在Mybatis環境的初始化過程中,SqlSessionFactoryBuilder會調用XMLConfigBuilder讀取所有的MybatisMapConfig.xml和所有的*Mapper.xml文件,構建Mybatis運行的核心對象Configuration對象,然後將該Configuration對象作爲參數構建一個SqlSessionFactory對象。
其中XMLConfigBuilder在構建Configuration對象時,也會調用XMLMapperBuilder用於讀取*Mapper文件,而XMLMapperBuilder會使用XMLStatementBuilder來讀取和build所有的SQL語句。如果是動態SQL則會使用XMLScriptBuilder,爲了協助XMLMapperBuilder可能會衍生出中間的助手比如MapperBuilderAssistant,增加緩存CacheBuilder
2.1.2、SQL類比如我們直接使用的原生SQL是構建模式麼?
new SQL(){{
SELECT("id,username,password,email,bio").FROM("author").WHERE("id=#{id}");
}}.toString();
可能這裏大家說沒有增加Builder名稱,但是我們看下SQL這個對象內部完成了一個構建SQL的操作,可以組合不同的SQL,再來看此模式主要用於將複雜對象的構造過程與其表示分離。它將複雜對象的創建過程劃分爲簡單的步驟,並屏蔽複雜對象內部的特定構造細節.是否一致呢?答案是一致的。那麼我們也可以從源碼中證明,現在已經統一抽象到AbstractSQL,由SQL對外暴露實現。
2.1.3、小結
- 對於通常意義來說源碼和我們Coding的時候會以Builder結尾來提示,見名知意。
- 使用場景:通常我們在一個函數或者構造器中無法完整的完成某個實例,那麼我們可以考慮使用構建器Builder模式
2.2、Factory工廠模式
顧名思義,通常我們有了第一個建造者模式的小結感悟,那麼工廠模式,是不是也是如此呢?*Factory.java作爲工廠模式呢?答案也是正確的。那麼我們熟悉的有哪些呢?DefaultSqlSessionFactory是SqlSessionFactory的實現。
2.2.1、DefaultSqlSessionFactory
public class DefaultSqlSessionFactory implements SqlSessionFactory {
//全局配置
private final Configuration configuration;
...省略本次無關內容...
//打開從數據源的會話
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
...省略本次無關內容...
}
但是從這裏看到的是與往常我們看到的工廠模式不太像,比如有條件判斷產生不同的對象,是不是有條件這個雖然沒有顯示用switch或者if判斷,但是通過入參數來達成目的了呢?比如不同的參數構建出不同的Session。
2.2.2、LogFactory
public final class LogFactory {
/**
* 被日誌實現的類使用
* Marker to be used by logging implementations that support markers.
*/
public static final String MARKER = "MYBATIS";
//日誌構造器
private static Constructor<? extends Log> logConstructor;
//靜態代碼初始化
static {
//SLF4j
tryImplementation(LogFactory::useSlf4jLogging);
//CommonsLog
tryImplementation(LogFactory::useCommonsLogging);
//Log4j2
tryImplementation(LogFactory::useLog4J2Logging);
//Log4j
tryImplementation(LogFactory::useLog4JLogging);
//JdkLog
tryImplementation(LogFactory::useJdkLogging);
//NoLog
tryImplementation(LogFactory::useNoLogging);
}
//阻止外部實例化
private LogFactory() {
// disable construction
}
//根據類獲取日誌接口
public static Log getLog(Class<?> aClass) {
return getLog(aClass.getName());
}
//根據類名獲取Log
public static Log getLog(String logger) {
try {
return logConstructor.newInstance(logger);
} catch (Throwable t) {
throw new LogException("Error creating logger for logger " + logger + ". Cause: " + t, t);
}
}
...省略本次無關內容...
}
這裏日誌工廠模式是怎麼實現的呢?實際上細心會發現Constructor<? extends Log> logConstructor,構造器中他們統一繼承了Log類來構造的時候找到具體的類。
2.3、單例模式
單例模式(Singleton Pattern):單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例,這個類稱爲單例類,它提供全局訪問的方法。
單例模式的要點有三個:一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。單例模式是一種對象創建型模式。
public class ErrorContext {
//行分割符號
private static final String LINE_SEPARATOR = System.getProperty("line.separator","\n");
//線程本地變量
private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<>();
//已經存儲的錯誤上下文
private ErrorContext stored;
//資源
private String resource;
//活動
private String activity;
//對象
private String object;
//消息
private String message;
//sql
private String sql;
//拋出異常
private Throwable cause;
//防止外部實例化
private ErrorContext() {
}
//錯誤上下文實例化---單例模式
public static ErrorContext instance() {
//從線程本地變量獲取,保證每個線程不一樣
ErrorContext context = LOCAL.get();
if (context == null) {
context = new ErrorContext();
LOCAL.set(context);
}
return context;
}
...省略本次無關內容...
}
ErrorContext全局只有一個上下文錯誤實例,但是這裏巧妙的使用了ThreadLocal LOCAL = new ThreadLocal<>();本來系統只應該有一個錯誤上下文,通過線程本地變量的形式,完成了每個線程請求有保留一份上下文錯誤實例。這個非常巧妙的使用了單例模式和線程本地變量相結合。如果有人說爲啥不可以構造器創建呢?private ErrorContext()而私有構造器就是爲了防止外部創建。然後第二個問題也來了,我們常說的單例模式併發的不安全呢?這裏通過線程本地變量也解決了哈哈。
2.4、代理模式
是否還記得我們之前的那個圖,Mybatis核心執行圖,這裏我們提到了動態代理,實際上代理模式就是將我們要直接執行的委託給代理執行,這裏常用的代理模式實現方式有2種,jdk的動態代理和cglib動態代理
2.5、模板模式
模板方法模式是所有模式中最爲常見的幾個模式之一,是基於繼承的代碼複用的基本技術。通常可以是將多個類通用的函數抽象成公共的複用函數,而這裏的思想代碼複用其實就是類似模板模式,比如一個抽象類約定了一些模版方法,子類可以根據自己的需要進行實現算法或者業務邏輯。
是否還記得我們跟蹤源碼的時候提到的執行器,而默認的時候開啓緩存的執行器CachingExecutor,時序圖中可以看到委託的Executor執行最終實際上是子類SimpleExecutor完成的動作。
2.6、適配器模式
適配器模式(Adapter Pattern) :將一個接口轉換成客戶希望的另一個接口,適配器模式使接口不兼容的那些類可以一起工作,其別名爲包裝器(Wrapper)。適配器模式既可以作爲類結構型模式,也可以作爲對象結構型模式。
在Mybatsi的logging包中,有一個Log接口,衆所周知我們需要兼容不同的Log實現來滿足用戶對於一個框架的使用。那麼Mybatis框架就需要進行兼容,而這個過程就用到了適配器模式
/**
* 日誌接口
*/
public interface Log {
//是否開啓debug模式
boolean isDebugEnabled();
//是否開啓trace模式
boolean isTraceEnabled();
//輸出錯誤信息
void error(String s, Throwable e);
//輸出錯誤信息
void error(String s);
//輸出debug信息
void debug(String s);
//輸出trace信息
void trace(String s);
//輸出war信息
void warn(String s);
}
讓他們統一實現Log接口,外部調用log實現根據需要委託給實現類執行。
2.7、裝飾者模式
裝飾模式(Decorator Pattern) :動態地給一個對象增加一些額外的職責(Responsibility),就增加對象功能來說,裝飾模式比生成子類實現更爲靈活。其別名也可以稱爲包裝器(Wrapper),與適配器模式的別名相同,但它們適用於不同的場合。適配器是委託給子類具體實現,用來兼容多種能力的支持。提高擴展性。而裝飾者模式屬於增強能力,主要是給一個類的增強某些功能,但不是必須的。
2.8、迭代器模式
迭代器(Iterator)模式,又叫做遊標(Cursor)模式。GOF給出的定義爲:提供一種方法訪問一個容器(container)對象中各個元素,而又不需暴露該對象的內部細節。Java的Iterator就是迭代器模式的接口,只要實現了該接口,就相當於應用了迭代器模式:比如Mybatis的PropertyTokenizer是property包中的重量級類,該類會被reflection包中其他的類頻繁的引用到。這個類實現了Iterator接口,在使用時經常被用到的是Iterator接口中的hasNext這個函數。
/**
* 屬性標記器
* @author Clinton Begin
*/
public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
//名稱
private String name;
//索引名稱
private final String indexedName;
//索引
private String index;
//子屬性
private final String children;
//根據完整的全名稱構造器
public PropertyTokenizer(String fullname) {
int delim = fullname.indexOf('.');
if (delim > -1) {
name = fullname.substring(0, delim);
children = fullname.substring(delim + 1);
} else {
name = fullname;
children = null;
}
indexedName = name;
delim = name.indexOf('[');
if (delim > -1) {
index = name.substring(delim + 1, name.length() - 1);
name = name.substring(0, delim);
}
}
//獲取名稱
public String getName() {
return name;
}
//獲取索引
public String getIndex() {
return index;
}
//獲取索引名稱
public String getIndexedName() {
return indexedName;
}
//獲取子名稱
public String getChildren() {
return children;
}
//是否還有下一個屬性
@Override
public boolean hasNext() {
return children != null;
}
//遍歷下一個屬性
@Override
public PropertyTokenizer next() {
return new PropertyTokenizer(children);
}
//移除能力沒有
@Override
public void remove() {
throw new UnsupportedOperationException("Remove is not supported, as it has no meaning in the context of properties.");
}
}
2.9、組合模式
組合(Composite)模式的定義:有時又叫作部分-整體模式,它是一種將對象組合成樹狀的層次結構的模式,用來表示“部分-整體”的關係,使用戶對單個對象和組合對象具有一致的訪問性。
Mybatis支持動態SQL的強大功能使用到了trim、if等動態元素,可以根據條件來生成不同情況下的SQL;
在DynamicSqlSource.getBoundSql方法裏,調用了rootSqlNode.apply(context)方法,apply方法是所有的動態節點都實現的接口:組合模式的簡單之處在於,所有的子節點都是同一類節點,可以遞歸的向下執行。而這個需要用XML標籤約束好,否則直接使用會報錯。所以XML會校驗是否符合dtd語法協議。
/**
* 動態SQL源
* @author Clinton Begin
*/
public class DynamicSqlSource implements SqlSource {
//全局配置
private final Configuration configuration;
//root節點
private final SqlNode rootSqlNode;
//動態sql源
public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
this.configuration = configuration;
this.rootSqlNode = rootSqlNode;
}
//獲取綁定的SQL
@Override
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(configuration, parameterObject);
//組合模式的整體root節點,直接獲取所有的動態部分SQL節點內容並組合起來
rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
context.getBindings().forEach(boundSql::setAdditionalParameter);
return boundSql;
}
}
2.10、責任鏈模式
責任鏈(Chain of Responsibility)模式的定義:爲了避免請求發送者與多個請求處理者耦合在一起,將所有請求的處理者通過前一對象記住其下一個對象的引用而連成一條鏈;當有請求發生時,可將請求沿着這條鏈傳遞,直到有對象處理它爲止
在責任鏈模式中,客戶只需要將請求發送到責任鏈上即可,無須關心請求的處理細節和請求的傳遞過程,所以責任鏈將請求的發送者和請求的處理者解耦了。
在Mybatis源碼中我們經常會根據自己需要來加一些會話攔截操作,而這系列動作就是Mybatis源碼中的責任鏈完成的
/**
* 攔截器鏈
* @author Clinton Begin
*/
public class InterceptorChain {
//攔截器鏈
private final List<Interceptor> interceptors = new ArrayList<>();
//所有的攔截器
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
//添加攔截器
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
//獲取只讀攔截器
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
2.11、策略模式
策略(Strategy)模式的定義:該模式定義了一系列算法,並將每個算法封裝起來,使它們可以相互替換,且算法的變化不會影響使用算法的客戶。策略模式屬於對象行爲模式,它通過對算法進行封裝,把使用算法的責任和算法的實現分割開來,並委派給不同的對象對這些算法進行管理。比如Mybatis源碼中的AbstractSQL核心類中的內部類的SQLStatement內部枚舉類LimitingRowsStrategy,根據不同數據庫策略來執行不同的分頁SQL語法。
public abstract class AbstractSQL<T> {
//私有屬性 And
private static final String AND = ") \nAND (";
//私有屬性 or
private static final String OR = ") \nOR (";
//私有SQL聲明
private final SQLStatement sql = new SQLStatement();
public abstract T getSelf();
//更新函數
public T UPDATE(String table) {
sql().statementType = SQLStatement.StatementType.UPDATE;
sql().tables.add(table);
return getSelf();
}
//set字段
public T SET(String sets) {
sql().sets.add(sets);
return getSelf();
}
/**
* set字段
* @since 3.4.2
*/
public T SET(String... sets) {
sql().sets.addAll(Arrays.asList(sets));
return getSelf();
}
//insert函數
public T INSERT_INTO(String tableName) {
sql().statementType = SQLStatement.StatementType.INSERT;
sql().tables.add(tableName);
return getSelf();
}
//插入VALUES關鍵字
public T VALUES(String columns, String values) {
INTO_COLUMNS(columns);
INTO_VALUES(values);
return getSelf();
}
/**
* 插入field列
* @since 3.4.2
*/
public T INTO_COLUMNS(String... columns) {
sql().columns.addAll(Arrays.asList(columns));
return getSelf();
}
/**
* 插入字段
* @since 3.4.2
*/
public T INTO_VALUES(String... values) {
List<String> list = sql().valuesList.get(sql().valuesList.size() - 1);
Collections.addAll(list, values);
return getSelf();
}
//查詢SQL函數
public T SELECT(String columns) {
sql().statementType = SQLStatement.StatementType.SELECT;
sql().select.add(columns);
return getSelf();
}
/**
* //查詢SQL函數
* @since 3.4.2
*/
public T SELECT(String... columns) {
sql().statementType = SQLStatement.StatementType.SELECT;
sql().select.addAll(Arrays.asList(columns));
return getSelf();
}
//去重字段
public T SELECT_DISTINCT(String columns) {
sql().distinct = true;
SELECT(columns);
return getSelf();
}
/**
* 去重字段
* @since 3.4.2
*/
public T SELECT_DISTINCT(String... columns) {
sql().distinct = true;
SELECT(columns);
return getSelf();
}
//刪除表
public T DELETE_FROM(String table) {
sql().statementType = SQLStatement.StatementType.DELETE;
sql().tables.add(table);
return getSelf();
}
//from表追加
public T FROM(String table) {
sql().tables.add(table);
return getSelf();
}
/**
* from表追加
* @since 3.4.2
*/
public T FROM(String... tables) {
sql().tables.addAll(Arrays.asList(tables));
return getSelf();
}
//連表
public T JOIN(String join) {
sql().join.add(join);
return getSelf();
}
/**
* 連表
* @since 3.4.2
*/
public T JOIN(String... joins) {
sql().join.addAll(Arrays.asList(joins));
return getSelf();
}
//內連
public T INNER_JOIN(String join) {
sql().innerJoin.add(join);
return getSelf();
}
/**
* 內連表
* @since 3.4.2
*/
public T INNER_JOIN(String... joins) {
sql().innerJoin.addAll(Arrays.asList(joins));
return getSelf();
}
//左連表
public T LEFT_OUTER_JOIN(String join) {
sql().leftOuterJoin.add(join);
return getSelf();
}
/**
* 左連表
* @since 3.4.2
*/
public T LEFT_OUTER_JOIN(String... joins) {
sql().leftOuterJoin.addAll(Arrays.asList(joins));
return getSelf();
}
//右連表
public T RIGHT_OUTER_JOIN(String join) {
sql().rightOuterJoin.add(join);
return getSelf();
}
/**
* 右連表
* @since 3.4.2
*/
public T RIGHT_OUTER_JOIN(String... joins) {
sql().rightOuterJoin.addAll(Arrays.asList(joins));
return getSelf();
}
//外連表
public T OUTER_JOIN(String join) {
sql().outerJoin.add(join);
return getSelf();
}
/**
* 外連表
* @since 3.4.2
*/
public T OUTER_JOIN(String... joins) {
sql().outerJoin.addAll(Arrays.asList(joins));
return getSelf();
}
//追加where條件
public T WHERE(String conditions) {
sql().where.add(conditions);
sql().lastList = sql().where;
return getSelf();
}
/**
* 追加where條件
* @since 3.4.2
*/
public T WHERE(String... conditions) {
sql().where.addAll(Arrays.asList(conditions));
sql().lastList = sql().where;
return getSelf();
}
//追加or
public T OR() {
sql().lastList.add(OR);
return getSelf();
}
//追加and
public T AND() {
sql().lastList.add(AND);
return getSelf();
}
//給group by增加一列屬性
public T GROUP_BY(String columns) {
sql().groupBy.add(columns);
return getSelf();
}
/**
* 給group by增加多列排序
* @since 3.4.2
*/
public T GROUP_BY(String... columns) {
sql().groupBy.addAll(Arrays.asList(columns));
return getSelf();
}
//給having增加單獨條件屬性
public T HAVING(String conditions) {
sql().having.add(conditions);
sql().lastList = sql().having;
return getSelf();
}
/**
* 給having增加條件
* @since 3.4.2
*/
public T HAVING(String... conditions) {
sql().having.addAll(Arrays.asList(conditions));
sql().lastList = sql().having;
return getSelf();
}
//給order by增加一列屬性
public T ORDER_BY(String columns) {
sql().orderBy.add(columns);
return getSelf();
}
/**
* 給order by增加列字段屬性
* @since 3.4.2
*/
public T ORDER_BY(String... columns) {
sql().orderBy.addAll(Arrays.asList(columns));
return getSelf();
}
/**
* 給#{limit}賦值內容
* Set the limit variable string(e.g. {@code "#{limit}"}).
*
* @param variable a limit variable string
* @return a self instance
* @see #OFFSET(String)
* @since 3.5.2
*/
public T LIMIT(String variable) {
sql().limit = variable;
sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.OFFSET_LIMIT;
return getSelf();
}
/**
* 設置limit值
* Set the limit value.
*
* @param value an offset value
* @return a self instance
* @see #OFFSET(long)
* @since 3.5.2
*/
public T LIMIT(int value) {
return LIMIT(String.valueOf(value));
}
/**
* 給遊標#{offset}賦值內容
* Set the offset variable string(e.g. {@code "#{offset}"}).
*
* @param variable a offset variable string
* @return a self instance
* @see #LIMIT(String)
* @since 3.5.2
*/
public T OFFSET(String variable) {
sql().offset = variable;
sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.OFFSET_LIMIT;
return getSelf();
}
/**
* 設置遊標值
* Set the offset value.
*
* @param value an offset value
* @return a self instance
* @see #LIMIT(int)
* @since 3.5.2
*/
public T OFFSET(long value) {
return OFFSET(String.valueOf(value));
}
/**
* Set the fetch first rows variable string(e.g. {@code "#{fetchFirstRows}"}).
* 給fetchFirstRows變量賦值
* @param variable a fetch first rows variable string
* @return a self instance
* @see #OFFSET_ROWS(String)
* @since 3.5.2
*/
public T FETCH_FIRST_ROWS_ONLY(String variable) {
sql().limit = variable;
sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.ISO;
return getSelf();
}
/**
* 用於DB2數據庫的分頁查詢返回值
* Set the fetch first rows value.
*
* @param value a fetch first rows value
* @return a self instance
* @see #OFFSET_ROWS(long)
* @since 3.5.2
*/
public T FETCH_FIRST_ROWS_ONLY(int value) {
return FETCH_FIRST_ROWS_ONLY(String.valueOf(value));
}
/**
* 用於DB2數據庫的分頁下標查詢
* Set the offset rows variable string(e.g. {@code "#{offset}"}).
*
* @param variable a offset rows variable string
* @return a self instance
* @see #FETCH_FIRST_ROWS_ONLY(String)
* @since 3.5.2
*/
public T OFFSET_ROWS(String variable) {
sql().offset = variable;
sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.ISO;
return getSelf();
}
/**
* 設置行數查詢的下標
* Set the offset rows value.
*
* @param value an offset rows value
* @return a self instance
* @see #FETCH_FIRST_ROWS_ONLY(int)
* @since 3.5.2
*/
public T OFFSET_ROWS(long value) {
return OFFSET_ROWS(String.valueOf(value));
}
/*
* used to add a new inserted row while do multi-row insert.
* 用於多行插入的時候,多添加一行SQL
* @since 3.5.2
*/
public T ADD_ROW() {
sql().valuesList.add(new ArrayList<>());
return getSelf();
}
private SQLStatement sql() {
return sql;
}
//使用usingAppender函數返回原對象
public <A extends Appendable> A usingAppender(A a) {
sql().sql(a);
return a;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sql().sql(sb);
return sb.toString();
}
//靜態內部安全可擴展類,
private static class SafeAppendable {
//來源jdk,並非線程安全類,由繼承者或者實現者來保護接口的安全性
private final Appendable a;
private boolean empty = true;
//實現了SafeAppendable創建時候,封裝的內部爲Appendable接口對象
public SafeAppendable(Appendable a) {
super();
this.a = a;
}
//追加字符串
public SafeAppendable append(CharSequence s) {
try {
if (empty && s.length() > 0) {
empty = false;
}
a.append(s);
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
//判斷是否爲空對象
public boolean isEmpty() {
return empty;
}
}
//靜態內部私有類
private static class SQLStatement {
//定義內部枚舉類聲明關鍵字DML操作
public enum StatementType {
DELETE, INSERT, SELECT, UPDATE
}
//私有枚舉類限制函數策略
private enum LimitingRowsStrategy {
//不需要分頁或者適用於SQLServer數據庫
NOP {
@Override
protected void appendClause(SafeAppendable builder, String offset, String limit) {
// NOP
}
},
//處理DB2數據庫
ISO {
@Override
protected void appendClause(SafeAppendable builder, String offset, String limit) {
if (offset != null) {
builder.append(" OFFSET ").append(offset).append(" ROWS");
}
if (limit != null) {
builder.append(" FETCH FIRST ").append(limit).append(" ROWS ONLY");
}
}
},
//處理mysql
OFFSET_LIMIT {
@Override
protected void appendClause(SafeAppendable builder, String offset, String limit) {
if (limit != null) {
builder.append(" LIMIT ").append(limit);
}
if (offset != null) {
builder.append(" OFFSET ").append(offset);
}
}
};
protected abstract void appendClause(SafeAppendable builder, String offset, String limit);
}
//DML關鍵字類型聲明
StatementType statementType;
//set字段集合
List<String> sets = new ArrayList<>();
//select字段集合
List<String> select = new ArrayList<>();
//tables集合
List<String> tables = new ArrayList<>();
//join集合
List<String> join = new ArrayList<>();
//內連join集合
List<String> innerJoin = new ArrayList<>();
//外連join集合
List<String> outerJoin = new ArrayList<>();
//左連join集合
List<String> leftOuterJoin = new ArrayList<>();
//右連join集合
List<String> rightOuterJoin = new ArrayList<>();
//where集合
List<String> where = new ArrayList<>();
//having集合
List<String> having = new ArrayList<>();
//groupBy集合
List<String> groupBy = new ArrayList<>();
//orderBy集合
List<String> orderBy = new ArrayList<>();
//
List<String> lastList = new ArrayList<>();
//字段列集合
List<String> columns = new ArrayList<>();
//字段值集合
List<List<String>> valuesList = new ArrayList<>();
//區別boolean值
boolean distinct;
//下標
String offset;
//行數
String limit;
//限制行策略默認不限制
LimitingRowsStrategy limitingRowsStrategy = LimitingRowsStrategy.NOP;
//空構造函數初始化字段值
public SQLStatement() {
// Prevent Synthetic Access
valuesList.add(new ArrayList<>());
}
//sql約定條款處理builder內容
private void sqlClause(SafeAppendable builder, String keyword, List<String> parts, String open, String close,
String conjunction) {
//如果關鍵字不爲空
if (!parts.isEmpty()) {
//如果構建關鍵字不爲空則追加換行
if (!builder.isEmpty()) {
builder.append("\n");
}
//構建器追加DML的關鍵字
builder.append(keyword);
//追加空格區分sql語句
builder.append(" ");
//追加左邊符號
builder.append(open);
//封裝關鍵字局部內容
String last = "________";
for (int i = 0, n = parts.size(); i < n; i++) {
String part = parts.get(i);
if (i > 0 && !part.equals(AND) && !part.equals(OR) && !last.equals(AND) && !last.equals(OR)) {
builder.append(conjunction);
}
builder.append(part);
last = part;
}
//追加右邊括號
builder.append(close);
}
}
//selectSQL
private String selectSQL(SafeAppendable builder) {
//如果去重則select拼接去重邏輯
if (distinct) {
sqlClause(builder, "SELECT DISTINCT", select, "", "", ", ");
} else {
sqlClause(builder, "SELECT", select, "", "", ", ");
}
//from拼接
sqlClause(builder, "FROM", tables, "", "", ", ");
//連表拼接
joins(builder);
//where連接
sqlClause(builder, "WHERE", where, "(", ")", " AND ");
//group by連接
sqlClause(builder, "GROUP BY", groupBy, "", "", ", ");
//having 連接
sqlClause(builder, "HAVING", having, "(", ")", " AND ");
//order by連接
sqlClause(builder, "ORDER BY", orderBy, "", "", ", ");
//限制行數策略
limitingRowsStrategy.appendClause(builder, offset, limit);
//返回SQL拼接
return builder.toString();
}
//封裝連表查詢的內容
private void joins(SafeAppendable builder) {
sqlClause(builder, "JOIN", join, "", "", "\nJOIN ");
sqlClause(builder, "INNER JOIN", innerJoin, "", "", "\nINNER JOIN ");
sqlClause(builder, "OUTER JOIN", outerJoin, "", "", "\nOUTER JOIN ");
sqlClause(builder, "LEFT OUTER JOIN", leftOuterJoin, "", "", "\nLEFT OUTER JOIN ");
sqlClause(builder, "RIGHT OUTER JOIN", rightOuterJoin, "", "", "\nRIGHT OUTER JOIN ");
}
//插入SQL語句的封裝
private String insertSQL(SafeAppendable builder) {
sqlClause(builder, "INSERT INTO", tables, "", "", "");
sqlClause(builder, "", columns, "(", ")", ", ");
for (int i = 0; i < valuesList.size(); i++) {
sqlClause(builder, i > 0 ? "," : "VALUES", valuesList.get(i), "(", ")", ", ");
}
return builder.toString();
}
//刪除SQL語句的封裝
private String deleteSQL(SafeAppendable builder) {
sqlClause(builder, "DELETE FROM", tables, "", "", "");
sqlClause(builder, "WHERE", where, "(", ")", " AND ");
limitingRowsStrategy.appendClause(builder, null, limit);
return builder.toString();
}
//更新SQL語句的封裝
private String updateSQL(SafeAppendable builder) {
sqlClause(builder, "UPDATE", tables, "", "", "");
joins(builder);
sqlClause(builder, "SET", sets, "", "", ", ");
sqlClause(builder, "WHERE", where, "(", ")", " AND ");
limitingRowsStrategy.appendClause(builder, null, limit);
return builder.toString();
}
//將SQL對象轉化爲String的SQL語句
public String sql(Appendable a) {
SafeAppendable builder = new SafeAppendable(a);
if (statementType == null) {
return null;
}
String answer;
switch (statementType) {
case DELETE:
answer = deleteSQL(builder);
break;
case INSERT:
answer = insertSQL(builder);
break;
case SELECT:
answer = selectSQL(builder);
break;
case UPDATE:
answer = updateSQL(builder);
break;
default:
answer = null;
}
return answer;
}
}
}