package com.ctrip.platform.dal.dao.sqlbuilder;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import com.ctrip.platform.dal.common.enums.DatabaseCategory;
import com.ctrip.platform.dal.dao.StatementParameter;
import com.ctrip.platform.dal.dao.StatementParameters;
public abstract class AbstractSqlBuilder implements TableSqlBuilder {
protected DatabaseCategory dbCategory = DatabaseCategory.MySql;
protected StatementParameters parameters = new StatementParameters();
protected int index = 1;
protected List<FieldEntry> selectOrUpdataFieldEntrys = new ArrayList<FieldEntry>();
private LinkedList<WhereClauseEntry> whereClauseEntries = new LinkedList<>();
private List<FieldEntry> whereFieldEntrys = new ArrayList<FieldEntry>();
private String tableName;
private boolean compatible = false;
public boolean isCompatible() {
return compatible;
}
public void setCompatible(boolean compatible) {
this.compatible = compatible;
}
public AbstractSqlBuilder from(String tableName) throws SQLException {
if(tableName ==null || tableName.isEmpty())
throw new SQLException("table name is illegal.");
this.tableName = tableName;
return this;
}
public AbstractSqlBuilder setDatabaseCategory(DatabaseCategory dbCategory) throws SQLException {
Objects.requireNonNull(dbCategory, "DatabaseCategory can't be null.");
this.dbCategory = dbCategory;
return this;
}
public String getTableName() {
return tableName;
}
public String getTableName(String shardStr) {
return tableName + shardStr;
}
/**
* 获取StatementParameters
* @return
*/
public StatementParameters buildParameters(){
parameters = new StatementParameters();
index = 1;
for(FieldEntry entry : selectOrUpdataFieldEntrys) {
parameters.add(new StatementParameter(index++, entry.getSqlType(), entry.getParamValue()).setSensitive(entry.isSensitive()).setName(entry.getFieldName()).setInParam(entry.isInParam()));
}
for(FieldEntry entry : whereFieldEntrys){
parameters.add(new StatementParameter(index++, entry.getSqlType(), entry.getParamValue()).setSensitive(entry.isSensitive()).setName(entry.getFieldName()).setInParam(entry.isInParam()));
}
return this.parameters;
}
/**
* 获取设置StatementParameters的index,返回值为构建后的sql中需要传值的个数加1
* @return
*/
public int getStatementParameterIndex(){
return this.index;
}
/**
* 对字段进行包裹,数据库是MySQL则用 `进行包裹,数据库是SqlServer则用[]进行包裹
* @param fieldName
* @return
*/
public String wrapField(String fieldName){
return wrapField(dbCategory, fieldName);
}
/**
* 对字段进行包裹,数据库是MySQL则用 `进行包裹,数据库是SqlServer则用[]进行包裹
* @param fieldName
* @return
*/
public static String wrapField(DatabaseCategory dbCategory, String fieldName){
if("*".equalsIgnoreCase(fieldName) || fieldName.contains("ROW_NUMBER") || fieldName.contains(",")){
return fieldName;
}
return dbCategory.quote(fieldName);
}
/**
* build sql.
* @return
*/
public abstract String build();
private static final String EMPTY = "";
/**
* build where expression
* @return
*/
public String getWhereExp(){
// return whereExp.toString().trim().isEmpty()? "": "WHERE"+ whereExp.toString();
if(whereClauseEntries.size() == 0)
return EMPTY;
LinkedList<WhereClauseEntry> filtered = new LinkedList<>();
for(WhereClauseEntry entry: whereClauseEntries) {
if(entry.isClause() && entry.isNull()){
meltDownNullValue(filtered);
continue;
}
if(entry.isBracket() && !((BracketClauseEntry)entry).isLeft()){
if(meltDownRightBracket(filtered))
continue;
}
// AND/OR
if(entry.isOperator() && !entry.isClause()) {
if(meltDownAndOrOperator(filtered))
continue;
}
filtered.add(entry);
}
StringBuilder sb = new StringBuilder();
for(WhereClauseEntry entry: filtered) {
sb.append(entry.getClause(dbCategory)).append(" ");
}
String whereClause = sb.toString().trim();
if(whereClause.isEmpty())
return "";
return whereClause;
}
private boolean meltDownAndOrOperator(LinkedList<WhereClauseEntry> filtered) {
// If it is the first element
if(filtered.size() == 0)
return true;
WhereClauseEntry entry = filtered.getLast();
// The last one is "("
if(entry.isBracket() && ((BracketClauseEntry)entry).isLeft())
return true;
// AND/OR/NOT AND/OR
if(entry.isOperator()) {
return true;
}
return false;
}
private boolean meltDownRightBracket(LinkedList<WhereClauseEntry> filtered) {
int bracketCount = 1;
while(filtered.size() > 0) {
WhereClauseEntry entry = filtered.getLast();
// One ")" only remove one "("
if(entry.isBracket() && ((BracketClauseEntry)entry).isLeft() && bracketCount == 1){
filtered.removeLast();
bracketCount--;
continue;
}
// Remove any leading AND/OR/NOT (BOT is both operator and clause)
if(entry.isOperator()) {
filtered.removeLast();
continue;
}
break;
}
return bracketCount == 0? true : false;
}
private void meltDownNullValue(LinkedList<WhereClauseEntry> filtered) {
if(filtered.size() == 0)
return;
while(filtered.size() > 0) {
WhereClauseEntry entry = filtered.getLast();
// Remove any leading AND/OR/NOT (NOT is both operator and clause)
if(entry.isOperator()) {
filtered.removeLast();
continue;
}
break;
}
}
/**
* 追加AND连接
* @return
*/
public AbstractSqlBuilder and(){
return add(OperatorClauseEntry.AND());
}
/**
* 追加OR连接
* @return
*/
public AbstractSqlBuilder or(){
return add(OperatorClauseEntry.OR());
}
private static final boolean DEFAULT_SENSITIVE = false;
/**
* 等于操作,且字段值不能为NULL,否则会抛出SQLException
* @param field 字段
* @param paramValue 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder equal(String field, Object paramValue, int sqlType) throws SQLException {
return equal(field, paramValue, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder equal(String field, Object paramValue, int sqlType, boolean sensitive) throws SQLException {
return addParam(field, "=", paramValue, sqlType, sensitive);
}
/**
* 等于操作,若字段值为NULL,则此条件不会加入SQL中
* @param field 字段
* @param paramValue 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder equalNullable(String field, Object paramValue, int sqlType) {
return equalNullable(field, paramValue, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder equalNullable(String field, Object paramValue, int sqlType, boolean sensitive) {
return addParamNullable(field, "=", paramValue, sqlType, sensitive);
}
/**
* 不等于操作,且字段值不能为NULL,否则会抛出SQLException
* @param field 字段
* @param paramValue 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder notEqual(String field, Object paramValue, int sqlType) throws SQLException {
return notEqual(field, paramValue, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder notEqual(String field, Object paramValue, int sqlType, boolean sensitive) throws SQLException {
return addParam(field, "!=", paramValue, sqlType, sensitive);
}
/**
* 不等于操作,若字段值为NULL,则此条件不会加入SQL中
* @param field 字段
* @param paramValue 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder notEqualNullable(String field, Object paramValue, int sqlType) {
return notEqualNullable(field, paramValue, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder notEqualNullable(String field, Object paramValue, int sqlType, boolean sensitive) {
return addParamNullable(field, "!=", paramValue, sqlType, sensitive);
}
/**
* 大于操作,且字段值不能为NULL,否则会抛出SQLException
* @param field 字段
* @param paramValue 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder greaterThan(String field, Object paramValue, int sqlType) throws SQLException {
return greaterThan(field, paramValue, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder greaterThan(String field, Object paramValue, int sqlType, boolean sensitive) throws SQLException {
return addParam(field, ">", paramValue, sqlType, sensitive);
}
/**
* 大于操作,若字段值为NULL,则此条件不会加入SQL中
* @param field 字段
* @param paramValue 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder greaterThanNullable(String field, Object paramValue, int sqlType) {
return greaterThanNullable(field, paramValue, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder greaterThanNullable(String field, Object paramValue, int sqlType, boolean sensitive) {
return addParamNullable(field, ">", paramValue, sqlType, sensitive);
}
/**
* 大于等于操作,且字段值不能为NULL,否则会抛出SQLException
* @param field 字段
* @param paramValue 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder greaterThanEquals(String field, Object paramValue, int sqlType) throws SQLException {
return greaterThanEquals(field, paramValue, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder greaterThanEquals(String field, Object paramValue, int sqlType, boolean sensitive) throws SQLException {
return addParam(field, ">=", paramValue, sqlType, sensitive);
}
/**
* 大于等于操作,若字段值为NULL,则此条件不会加入SQL中
* @param field 字段
* @param paramValue 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder greaterThanEqualsNullable(String field, Object paramValue, int sqlType) {
return greaterThanEqualsNullable(field, paramValue, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder greaterThanEqualsNullable(String field, Object paramValue, int sqlType, boolean sensitive) {
return addParamNullable(field, ">=", paramValue, sqlType, sensitive);
}
/**
* 小于操作,且字段值不能为NULL,否则会抛出SQLException
* @param field 字段
* @param paramValue 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder lessThan(String field, Object paramValue, int sqlType) throws SQLException {
return lessThan(field, paramValue, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder lessThan(String field, Object paramValue, int sqlType, boolean sensitive) throws SQLException {
return addParam(field, "<", paramValue, sqlType, sensitive);
}
/**
* 小于操作,若字段值为NULL,则此条件不会加入SQL中
* @param field 字段
* @param paramValue 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder lessThanNullable(String field, Object paramValue, int sqlType) {
return lessThanNullable(field, paramValue, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder lessThanNullable(String field, Object paramValue, int sqlType, boolean sensitive) {
return addParamNullable(field, "<", paramValue, sqlType, sensitive);
}
/**
* 小于等于操作,且字段值不能为NULL,否则会抛出SQLException
* @param field 字段
* @param paramValue 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder lessThanEquals(String field, Object paramValue, int sqlType) throws SQLException {
return lessThanEquals(field, paramValue, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder lessThanEquals(String field, Object paramValue, int sqlType, boolean sensitive) throws SQLException {
return addParam(field, "<=", paramValue, sqlType, sensitive);
}
/**
* 小于等于操作,若字段值为NULL,则此条件不会加入SQL中
* @param field 字段
* @param paramValue 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder lessThanEqualsNullable(String field, Object paramValue, int sqlType) {
return lessThanEqualsNullable(field, paramValue, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder lessThanEqualsNullable(String field, Object paramValue, int sqlType, boolean sensitive) {
return addParamNullable(field, "<=", paramValue, sqlType, sensitive);
}
/**
* Between操作,且字段值不能为NULL,否则会抛出SQLException
* @param field 字段
* @param paramValue1 字段值1
* @param paramValue2 字段值2
* @return
* @throws SQLException
*/
public AbstractSqlBuilder between(String field, Object paramValue1, Object paramValue2, int sqlType) throws SQLException {
return between(field, paramValue1, paramValue2, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder between(String field, Object paramValue1, Object paramValue2, int sqlType, boolean sensitive) throws SQLException {
if (paramValue1 == null || paramValue2 == null)
throw new SQLException(field + " is not support null value.");
return add(new BetweenClauseEntry(field, paramValue1, paramValue2, sqlType, sensitive, whereFieldEntrys));
}
/**
* Between操作,若字段值为NULL,则此条件不会加入SQL中
* @param field 字段
* @param paramValue1 字段值1
* @param paramValue2 字段值2
* @return
* @throws SQLException
*/
public AbstractSqlBuilder betweenNullable(String field, Object paramValue1, Object paramValue2, int sqlType) {
return betweenNullable(field, paramValue1, paramValue2, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder betweenNullable(String field, Object paramValue1, Object paramValue2, int sqlType, boolean sensitive) {
//如果paramValue==null,则field不会作为条件加入到最终的SQL中。
if(paramValue1 == null || paramValue2 == null)
return add(new NullValueClauseEntry());
return add(new BetweenClauseEntry(field, paramValue1, paramValue2, sqlType, sensitive, whereFieldEntrys));
}
/**
* Like操作,且字段值不能为NULL,否则会抛出SQLException
* @param field 字段
* @param paramValue 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder like(String field, Object paramValue, int sqlType) throws SQLException {
return like(field, paramValue, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder like(String field, Object paramValue, int sqlType, boolean sensitive) throws SQLException {
return addParam(field, "LIKE", paramValue, sqlType, sensitive);
}
/**
* Like操作,若字段值为NULL,则此条件不会加入SQL中
* @param field 字段
* @param paramValue 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder likeNullable(String field, Object paramValue, int sqlType) {
return likeNullable(field, paramValue, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder likeNullable(String field, Object paramValue, int sqlType, boolean sensitive) {
return addParamNullable(field, "LIKE", paramValue, sqlType, sensitive);
}
/**
* In操作,且字段值不能为NULL,否则会抛出SQLException
* @param field 字段
* @param paramValues 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder in(String field, List<?> paramValues, int sqlType) throws SQLException {
return in(field, paramValues, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder in(String field, List<?> paramValues, int sqlType, boolean sensitive) throws SQLException {
if(null == paramValues || paramValues.size() == 0)
throw new SQLException(field + " must have more than one value.");
for(Object obj:paramValues)
if(obj==null)
throw new SQLException(field + " is not support null value.");
return addInParam(field, paramValues, sqlType, sensitive);
}
/**
* In操作,允许字段值为NULL.
* 若传入的字段值数量为0,则抛出异常。
* @param field 字段
* @param paramValues 字段值
* @return
* @throws SQLException
*/
public AbstractSqlBuilder inNullable(String field, List<?> paramValues, int sqlType) throws SQLException {
return inNullable(field, paramValues, sqlType, DEFAULT_SENSITIVE);
}
public AbstractSqlBuilder inNullable(String field, List<?> paramValues, int sqlType, boolean sensitive) throws SQLException {
if(null == paramValues){
return add(new NullValueClauseEntry());
}
if(paramValues.size() == 0){
throw new SQLException(field + " must have more than one value.");
}
Iterator<?> ite = paramValues.iterator();
while(ite.hasNext()){
if(ite.next()==null){
ite.remove();
}
}
if(paramValues.size() == 0){
return add(new NullValueClauseEntry());
}
return addInParam(field, paramValues, sqlType, sensitive);
}
/**
* Is null操作
* @param field 字段
* @return
*/
public AbstractSqlBuilder isNull(String field){
return add(new NullClauseEntry(field, true));
}
/**
* Is not null操作
* @param field 字段
* @return
*/
public AbstractSqlBuilder isNotNull(String field){
return add(new NullClauseEntry(field, false));
}
/**
* Add "("
*/
public AbstractSqlBuilder leftBracket(){
return add(BracketClauseEntry.leftBracket());
}
/**
* Add ")"
*/
public AbstractSqlBuilder rightBracket(){
return add(BracketClauseEntry.rightBracket());
}
/**
* Add "NOT"
*/
public AbstractSqlBuilder not(){
return add(new NotClauseEntry());
}
private AbstractSqlBuilder addInParam(String field, List<?> paramValues, int sqlType, boolean sensitive){
return add(new InClauseEntry(field, paramValues, sqlType, sensitive, whereFieldEntrys, compatible));
}
private AbstractSqlBuilder addParam(String field, String condition, Object paramValue, int sqlType, boolean sensitive) throws SQLException{
if(paramValue == null)
throw new SQLException(field + " is not support null value.");
return add(new SingleClauseEntry(field, condition, paramValue, sqlType, sensitive, whereFieldEntrys));
}
private AbstractSqlBuilder addParamNullable(String field, String condition, Object paramValue, int sqlType, boolean sensitive){
if(paramValue == null)
return add(new NullValueClauseEntry());
return add(new SingleClauseEntry(field, condition, paramValue, sqlType, sensitive, whereFieldEntrys));
}
private static abstract class WhereClauseEntry {
private String clause;
public boolean isOperator() {
return false;
}
public boolean isBracket() {
return false;
}
public boolean isClause() {
return false;
}
public boolean isNull() {
return false;
}
//To make it build late when DatabaseCategory is set
public abstract String getClause(DatabaseCategory dbCategory);
public String toString() {
return clause;
}
}
private static class NullValueClauseEntry extends WhereClauseEntry {
public boolean isNull() {
return true;
}
public boolean isClause() {
return true;
}
public String getClause(DatabaseCategory dbCategory) {
return "";
}
}
private static class SingleClauseEntry extends WhereClauseEntry {
private String condition;
private FieldEntry entry;
public SingleClauseEntry(String field, String condition, Object paramValue, int sqlType, boolean sensitive, List<FieldEntry> whereFieldEntrys) {
this.condition = condition;
entry = new FieldEntry(field, paramValue, sqlType, sensitive);
whereFieldEntrys.add(entry);
}
public boolean isClause() {
return true;
}
public String getClause(DatabaseCategory dbCategory) {
return String.format("%s %s ?", wrapField(dbCategory, entry.getFieldName()), condition);
}
}
private static class BetweenClauseEntry extends WhereClauseEntry {
private FieldEntry entry1;
private FieldEntry entry2;
public BetweenClauseEntry(String field, Object paramValue1, Object paramValue2, int sqlType, boolean sensitive, List<FieldEntry> whereFieldEntrys) {
entry1 = new FieldEntry(field, paramValue1, sqlType, sensitive);
entry2 = new FieldEntry(field, paramValue2, sqlType, sensitive);
whereFieldEntrys.add(entry1);
whereFieldEntrys.add(entry2);
}
public boolean isClause() {
return true;
}
public String getClause(DatabaseCategory dbCategory) {
return wrapField(dbCategory, entry1.getFieldName()) + " BETWEEN ? AND ?";
}
}
private static class InClauseEntry extends WhereClauseEntry {
private String field;
private String questionMarkList;
private boolean compatible;
private static final String IN_CLAUSE = " in ( ? )";
private List<FieldEntry> entries;
public InClauseEntry(String field, List<?> paramValues, int sqlType, boolean sensitive, List<FieldEntry> whereFieldEntrys, boolean compatible){
this.field = field;
this.compatible = compatible;
if(compatible)
create(field, paramValues, sqlType, sensitive, whereFieldEntrys);
else{
whereFieldEntrys.add(new FieldEntry(field, paramValues, sqlType, sensitive).setInParam(true));
}
}
private void create(String field, List<?> paramValues, int sqlType, boolean sensitive, List<FieldEntry> whereFieldEntrys){
StringBuilder temp = new StringBuilder();
temp.append(" in ( ");
entries = new ArrayList<>(paramValues.size());
for(int i=0,size=paramValues.size();i<size;i++){
temp.append("?");
if(i!=size-1){
temp.append(", ");
}
FieldEntry entry = new FieldEntry(field, paramValues.get(i), sqlType, sensitive);
entries.add(entry);
}
temp.append(" )");
questionMarkList = temp.toString();
whereFieldEntrys.addAll(entries);
}
public boolean isClause() {
return true;
}
public String getClause(DatabaseCategory dbCategory) {
return compatible ?
wrapField(dbCategory, field) + questionMarkList:
wrapField(dbCategory, field) + IN_CLAUSE;
}
}
private static class NullClauseEntry extends WhereClauseEntry {
private String field;
private boolean isNull;
public NullClauseEntry(String field, boolean isNull) {
this.field = field;
this.isNull= isNull;
}
public boolean isClause() {
return true;
}
public String getClause(DatabaseCategory dbCategory) {
return wrapField(dbCategory, field) + (isNull ? " IS NULL" : " IS NOT NULL");
}
}
private static class NotClauseEntry extends WhereClauseEntry {
public NotClauseEntry() {
}
public boolean isClause() {
return true;
}
public boolean isOperator() {
return true;
}
public String getClause(DatabaseCategory dbCategory) {
return "NOT";
}
}
private static class OperatorClauseEntry extends WhereClauseEntry {
private String operator;
public OperatorClauseEntry(String operator) {
this.operator = operator;
}
public String getClause(DatabaseCategory dbCategory) {
return operator;
}
@Override
public boolean isOperator() {
return true;
}
static OperatorClauseEntry AND() {
return new OperatorClauseEntry("AND");
}
static OperatorClauseEntry OR() {
return new OperatorClauseEntry("OR");
}
}
private static class BracketClauseEntry extends WhereClauseEntry {
private boolean left;
public BracketClauseEntry(boolean isLeft) {
left = isLeft;
}
public String getClause(DatabaseCategory dbCategory) {
return left? "(" : ")";
}
public boolean isBracket() {
return true;
}
public boolean isLeft() {
return left;
}
static BracketClauseEntry leftBracket() {
return new BracketClauseEntry(true);
}
static BracketClauseEntry rightBracket() {
return new BracketClauseEntry(false);
}
}
private AbstractSqlBuilder add(WhereClauseEntry entry) {
whereClauseEntries.add(entry);
return this;
}
}