1. 程式人生 > >Mybatis(攔截器實現)通用mapper及全ORM實現(四)

Mybatis(攔截器實現)通用mapper及全ORM實現(四)

到目前為止,我們通過mybatis的攔截器完成了一些基礎mapper的功能,接下來我們來看看如何能夠實現一個物件關係對映的全ORM操作。

實體與表、列的定義已經完成了,那剩下要做的就是: 1、定義如何通過物件方式編寫sql語句 2、把查詢物件轉譯成sql去查詢 3、把查詢結果轉化成實體物件

我們先看看mybatisext關於查詢物件的一些類關係:

SqlStatement介面

所有查詢物件必須實現一下幾個方法: 1、getSqlWrapper()      返回一個sql資訊物件包括sql語句和裡面的相關引數map資訊 2、getResultMap()     返回這個查詢sql的返回結果中每個欄位對應的實體類及其中的屬性 3、getFormatSqlWrapper()     返回一個標準的sql資訊物件,與getSqlWrapper不同是sql語句裡面不會有需要替換的變數,以mysql為例引數會替換成?,幷包括根據引數順序的引數值陣列 4、getFormatSqlReplacement()      sql中引數替代自負,預設?

package cw.frame.mybatisext.base;

import java.util.HashMap;
import java.util.Map;

public class SqlWrapper {
    private String sql;
    private Map<String, Object> parameters = new HashMap<String, Object>();

    public SqlWrapper(){}

    public SqlWrapper(String sql){
        this.sql = sql;
    }

    public SqlWrapper(String sql, Map<String, Object> parameters){
        this.sql = sql;
        this.parameters = parameters;
    }

    public Map<String, Object> getParameters() {
        return parameters;
    }

    public String getSql() {
        return sql;
    }

    public void setParameters(Map<String, Object> parameters) {
        this.parameters = parameters;
    }

    public void setSql(String sql) {
        this.sql = sql;
    }
}
package cw.frame.mybatisext.base;

import cw.frame.mybatisext.base.entity.TableInfo;

import java.util.*;

public class ResultMap {
    private TableInfo tableInfo;
    private Map<String, String> resultMap;
    private Map<String, ResultMap> subResultMap;

    public ResultMap(TableInfo tableInfo){
        this.tableInfo = tableInfo;
        this.resultMap = new HashMap<String, String>();
        this.subResultMap = null;
    }

    public void addResultMap(String resultName, String propertyName){
        this.resultMap.put(resultName, propertyName);
    }

    /**
     * 新增子結果對映
     * @param subResultMap 子結果map物件
     * @param resultPropertyName 結果儲存欄位
     */
    public void addSubResultMap(ResultMap subResultMap, String resultPropertyName){
        if (this.subResultMap == null){
            this.subResultMap = new HashMap<String, ResultMap>();
        }

        this.subResultMap.put(resultPropertyName, subResultMap);
    }

    public Map<String, String> getResultMap(){
        return this.resultMap;
    }

    public boolean hasSubResultMap(){
        if (this.subResultMap != null && this.subResultMap.size() > 0){
            return true;
        } else {
            return false;
        }
    }

    public Map<String, ResultMap> getSubResultMap() {
        return subResultMap;
    }

    public TableInfo getTableInfo(){
        return this.tableInfo;
    }
}

BaseSqlStatement抽象類

SqlStatement的實現類,定義了基礎mapper中需要攔截的sql語句常量、引數名稱常量,和一個抽象方法prepare()

getFormatSqlWrapper() -> getSqlWrapper() -> prepare()

子類需要完善的prepare()方法要做的事情也就是建立一個SqlWrapper物件

package cw.frame.mybatisext.base;

