package com.xiaoleilu.hutool.db.sql;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import com.xiaoleilu.hutool.db.DbRuntimeException;
import com.xiaoleilu.hutool.db.DbUtil;
import com.xiaoleilu.hutool.db.Entity;
import com.xiaoleilu.hutool.db.dialect.DialectName;
import com.xiaoleilu.hutool.log.Log;
import com.xiaoleilu.hutool.log.StaticLog;
import com.xiaoleilu.hutool.util.ArrayUtil;
import com.xiaoleilu.hutool.util.CollectionUtil;
import com.xiaoleilu.hutool.util.ObjectUtil;
import com.xiaoleilu.hutool.util.StrUtil;
/**
* SQL构建器<br>
* 首先拼接SQL语句,值使用 ? 占位<br>
* 调用getParamValues()方法获得占位符对应的值
*
* @author Looly
*
*/
public class SqlBuilder {
private final static Log log = StaticLog.get();
private static boolean showSql;
private static boolean formatSql;
//--------------------------------------------------------------- Static methods start
/**
* 创建SQL构建器
* @return SQL构建器
*/
public static SqlBuilder create(){
return new SqlBuilder();
}
/**
* 创建SQL构建器
* @param wrapper 包装器
* @return SQL构建器
*/
public static SqlBuilder create(Wrapper wrapper){
return new SqlBuilder(wrapper);
}
/**
* 设置全局配置:是否通过debug日志显示SQL
* @param isShowSql 是否显示SQL
* @param isFormatSql 是否格式化显示的SQL
*/
public static void setShowSql(boolean isShowSql, boolean isFormatSql){
showSql = isShowSql;
formatSql = isFormatSql;
}
//--------------------------------------------------------------- Static methods end
//--------------------------------------------------------------- Enums start
/**
* SQL中多表关联用的关键字
* @author Looly
*
*/
public static enum Join{
/** 如果表中有至少一个匹配,则返回行 */
INNER,
/** 即使右表中没有匹配,也从左表返回所有的行 */
LEFT,
/** 即使左表中没有匹配,也从右表返回所有的行 */
RIGHT,
/** 只要其中一个表中存在匹配,就返回行 */
FULL
}
//--------------------------------------------------------------- Enums end
final private StringBuilder sql = new StringBuilder();
/** 占位符对应的值列表 */
final private List<Object> paramValues = new ArrayList<Object>();
/** 包装器 */
private Wrapper wrapper;
//--------------------------------------------------------------- Constructor start
public SqlBuilder() {
}
public SqlBuilder(Wrapper wrapper) {
this.wrapper = wrapper;
}
//--------------------------------------------------------------- Constructor end
//--------------------------------------------------------------- Builder start
/**
* 插入,使用默认的ANSI方言
* @param entity 实体
* @return 自己
*/
public SqlBuilder insert(Entity entity){
return this.insert(entity, DialectName.ANSI);
}
/**
* 插入
* @param entity 实体
* @param dialectName 方言名
* @return 自己
*/
public SqlBuilder insert(Entity entity, DialectName dialectName){
//验证
DbUtil.validateEntity(entity);
if(null != wrapper) {
//包装字段名
entity = wrapper.wrap(entity);
}
final boolean isOracle = ObjectUtil.equal(dialectName, DialectName.ORACLE);//对Oracle的特殊处理
final StringBuilder fields = new StringBuilder();
final StringBuilder placeHolder = new StringBuilder();
boolean isFirst = true;
String field;
Object value;
for (Entry<String, Object> entry : entity.entrySet()) {
field = entry.getKey();
value = entry.getValue();
if(StrUtil.isNotBlank(field) && null != value){//只对值为非空的数据做插入操作
if(isFirst){
isFirst = false;
}else{
//非第一个参数,追加逗号
fields.append(", ");
placeHolder.append(", ");
}
fields.append(field);
if(isOracle && value instanceof String && StrUtil.endWithIgnoreCase((String)value, ".nextval")) {
//Oracle的特殊自增键,通过字段名.nextval获得下一个值
placeHolder.append(value);
}else {
placeHolder.append("?");
paramValues.add(value);
}
}
}
sql.append("INSERT INTO ")
.append(entity.getTableName()).append(" (").append(fields).append(") VALUES (")
.append(placeHolder.toString()).append(")");
return this;
}
/**
* 删除
* @param tableName 表名
* @return 自己
*/
public SqlBuilder delete(String tableName){
if(StrUtil.isBlank(tableName)) {
throw new DbRuntimeException("Table name is blank !");
}
if(null != wrapper) {
//包装表名
tableName = wrapper.wrap(tableName);
}
sql.append("DELETE FROM ").append(tableName);
return this;
}
/**
* 更新
* @param entity 要更新的实体
* @return 自己
*/
public SqlBuilder update(Entity entity){
//验证
DbUtil.validateEntity(entity);
if(null != wrapper) {
//包装字段名
entity = wrapper.wrap(entity);
}
sql.append("UPDATE ").append(entity.getTableName()).append(" SET ");
String key;
for (Entry<String, Object> entry : entity.entrySet()) {
key = entry.getKey();
if(StrUtil.isNotBlank(key)){
if (paramValues.size() > 0) {
sql.append(", ");
}
sql.append(entry.getKey()).append(" = ? ");
paramValues.add(entry.getValue());//更新不对空做处理,因为存在清空字段的情况
}
}
return this;
}
/**
* 查询
* @param isDistinct 是否添加DISTINCT关键字(查询唯一结果)
* @param fields 查询的字段
* @return 自己
*/
public SqlBuilder select(boolean isDistinct, String... fields){
return select(isDistinct, Arrays.asList(fields));
}
/**
* 查询
* @param isDistinct 是否添加DISTINCT关键字(查询唯一结果)
* @param fields 查询的字段
* @return 自己
*/
public SqlBuilder select(boolean isDistinct, Collection<String> fields){
sql.append("SELECT ");
if(isDistinct) {
sql.append("DISTINCT ");
}
if (CollectionUtil.isEmpty(fields)) {
sql.append("*");
} else {
if(null != wrapper) {
//包装字段名
fields = wrapper.wrap(fields);
}
sql.append(CollectionUtil.join(fields, StrUtil.COMMA));
}
return this;
}
/**
* 查询(非Distinct)
* @param fields 查询的字段
* @return 自己
*/
public SqlBuilder select(String... fields){
return select(false, fields);
}
/**
* 查询(非Distinct)
* @param fields 查询的字段
* @return 自己
*/
public SqlBuilder select(Collection<String> fields){
return select(false, fields);
}
/**
* 添加 from语句
* @param tableNames 表名列表(多个表名用于多表查询)
* @return 自己
*/
public SqlBuilder from(String... tableNames){
if(ArrayUtil.isEmpty(tableNames) || StrUtil.hasBlank(tableNames)) {
throw new DbRuntimeException("Table name is blank in table names !");
}
if(null != wrapper) {
//包装表名
tableNames = wrapper.wrap(tableNames);
}
sql.append(" FROM ").append(ArrayUtil.join(tableNames, StrUtil.COMMA));
return this;
}
/**
* 添加Where语句<br>
* 只支持单一的逻辑运算符(例如多个条件之间)
*
* @param logicalOperator 逻辑运算符
* @param conditions 条件,当条件为空时,只添加WHERE关键字
* @return 自己
*/
public SqlBuilder where(LogicalOperator logicalOperator, Condition... conditions){
if(ArrayUtil.isNotEmpty(conditions)) {
if(null != wrapper) {
//包装字段名
conditions = wrapper.wrap(conditions);
}
where(buildCondition(logicalOperator, conditions));
}
return this;
}
/**
* 添加Where语句<br>
*
* @param where WHERE语句之后跟的条件语句字符串
* @return 自己
*/
public SqlBuilder where(String where){
if(StrUtil.isNotBlank(where)){
sql.append(" WHERE ").append(where);
}
return this;
}
/**
* 多值选择
* @param field 字段名
* @param values 值列表
* @return 自身
*/
@SuppressWarnings("unchecked")
public <T> SqlBuilder in(String field, T... values) {
sql.append(wrapper.wrap(field)).append(" IN ").append("(").append(ArrayUtil.join(values, StrUtil.COMMA)).append(")");
return this;
}
/**
* 分组
* @param fields 字段
* @return 自己
*/
public SqlBuilder groupBy(String... fields){
if(ArrayUtil.isNotEmpty(fields)) {
if(null != wrapper) {
//包装字段名
fields = wrapper.wrap(fields);
}
sql.append(" GROUP BY ").append(ArrayUtil.join(fields, StrUtil.COMMA));
}
return this;
}
/**
* 添加Having语句
* @param logicalOperator 逻辑运算符
* @param conditions 条件
* @return 自己
*/
public SqlBuilder having(LogicalOperator logicalOperator, Condition... conditions){
if(ArrayUtil.isNotEmpty(conditions)) {
if(null != wrapper) {
//包装字段名
conditions = wrapper.wrap(conditions);
}
having(buildCondition(logicalOperator, conditions));
}
return this;
}
/**
* 添加Having语句
* @param having 条件语句
* @return 自己
*/
public SqlBuilder having(String having){
if(StrUtil.isNotBlank(having)){
sql.append(" HAVING ").append(having);
}
return this;
}
/**
* 排序
* @param orders 排序对象
* @return 自己
*/
public SqlBuilder orderBy(Order... orders){
if(ArrayUtil.isEmpty(orders)){
return this;
}
sql.append(" ORDER BY ");
String field = null;
boolean isFirst = true;
for (Order order : orders) {
if(null != wrapper) {
//包装字段名
field = wrapper.wrap(order.getField());
}
if(StrUtil.isBlank(field)){
continue;
}
//只有在非第一项前添加逗号
if (isFirst) {
isFirst = false;
} else {
sql.append(StrUtil.COMMA);
}
sql.append(field);
final Direction direction = order.getDirection();
if(null != direction){
sql.append(StrUtil.SPACE).append(direction);
}
}
return this;
}
/**
* 多表关联
* @param tableName 被关联的表名
* @param join 内联方式
* @return 自己
*/
public SqlBuilder join(String tableName, Join join){
if(StrUtil.isBlank(tableName)) {
throw new DbRuntimeException("Table name is blank !");
}
if(null != join) {
sql.append(StrUtil.SPACE).append(join).append(" JOIN ");
if(null != wrapper) {
//包装表名
tableName = wrapper.wrap(tableName);
}
sql.append(tableName);
}
return this;
}
/**
* 配合JOIN的 ON语句,多表关联的条件语句<br>
* 只支持单一的逻辑运算符(例如多个条件之间)
* @param logicalOperator 逻辑运算符
* @param conditions 条件
* @return 自己
*/
public SqlBuilder on(LogicalOperator logicalOperator, Condition... conditions){
if(ArrayUtil.isNotEmpty(conditions)) {
if(null != wrapper) {
//包装字段名
conditions = wrapper.wrap(conditions);
}
on(buildCondition(logicalOperator, conditions));
}
return this;
}
/**
* 配合JOIN的 ON语句,多表关联的条件语句<br>
* 只支持单一的逻辑运算符(例如多个条件之间)
* @param on 条件
* @return 自己
*/
public SqlBuilder on(String on){
if(StrUtil.isNotBlank(on)){
this.sql.append(" ON ").append(on);
}
return this;
}
/**
* 追加SQL其它部分
* @param sqlPart SQL其它部分
* @return 自己
*/
public SqlBuilder append(Object sqlPart){
if(null != sqlPart) {
this.sql.append(sqlPart);
}
return this;
}
/**
* 构建查询SQL
* @param query {@link Query}
* @return this
*/
public SqlBuilder query(Query query){
return this
.select(query.getFields())
.from(query.getTableNames())
.where(LogicalOperator.AND, query.getWhere());
}
//--------------------------------------------------------------- Builder end
/**
* 获得占位符对应的值列表<br>
* @return 占位符对应的值列表
*/
public List<Object> getParamValues() {
return paramValues;
}
/**
* 获得占位符对应的值列表<br>
* @return 占位符对应的值列表
*/
public Object[] getParamValueArray() {
return paramValues.toArray(new Object[paramValues.size()]);
}
/**
* 构建
* @return 构建好的SQL语句
*/
public String build(){
return this.build(showSql);
}
/**
* 构建
* @param isShowDebugSql 显示SQL的debug日志
* @return 构建好的SQL语句
*/
public String build(boolean isShowDebugSql){
final String sqlStr = this.sql.toString().trim();
if(isShowDebugSql){
log.debug("\n{}", formatSql ? SqlFormatter.format(sqlStr) : sqlStr);
}
return sqlStr;
}
@Override
public String toString() {
return this.build();
}
//--------------------------------------------------------------- private method start
/**
* 构建组合条件
* @param logicalOperator 逻辑运算符
* @param conditions 条件对象
* @return 构建后的SQL语句条件部分
*/
private String buildCondition(LogicalOperator logicalOperator, Condition... conditions){
if(ArrayUtil.isEmpty(conditions)) {
return StrUtil.EMPTY;
}
if(null == logicalOperator) {
logicalOperator = LogicalOperator.AND;
}
final StringBuilder conditionStr = new StringBuilder();
boolean isFirst = true;
for (Condition condition : conditions) {
//添加逻辑运算符
if(isFirst){
isFirst = false;
}else {
conditionStr.append(StrUtil.SPACE).append(logicalOperator).append(StrUtil.SPACE);
}
//添加条件表达式
conditionStr.append(condition.getField()).append(StrUtil.SPACE).append(condition.getOperator());
if(condition.isPlaceHolder()) {
//使用条件表达式占位符
conditionStr.append(" ?");
paramValues.add(condition.getValue());
}else {
//直接使用条件值
conditionStr.append(condition.getValue());
}
}
return conditionStr.toString();
}
//--------------------------------------------------------------- private method end
}