import cw.frame.mybatisext.SqlStatement;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public abstract class BaseSqlStatement implements SqlStatement {

    public static final String OPERATE_TYPE_NAME_PRE = "mybatisext.";
    public static final String OPERATE_TYPE_ADD_ONE = OPERATE_TYPE_NAME_PRE + "addOne";
    public static final String OPERATE_TYPE_ADD_MANY = OPERATE_TYPE_NAME_PRE + "addMany";

    public static final String OPERATE_TYPE_REMOVE_BY_ID = OPERATE_TYPE_NAME_PRE + "removeById";
    public static final String OPERATE_TYPE_REMOVE_BY_IDS = OPERATE_TYPE_NAME_PRE + "removeByIds";
    public static final String OPERATE_TYPE_REMOVE = OPERATE_TYPE_NAME_PRE + "remove";

    public static final String OPERATE_TYPE_UPDAT_BY_ID = OPERATE_TYPE_NAME_PRE + "updateById";
    public static final String OPERATE_TYPE_UPDATE = OPERATE_TYPE_NAME_PRE + "update";

    public static final String OPERATE_TYPE_GET_BY_ID = OPERATE_TYPE_NAME_PRE + "findById";
    public static final String OPERATE_TYPE_GET_BY_IDS = OPERATE_TYPE_NAME_PRE + "findByIds";
    public static final String OPERATE_TYPE_GET_ONE = OPERATE_TYPE_NAME_PRE + "getOne";
    public static final String OPERATE_TYPE_GET_MANY = OPERATE_TYPE_NAME_PRE + "getMany";
    public static final String OPERATE_TYPE_GET_PAGE = OPERATE_TYPE_NAME_PRE + "getPage";

    public static final String OPERATE_PARAM_ENTITY = "entity";
    public static final String OPERATE_PARAM_ENTITIES = "entities";
    public static final String OPERATE_PARAM_PRIMARY_KEY = "id";
    public static final String OPERATE_PARAM_PRIMARY_KEYS = "ids";
    public static final String OPERATE_PARAM_SQLSTATEMANT = "sqlStatement";
    public static final String OPERATE_PARAM_PAGER = "pager";

    private SqlWrapper sqlWrapper;
    private ResultMap resultMap;
    private FormatSqlWrapper formatSqlWrapper = null;

    @Override
    public SqlWrapper getSqlWrapper(){
        if (this.sqlWrapper == null){
            this.prepare();
        }

        return this.sqlWrapper;
    }

    @Override
    public ResultMap getResultMap(){
        return this.resultMap;
    }

    @Override
    public FormatSqlWrapper getFormatSqlWrapper(){
        if (this.formatSqlWrapper == null){
            this.formatSql();
        }

        return this.formatSqlWrapper;
    }

    public void setResultMap(ResultMap resultMap){
        this.resultMap = resultMap;
    }

    public String getParameterName(String tableName, String propertyName){
        StringBuilder sb = new StringBuilder();

        sb.append("#{");
        if(tableName != null && !tableName.isEmpty()){
            sb.append(tableName).append(".");
        }
        sb.append(propertyName);
        sb.append("}");

        return sb.toString();
    }

    protected void setSqlWrapper(String sql) {
        this.sqlWrapper = new SqlWrapper(sql);
    }

    protected void setSqlWrapper(String sql, Map<String, Object> parameters) {
        this.sqlWrapper = new SqlWrapper(sql, parameters);
    }

    protected void setSqlWrapper(SqlWrapper sqlWrapper) {
        this.sqlWrapper = sqlWrapper;
    }

    protected void unsetSqlWrapper(){
        this.sqlWrapper = null;
    }

    protected SqlWrapper combineSqlWrappers(List<SqlStatement> sqlStatements, String joinStr){
        if (sqlStatements.size() == 0){
            return new SqlWrapper();
        }

        StringBuilder sb = new StringBuilder();
        Map<String, Object> parameters = new HashMap<String, Object>();

        for(SqlStatement sqlStatement : sqlStatements){
            if (sb.length() > 0){
                sb.append(joinStr);
            }

            SqlWrapper sqlWrapper = sqlStatement.getSqlWrapper();
            sb.append(sqlWrapper.getSql());
            parameters.putAll(sqlWrapper.getParameters());
        }

        return new SqlWrapper(sb.toString(), parameters);
    }

    protected abstract void prepare();

    private void formatSql(){
        StringBuffer sbSql = new StringBuffer();
        List<Object> values = new ArrayList<Object>();
        SqlWrapper sqlWrapper = this.getSqlWrapper();

        Pattern pattern = Pattern.compile("#\\{\\S+?\\}");
        Matcher matcher = pattern.matcher(sqlWrapper.getSql());

        while (matcher.find()){
            matcher.appendReplacement(sbSql, this.getFormatSqlReplacement());
            values.add(sqlWrapper.getParameters().get(matcher.group()));
        }

        matcher.appendTail(sbSql);

        this.formatSqlWrapper = new FormatSqlWrapper(sbSql.toString(), values);
    }
}

接下來就是定義具體的查詢物件了,mybatisext把一個查詢拆分成幾個部分並進行不同的類封裝: 1、select欄位部分 2、表連線部分 3、單個條件欄位部分 4、單個分組group欄位部分 5、單個having欄位你部分 6、單個order欄位排序部分 7、多個條件、排序、分組、連線的部分

我們來逐一看下程式碼

package cw.frame.mybatisext.provider.mysql.statement;

import cw.frame.mybatisext.base.BaseSqlStatement;
import cw.frame.mybatisext.base.ResultMap;
import cw.frame.mybatisext.base.entity.BaseExtEntity;
import cw.frame.mybatisext.base.entity.ColumnInfo;
import cw.frame.mybatisext.base.entity.TableInfo;
import cw.frame.mybatisext.provider.mysql.ExpressionExplain;
import cw.frame.mybatisext.provider.mysql.ExpressionResult;

import java.util.*;

public class SelectStatement extends BaseSqlStatement {
    private Class<? extends BaseExtEntity> entityClassType;
    private Set<String> selectPropertyNames = new TreeSet<String >();
    private Map<String, String> selectPropertyNameMap = new HashMap<String, String>();
    private TableInfo tableInfo;
    private String aliasTableName;
    private Map<String, String> propertyResultMap = new HashMap<String, String>();
    private ResultMap resultMap;

    public SelectStatement(Class<? extends BaseExtEntity> entityClassType, String aliasTableName){
        this.entityClassType = entityClassType;
        this.aliasTableName = aliasTableName;
        this.tableInfo = TableInfo.getTableInfo(this.entityClassType);
        this.resultMap = new ResultMap(this.tableInfo);

        this.setResultMap(this.resultMap);
    }

    /**
     * 設定查詢列
     * @param propertyNames propertyName, max(#{propertyName})等
     */
    public void select(String... propertyNames){
        for (String propertyName : propertyNames){
            if (propertyName.equals("*")){
                for (ColumnInfo columnInfo : this.tableInfo.getColumnns()){
                    this.selectPropertyNames.add(columnInfo.getPropertyName());
                }
                break;

            } else {
                this.selectPropertyNames.add(propertyName);
            }
        }

        this.unsetSqlWrapper();
    }

    /**
     * 設定查詢列
     * @param propertyName propertyName, max(#{propertyName})等
     * @param resultPropertyName 結果返回儲存屬性欄位
     */
    public void selectAs(String propertyName, String resultPropertyName){
        this.selectPropertyNameMap.put(propertyName, resultPropertyName);
    }

    /**
     * 設定查詢列
     * @param propertyNameMap k:propertyName propertyName, max(#{propertyName})等,v:結果返回儲存屬性欄位
     */
    public void selectAs(Map<String, String> propertyNameMap){
        this.selectPropertyNameMap.putAll(propertyNameMap);
    }

    public TableInfo getTableInfo(){
        return this.tableInfo;
    }

    public boolean hasSelectFields(){
        return this.selectPropertyNames.size() > 0;
    }

    public String getAliasTableName() {
        return aliasTableName;
    }

    public Map<String, String> getPropertyResultMap() {
        return propertyResultMap;
    }

    @Override
    protected void prepare(){
        StringBuilder sb = new StringBuilder();
        for (String propertyName : this.selectPropertyNames){
            this.buildSelectItemPartSql(sb, propertyName, null);
        }
        for (String propertyName : this.selectPropertyNameMap.keySet()){
            this.buildSelectItemPartSql(sb, propertyName, this.selectPropertyNameMap.get(propertyName));
        }

        // 查詢所有欄位
        if (sb.length() == 0){
            for (String propertyName : this.tableInfo.getPropertyNames()){
                this.buildSelectItemPartSql(sb, propertyName, null);
            }
        }

        String sql = sb.toString();

        this.setSqlWrapper(sql);
    }

    private void buildSelectItemPartSql(StringBuilder sb, String selectFieldString, String resultProperty){
        if (sb.length() != 0){
            sb.append(",");
        }

        ExpressionResult expressionResult = ExpressionExplain.explain(selectFieldString, this);
        ColumnInfo columnInfo = expressionResult.getColumn();

        if (expressionResult.isExpression()){
            sb.append(expressionResult.getResult());
        } else {
            sb.append(this.aliasTableName).append(".`");
            sb.append(columnInfo.getColumnName()).append("`");
        }

        String asName;
        String resultPropertyName;
        if (resultProperty == null || resultProperty.isEmpty()){
            asName = this.aliasTableName + "_" + columnInfo.getColumnName();
            resultPropertyName = columnInfo.getPropertyName();
        } else {
            ColumnInfo resultColumn = this.tableInfo.getColumnByPropertyName(resultProperty);
            resultPropertyName = resultProperty;
            if (resultColumn.isDbColumn()){
                asName = this.aliasTableName + "_" + resultColumn.getPropertyName();
            } else {
                asName = this.aliasTableName + "_" + resultPropertyName;
            }
        }

        sb.append(" ").append(asName);

        this.propertyResultMap.put(resultPropertyName, asName);
        this.resultMap.addResultMap(asName, resultPropertyName);
    }
}

通過SelectStatement可以設定需要查詢的列,也可以指定查詢列結果別名(column1 as xxx),並對自定義了一個別名:表別名_列名(或指定的別名),最終把這些別名和實體對應關係記錄在一個ResultMap類裡

package cw.frame.mybatisext.provider.mysql.statement;

import cw.frame.mybatisext.base.BaseSqlStatement;
import cw.frame.mybatisext.base.SqlWrapper;
import cw.frame.mybatisext.enumeration.JoinType;
import cw.frame.mybatisext.provider.mysql.MySqlStatement;

import java.util.HashMap;
import java.util.Map;

public class SingleJoinStatement extends BaseSqlStatement {

    private SelectStatement fromSelectStatement;
    private Object toStatement;
    private JoinType joinType;
    private Map<String, String> joinRelationshipMap = new HashMap<String, String>();

    public SingleJoinStatement(SelectStatement fromSelectStatement, SelectStatement toSelectStatement, JoinType joinType){
        this.fromSelectStatement = fromSelectStatement;
        this.toStatement = toSelectStatement;
        this.joinType = joinType;
    }

    public SingleJoinStatement(SelectStatement fromSelectStatement, MySqlStatement toMySqlStatement, JoinType joinType){
        this.fromSelectStatement = fromSelectStatement;
        this.toStatement = toMySqlStatement;
        this.joinType = joinType;
    }

    public void setRelationship(String fromPropertyName, String toPropertyName){
        this.joinRelationshipMap.put(fromPropertyName, toPropertyName);
    }

    @Override
    protected void prepare(){
        StringBuilder sb = new StringBuilder();
        Map<String, Object> parameters = new HashMap<String, Object>();

        SelectStatement toSelectStatement = null;
        MySqlStatement toMySqlStatement = null;

        for (String fromPropertyName : this.joinRelationshipMap.keySet()){
            String toPropertyName = this.joinRelationshipMap.get(fromPropertyName);
            if (sb.length() == 0){
                sb.append(this.getJoinTypeSqlString(this.joinType)).append(" ");
                if (this.toStatement instanceof SelectStatement){
                    toSelectStatement = (SelectStatement)this.toStatement;

                    String toTableAsName = toSelectStatement.getAliasTableName();
                    sb.append("`").append(toSelectStatement.getTableInfo().getTableName()).append("`");
                    sb.append(" ").append(toTableAsName);
                } else {
                    // 連線子查詢
                    toMySqlStatement = (MySqlStatement)this.toStatement;
                    String toTableAsName = toMySqlStatement.getSelectStatement().getAliasTableName();

                    SqlWrapper wrapper = toMySqlStatement.getSqlWrapper();
                    parameters.putAll(wrapper.getParameters());

                    sb.append("(").append(wrapper.getSql()).append(")");
                    sb.append(" ").append(toTableAsName);
                }
                sb.append(" on ");
            } else {
                sb.append(" and ");
            }

            sb.append(this.fromSelectStatement.getAliasTableName()).append(".`");
            sb.append(this.fromSelectStatement.getTableInfo().getColumnByPropertyName(fromPropertyName).getColumnName());
            sb.append("`=");

            if (toSelectStatement != null){
                sb.append(toSelectStatement.getAliasTableName()).append(".").append("`");
                sb.append(toSelectStatement.getTableInfo().getColumnByPropertyName(toPropertyName).getColumnName());
                sb.append("`");
            } else {
                sb.append(toMySqlStatement.getSelectStatement().getAliasTableName());
                sb.append(".`");
                sb.append(toMySqlStatement.getSelectStatement().getPropertyResultMap().get(toPropertyName));
                sb.append("`");
            }
        }


        this.setSqlWrapper(new SqlWrapper(sb.toString(), parameters));
    }

    private String getJoinTypeSqlString(JoinType joinType){
        String str;
        switch (joinType){
            case LEFT_JOIN:
                str = "left join";
                break;
            case INNER_JOIN:
                str = "inner join";
                break;
            case RIGHT_JOIN:
                str = "right join";
                break;
            default:
                str = "inner join";
                break;
        }

        return str;
    }
}

SingleJoinStatement支援了兩個表的連線和表與一個查詢物件(子查詢)的連線

package cw.frame.mybatisext.provider.mysql.statement;

import cw.frame.mybatisext.base.BaseSqlStatement;
import cw.frame.mybatisext.SqlStatement;
import cw.frame.mybatisext.base.SqlWrapper;
import cw.frame.mybatisext.base.entity.BaseExtEnum;
import cw.frame.mybatisext.enumeration.ConditionType;

import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.Map;

public class SingleConditionStatement extends BaseSqlStatement {
    private ConditionType conditionType;
    private String columnName;
    private Object propertyValue;
    private String tableName = "";

    public SingleConditionStatement(String tableName, String columnName, ConditionType conditionType, Object propertyValue){
        this.columnName = columnName;
        this.conditionType = conditionType;
        this.propertyValue = propertyValue;
        this.tableName = tableName;
    }

    public String getColumnName() {
        return columnName;
    }

    public ConditionType getConditionType() {
        return conditionType;
    }

    public Object getPropertyValue() {
        return propertyValue;
    }

    @Override
    protected void prepare(){
        Map<String, Object> parameters = new HashMap<String, Object>();
        StringBuilder sb = new StringBuilder();
        String conditionColumnName = "`" + this.columnName + "`";

        if (!this.tableName.isEmpty()){
            conditionColumnName = this.tableName + "." + conditionColumnName;
        }

        switch (this.conditionType){
            case IN:
                sb.append(" find_in_set (").append(conditionColumnName).append(",");
                break;
            case NOT_IN:
                sb.append(" not find_in_set (").append(conditionColumnName).append(",");
                break;
            case EQUAL:
                sb.append(conditionColumnName);
                sb.append("=");
                break;
            case NOT_EQUAL:
                sb.append(conditionColumnName);
                sb.append("<>");
                break;
            case GREATER:
                sb.append(conditionColumnName);
                sb.append(">");
                break;
            case GREATER_AND_EQUAL:
                sb.append(conditionColumnName);
                sb.append(">=");
                break;
            case LESS:
                sb.append(conditionColumnName);
                sb.append("<");
                break;
            case LESS_AND_EQUAL:
                sb.append(conditionColumnName);
                sb.append("<=");
                break;
            case LIKE:
                sb.append(conditionColumnName);
                sb.append(" like ");
                break;
            case NOT_LIKE:
                sb.append(conditionColumnName);
                sb.append(" not like ");
                break;
        }

        if (this.propertyValue instanceof SqlStatement){
            SqlStatement sqlStatement = (SqlStatement) this.propertyValue;
            SqlWrapper sqlWrapper = sqlStatement.getSqlWrapper();
            String sql = sqlWrapper.getSql();
            sb.append(sql);
            parameters.putAll(sqlWrapper.getParameters());
        } else {
            String parameterName = this.getParameterName(this.tableName, this.columnName);
            sb.append(parameterName);
            if (this.conditionType == ConditionType.IN || this.conditionType == ConditionType.NOT_IN){
                if (this.propertyValue.getClass().isArray()){
                    StringBuilder sbValue = new StringBuilder();
                    for (int i=0; i<Array.getLength(this.propertyValue); i++){
                        if (sbValue.length() > 0){
                            sbValue.append(",");
                        }
                        sbValue.append(Array.get(this.propertyValue, i));
                    }
                    parameters.put(parameterName, this.getPropertyValue(this.propertyValue).toString());
                } else {
                    parameters.put(parameterName, this.getPropertyValue(this.propertyValue));
                }
            } else {
                parameters.put(parameterName, this.getPropertyValue(this.propertyValue));
            }
        }

        if (this.conditionType == ConditionType.IN || this.conditionType == ConditionType.NOT_IN){
            sb.append(")");
        }

        this.setSqlWrapper(new SqlWrapper(sb.toString(), parameters));
    }

    private Object getPropertyValue(Object propertyValue){
        if (propertyValue instanceof BaseExtEnum){
            return ((BaseExtEnum) propertyValue).getValue();
        } else {
            return propertyValue;
        }
    }
}
package cw.frame.mybatisext.provider.mysql.statement;

import cw.frame.mybatisext.base.BaseSqlStatement;
import cw.frame.mybatisext.base.SqlWrapper;

public class SingleGroupStatement extends BaseSqlStatement {
    private String columnName;
    private String tableName = "";

    private SqlWrapper sqlWrapper;

    public SingleGroupStatement(String columnName){
        this.columnName = columnName;
    }

    public SingleGroupStatement(String columnName, String tableName){
        this.columnName = columnName;
        this.tableName = tableName;
    }

    @Override
    protected void prepare(){
        StringBuilder sb = new StringBuilder();
        sb.append(this.tableName).append(".`");
        sb.append(this.columnName).append("`");

        this.setSqlWrapper(new SqlWrapper(sb.toString()));
    }
}
package cw.frame.mybatisext.provider.mysql.statement;

import cw.frame.mybatisext.base.BaseSqlStatement;
import cw.frame.mybatisext.base.SqlWrapper;
import cw.frame.mybatisext.enumeration.OrderType;

public class SingleOrderStatement extends BaseSqlStatement {
    private String columnName;
    private String tableName = "";
    private OrderType orderType = OrderType.DESCENDING;

    private SqlWrapper sqlWrapper;

    public SingleOrderStatement(String columnName){
        this.columnName = columnName;
    }

    public SingleOrderStatement(String columnName, String tableName){
        this.columnName = columnName;
        this.tableName = tableName;
    }

    public SingleOrderStatement(String columnName, OrderType orderType){
        this.columnName = columnName;
        this.orderType = orderType;
    }

    public SingleOrderStatement(String columnName, OrderType orderType, String tableName){
        this.columnName = columnName;
        this.orderType = orderType;
        this.tableName = tableName;
    }

    @Override
    protected void prepare(){
        StringBuilder sb = new StringBuilder();
        sb.append(this.tableName).append(".`");
        sb.append(this.columnName).append("`");
        if (this.orderType == OrderType.DESCENDING){
            sb.append(" desc");
        }

        this.setSqlWrapper(new SqlWrapper(sb.toString()));
    }
}
package cw.frame.mybatisext.provider.mysql.statement;

import cw.frame.mybatisext.base.BaseSqlStatement;
import cw.frame.mybatisext.SqlStatement;
import cw.frame.mybatisext.base.SqlWrapper;

import java.util.ArrayList;
import java.util.List;

public class MutiSqlStatement extends BaseSqlStatement {

    private List<SqlStatement> statements;
    private String joinStr;

    public MutiSqlStatement(String joinStr){
        this.statements = new ArrayList<SqlStatement>();
        this.joinStr = joinStr;
    }

    public MutiSqlStatement(List<SqlStatement> statements, String joinStr){
        this.statements = statements;
        this.joinStr = joinStr;
    }

    public MutiSqlStatement addStatement(SqlStatement statement){
        this.statements.add(statement);
        this.unsetSqlWrapper();

        return this;
    }

    @Override
    protected void prepare(){
        SqlWrapper wrapper = this.combineSqlWrappers(this.statements, this.joinStr);

        this.setSqlWrapper(wrapper);
    }
}

MutiSqlStatement其實就是多個查詢物件的集合,並把每個查詢物件通過指定的連線字串進行連線,引數進行合併

MysqlStatement

最後,為了方便使用者編寫sql,不用過多關心以上那些類,建立了一個MysqlStatement類,同樣繼承了BaseSqlStatement

package cw.frame.mybatisext.provider.mysql;

import cw.frame.mybatisext.SqlStatement;
import cw.frame.mybatisext.base.BaseSqlStatement;
import cw.frame.mybatisext.base.ResultMap;
import cw.frame.mybatisext.base.SqlWrapper;
import cw.frame.mybatisext.base.TableIndentityProvider;
import cw.frame.mybatisext.base.entity.RelationshipInfo;
import cw.frame.mybatisext.base.entity.TableInfo;
import cw.frame.mybatisext.base.entity.BaseExtEntity;
import cw.frame.mybatisext.enumeration.ConditionType;
import cw.frame.mybatisext.enumeration.JoinType;
import cw.frame.mybatisext.enumeration.OrderType;
import cw.frame.mybatisext.provider.mysql.statement.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MySqlStatement extends BaseSqlStatement {
    private Class<? extends BaseExtEntity> entityClass;
    private TableInfo tableInfo;

    private TableIndentityProvider tableIndentityProvider;
    private OperateType operateType;

    private SelectStatement selectStatement = null;
    private List<MySqlStatement> selectSubMySqlStatements = null;
    private MutiSqlStatement joinStatements = null;
    private MutiSqlStatement conditionStatemennts = null;
    private MutiSqlStatement orderStatemennts = null;
    private MutiSqlStatement groupStatemennts = null;
    private MutiSqlStatement havingStatemennts = null;
    private String limitString = "";

    private List<UpdateSetItem> updateSetItems = new ArrayList<UpdateSetItem>();

    public static MySqlStatement createSelectStatement(Class<? extends BaseExtEntity> entityClass){
        return new MySqlStatement(entityClass, OperateType.SELECT);
    }

    public static MySqlStatement createUpdateStatement(Class<? extends BaseExtEntity> entityClass){
        return new MySqlStatement(entityClass, OperateType.UPDATE);
    }

    public static MySqlStatement createDeleteStatement(Class<? extends BaseExtEntity> entityClass){
        return new MySqlStatement(entityClass, OperateType.DELETE);
    }

    public MySqlStatement(Class<? extends BaseExtEntity> entityClass, TableIndentityProvider tableIndentityProvider){
        this.init(entityClass, tableIndentityProvider);
        this.operateType = OperateType.SELECT;
    }

    private MySqlStatement(Class<? extends BaseExtEntity> entityClass, OperateType operateType){
        this.init(entityClass, new TableIndentityProvider());
        this.operateType = operateType;
    }

    public SelectStatement getSelectStatement() {
        return this.selectStatement;
    }

    public MySqlStatement select(String... propertyNames){
        this.selectStatement.select(propertyNames);

        return this;
    }

    public MySqlStatement selectAs(String propertyName, String resultPropertyName){
        this.selectStatement.selectAs(propertyName, resultPropertyName);
        return this;
    }

    public MySqlStatement selectAs(Map<String, String> propertyNameMap){
        this.selectStatement.selectAs(propertyNameMap);
        return this;
    }

    public MySqlStatement set(String propertyName, Object propertyValue){
        this.updateSetItems.add(new UpdateSetItem(propertyName, propertyValue));
        return this;
    }

    /**
     * set a.name = b.name
     * @param propertyName
     * @param subQuery
     * @param subPropertyName
     * @return
     */
    public MySqlStatement set(String propertyName, MySqlStatement subQuery, String subPropertyName){
        this.updateSetItems.add(new UpdateSetItem(propertyName, subQuery.getSelectStatement().getAliasTableName(), subPropertyName));
        return this;
    }

    public MySqlStatement set(Map<String, Object> propertyMap){
        for (String propertyName : propertyMap.keySet()){
            this.updateSetItems.add(new UpdateSetItem(propertyName, propertyMap.get(propertyName)));
        }
        return this;
    }

    public MySqlStatement join(Class<? extends BaseExtEntity> classType, JoinType joinType, String fromPropertyName, String toPropertyName){
        SingleJoinStatement singleJoinStatement = this.getSingleJoinStatement(classType, joinType);
        singleJoinStatement.setRelationship(fromPropertyName, toPropertyName);

        return this;
    }

    public MySqlStatement join(Class<? extends BaseExtEntity> classType, JoinType joinType, Map<String, String> relationshipMap){
        SingleJoinStatement singleJoinStatement = this.getSingleJoinStatement(classType, joinType);
        for (String fromPropertyName : relationshipMap.keySet()){
            singleJoinStatement.setRelationship(fromPropertyName, relationshipMap.get(fromPropertyName));
        }

        return this;
    }

    public MySqlStatement join(MySqlStatement subQuery, JoinType joinType, String fromPropertyName, String toPropertyName, boolean joinByQuery, String relationshipPropertyName){
        if (joinByQuery){
            SingleJoinStatement statement = this.getSingleJoinStatement(subQuery, joinType);
            statement.setRelationship(fromPropertyName, toPropertyName);
        } else {
            SingleJoinStatement singleJoinStatement = this.getSingleJoinStatement(subQuery.getSelectStatement(), joinType);
            singleJoinStatement.setRelationship(fromPropertyName, toPropertyName);
        }

        if (this.selectSubMySqlStatements == null){
            this.selectSubMySqlStatements = new ArrayList<MySqlStatement>();
        }
        this.selectSubMySqlStatements.add(subQuery);
        this.getResultMap().addSubResultMap(subQuery.getResultMap(), relationshipPropertyName);

        return this;
    }

    public MySqlStatement join(MySqlStatement subQuery, JoinType joinType, Map<String, String> relationshipMap, boolean joinByQuery){
        SingleJoinStatement singleJoinStatement;

        if (joinByQuery){
            singleJoinStatement = this.getSingleJoinStatement(subQuery, joinType);

        } else {
            singleJoinStatement = this.getSingleJoinStatement(subQuery.getSelectStatement(), joinType);

        }

        for (String fromPropertyName : relationshipMap.keySet()){
            singleJoinStatement.setRelationship(fromPropertyName, relationshipMap.get(fromPropertyName));
        }

        return this;
    }

    public MySqlStatement where(String propertyName, ConditionType conditionType, Object value){
        this.setCondition(this.selectStatement, propertyName, conditionType, value);

        return this;
    }

    public MySqlStatement where(MySqlStatement subQuery, String propertyName, ConditionType conditionType, Object value){
        this.setCondition(subQuery.getSelectStatement(), propertyName, conditionType, value);

        return this;
    }

    public MySqlStatement orderBy(String propertyName, OrderType orderType){
        this.setOrderBy(this.selectStatement, propertyName, orderType);
        return this;
    }

    public MySqlStatement orderBy(MySqlStatement subQuery, String propertyName, OrderType orderType){
        this.setOrderBy(subQuery.getSelectStatement(), propertyName, orderType);
        return this;
    }

    public MySqlStatement groupBy(String propertyName){
        this.setGroupBy(this.selectStatement, propertyName);
        return this;
    }

    public MySqlStatement groupBy(MySqlStatement subQuery, String propertyName){
        this.setGroupBy(subQuery.getSelectStatement(), propertyName);
        return this;
    }

    public MySqlStatement having(String expression, ConditionType conditionType, Object value){
        this.setHaving(this.selectStatement, expression, conditionType, value);
        return this;
    }

    public MySqlStatement having(MySqlStatement subQuery, String expression, ConditionType conditionType, Object value){
        this.setHaving(subQuery.getSelectStatement(), expression, conditionType, value);
        return this;
    }

    public MySqlStatement limit(int limit){
        return this.limit(limit, 0);
    }

    public MySqlStatement limit(int limit, int skip){
        StringBuilder sb = new StringBuilder();
        sb.append("limit ");
        if (skip > 0){
            sb.append(skip).append(",").append(limit);
        } else {
            sb.append(limit);
        }
        this.limitString = sb.toString();

        return this;
    }

    /**
     * 根據實體型別建立子查詢物件,不會有查詢欄位返回
     * @param entityClass
     * @return MySqlStatement
     */
    public MySqlStatement createSubQuery(Class<? extends BaseExtEntity> entityClass){
        MySqlStatement operator = new MySqlStatement(entityClass, this.tableIndentityProvider);

        return operator;
    }

    /**
     * 建立子查詢物件,結果儲存於@OneOne or @OneMany定義的關係欄位, 採用inner join連線
     * @param relationshipPropertyName 關係欄位名
     * @return MySqlStatement
     */
    public MySqlStatement createSubQuery(String relationshipPropertyName){
        return this.createSubQuery(relationshipPropertyName, JoinType.INNER_JOIN);
    }

    /**
     * 建立子查詢物件,結果儲存於@OneOne or @OneMany定義的關係欄位
     * @param relationshipPropertyName 關係欄位名
     * @param joinType inner/left/right join
     * @return MySqlStatement
     */
    public MySqlStatement createSubQuery(String relationshipPropertyName, JoinType joinType){
        RelationshipInfo relationshipInfo = this.tableInfo.getOneManyMap().getOrDefault(relationshipPropertyName, null);
        if (relationshipInfo == null){
            relationshipInfo = this.tableInfo.getOneOneMap().getOrDefault(relationshipPropertyName, null);
        }

        if (relationshipInfo == null){
            throw new IllegalArgumentException("relationshipPropertyName error: " + relationshipPropertyName);
        }

        MySqlStatement operator = new MySqlStatement(relationshipInfo.getSubTable().getTableEntityClass(), this.tableIndentityProvider);

        this.join(operator, joinType, relationshipInfo.getPropertyKey(), relationshipInfo.getForeignKey(), false, relationshipPropertyName);

        return operator;
    }

    @Override
    protected void prepare(){
        StringBuilder sb = new StringBuilder();
        Map<String, Object> parameters = new HashMap<String, Object>();

        switch (this.operateType){
            case SELECT:
                sb.append("select ");

                SqlWrapper selectSqlWrapper = this.selectStatement.getSqlWrapper();
                sb.append(selectSqlWrapper.getSql());
                parameters.putAll(selectSqlWrapper.getParameters());

                this.setSubSelectFieldsSql(this.selectSubMySqlStatements, sb, parameters);

                sb.append(" from ").append(this.selectStatement.getTableInfo().getTableName()).append(" ");
                sb.append(this.selectStatement.getAliasTableName());

                this.combineByWrapper(this.joinStatements, sb, parameters, " ");

                this.combineByWrapper(this.conditionStatemennts, sb, parameters, " where ");

                this.combineByWrapper(this.orderStatemennts, sb, parameters, " order by ");

                this.combineByWrapper(this.groupStatemennts, sb, parameters, " group by ");

                this.combineByWrapper(this.havingStatemennts, sb, parameters, " having ");

                if (this.limitString != null && !this.limitString.isEmpty()){
                    sb.append(" ").append(this.limitString);
                }

                break;

            case UPDATE:
                sb.append("update ").append(this.selectStatement.getTableInfo().getTableName()).append(" ");
                sb.append(this.selectStatement.getAliasTableName());

                this.combineByWrapper(this.joinStatements, sb, parameters, " ");

                sb.append(" set ");
                int i=0;
                for (UpdateSetItem updateSetItem : this.updateSetItems){
                    if (i > 0){
                        sb.append(",");
                    }
                    String parameterName = this.getParameterName(this.selectStatement.getAliasTableName(), updateSetItem.getPropertyName());
                    sb.append(this.selectStatement.getAliasTableName()).append(".`");
                    sb.append(updateSetItem.getPropertyName());
                    sb.append("`=");

                    if (updateSetItem.isUseTargetTableProperty()){
                        sb.append(updateSetItem.getTargetTableName()).append(".`");
                        sb.append(updateSetItem.targetPropertyName).append("`");
                    } else {
                        sb.append(parameterName);
                        parameters.put(parameterName, updateSetItem.getPropertyValue());
                    }
                    i++;
                }

                this.combineByWrapper(this.conditionStatemennts, sb, parameters, " where ");

                break;

            case DELETE:
                sb.append("delete ").append(this.selectStatement.getAliasTableName()).append(" from ").append(this.selectStatement.getTableInfo().getTableName()).append(" ");
                sb.append(this.selectStatement.getAliasTableName());

                this.combineByWrapper(this.joinStatements, sb, parameters, " ");
                this.combineByWrapper(this.conditionStatemennts, sb, parameters, " where ");

                break;
        }

        this.setSqlWrapper(sb.toString(), parameters);
    }

    public List<MySqlStatement> getSelectSubMySqlStatements(){
        return this.selectSubMySqlStatements;
    }

    @Override
    public ResultMap getResultMap(){
        return this.selectStatement.getResultMap();
    }

    private void setGroupBy(SelectStatement statement, String propertyName){
        String tableName = statement.getAliasTableName();
        String columnName = statement.getTableInfo().getColumnByPropertyName(propertyName).getColumnName();
        SingleGroupStatement groupStatement = new SingleGroupStatement(columnName, tableName);

        if (this.groupStatemennts == null){
            this.groupStatemennts = new MutiSqlStatement(",");
        }
        this.groupStatemennts.addStatement(groupStatement);
    }

    private void setSubSelectFieldsSql(List<MySqlStatement> subMySqlStatements, StringBuilder sb, Map<String, Object> parameters){
        if (subMySqlStatements != null){
            for (MySqlStatement operator : subMySqlStatements){
                SelectStatement statement = operator.getSelectStatement();
                if (statement.hasSelectFields()){
                    this.combineByWrapper(statement, sb, parameters, ",");
                }

                this.setSubSelectFieldsSql(operator.getSelectSubMySqlStatements(), sb, parameters);
            }
        }
    }

    private void setOrderBy(SelectStatement statement, String propertyName, OrderType orderType){
        String tableName = statement.getAliasTableName();
        String columnName = statement.getTableInfo().getColumnByPropertyName(propertyName).getColumnName();

        SingleOrderStatement singleOrderStatement = new SingleOrderStatement(columnName, orderType, tableName);

        if (this.orderStatemennts == null){
            this.orderStatemennts = new MutiSqlStatement(",");
        }

        this.orderStatemennts.addStatement(singleOrderStatement);
    }

    private void setCondition(SelectStatement statement, String propertyName, ConditionType conditionType, Object value){
        if (this.conditionStatemennts == null){
            this.conditionStatemennts = new MutiSqlStatement(" and ");
        }

        String tableName = statement.getAliasTableName();
        String columnName = statement.getTableInfo().getColumnByPropertyName(propertyName).getColumnName();

        SingleConditionStatement singleConditionStatement = new SingleConditionStatement(tableName, columnName, conditionType, value);
        this.conditionStatemennts.addStatement(singleConditionStatement);
    }

    private SingleJoinStatement getSingleJoinStatement(Class<? extends BaseExtEntity> classType, JoinType joinType){
        SelectStatement statement = new SelectStatement(classType, this.tableIndentityProvider.getNextTableAsName());

        return this.getSingleJoinStatement(statement, joinType);
    }

    private SingleJoinStatement getSingleJoinStatement(SelectStatement statement, JoinType joinType){
        this.initJoinStatements();

        SingleJoinStatement singleJoinStatement = new SingleJoinStatement(this.selectStatement, statement, joinType);

        this.joinStatements.addStatement(singleJoinStatement);

        return singleJoinStatement;

    }

    private SingleJoinStatement getSingleJoinStatement(MySqlStatement subQuery, JoinType joinType){
        this.initJoinStatements();

        SingleJoinStatement singleJoinStatement = new SingleJoinStatement(this.selectStatement, subQuery, joinType);

        this.joinStatements.addStatement(singleJoinStatement);

        return singleJoinStatement;
    }

    private void initJoinStatements(){
        if (this.joinStatements == null){
            this.joinStatements = new MutiSqlStatement(" ");
        }
    }

    private void setHaving(SelectStatement statement, String expression, ConditionType conditionType, Object value){
        if (this.havingStatemennts == null){
            this.havingStatemennts = new MutiSqlStatement(",");
        }
        SingleHavingStatement singleHavingStatement = new SingleHavingStatement(statement, expression, conditionType, value);

        this.havingStatemennts.addStatement(singleHavingStatement);
    }

    private void combineByWrapper(SqlStatement statement, StringBuilder sb, Map<String, Object> parameters, String preStr){
        if (statement != null){
            sb.append(preStr);
            SqlWrapper wrapper = statement.getSqlWrapper();
            sb.append(wrapper.getSql());
            parameters.putAll(wrapper.getParameters());
        }
    }

    private void init(Class<? extends BaseExtEntity> entityClass, TableIndentityProvider tableIndentityProvider){
        this.entityClass = entityClass;
        this.tableIndentityProvider = tableIndentityProvider;
        this.tableInfo = TableInfo.getTableInfo(this.entityClass);
        this.selectStatement = new SelectStatement(this.entityClass, this.tableIndentityProvider.getNextTableAsName());
    }

    private enum OperateType {
        SELECT,
        UPDATE,
        DELETE,
    }

    private class UpdateSetItem{
        private String propertyName;
        private Object propertyValue;
        private String targetTableName;
        private String targetPropertyName;
        private boolean useTargetTableProperty;

        public UpdateSetItem(String propertyName, Object propertyValue){
            this.propertyName = propertyName;
            this.propertyValue = propertyValue;
            this.useTargetTableProperty = false;
        }

        public UpdateSetItem(String propertyName, String targetTableName, String targetPropertyName){
            this.propertyName = propertyName;
            this.targetTableName = targetTableName;
            this.targetPropertyName = targetPropertyName;
            this.useTargetTableProperty = true;
        }

        public String getPropertyName() {
            return propertyName;
        }

        public Object getPropertyValue() {
            return propertyValue;
        }

        public String getTargetPropertyName() {
            return targetPropertyName;
        }

        public String getTargetTableName() {
            return targetTableName;
        }

        public boolean isUseTargetTableProperty() {
            return useTargetTableProperty;
        }
    }
}

查詢物件就先介紹到這裡,原始碼下載