Commit 02897568 authored by liang.tang's avatar liang.tang
Browse files

magic-api

parents
Pipeline #222 failed with stages
in 0 seconds
package org.ssssssss.magicapi.modules.db;
import org.ssssssss.magicapi.core.context.RequestContext;
import org.ssssssss.magicapi.core.context.RequestEntity;
import org.ssssssss.magicapi.modules.db.inteceptor.SQLInterceptor;
import org.ssssssss.magicapi.modules.db.mybatis.MybatisParser;
import org.ssssssss.magicapi.modules.db.mybatis.SqlNode;
import org.ssssssss.magicapi.modules.db.mybatis.TextSqlNode;
import org.ssssssss.script.runtime.RuntimeContext;
import java.util.*;
import java.util.function.Supplier;
import java.util.regex.Pattern;
/**
* SQL参数处理
*
* @author mxd
*/
public class BoundSql {
private static final Pattern REPLACE_MULTI_WHITE_LINE = Pattern.compile("(\r?\n(\\s*\r?\n)+)");
private static final List<String> MYBATIS_TAGS = Arrays.asList("</where>", "</if>", "</trim>", "</set>", "</foreach>");
private String sqlOrXml;
private List<Object> parameters = new ArrayList<>();
private Set<String> excludeColumns;
private SQLModule sqlModule;
private Map<String, Object> bindParameters;
private RuntimeContext runtimeContext;
public BoundSql(RuntimeContext runtimeContext, String sqlOrXml, List<Object> parameters, SQLModule sqlModule) {
this.sqlOrXml = sqlOrXml;
this.parameters = parameters;
this.sqlModule = sqlModule;
this.runtimeContext = runtimeContext;
}
public BoundSql(RuntimeContext runtimeContext, String sqlOrXml, Map<String, Object> parameters, SQLModule sqlModule) {
this.sqlOrXml = sqlOrXml;
this.bindParameters = parameters;
this.sqlModule = sqlModule;
this.runtimeContext = runtimeContext;
this.init();
}
private BoundSql(RuntimeContext runtimeContext, String sqlOrXml) {
this.sqlOrXml = sqlOrXml;
this.runtimeContext = runtimeContext;
this.init();
}
BoundSql(RuntimeContext runtimeContext, String sql, SQLModule sqlModule) {
this(runtimeContext, sql);
this.sqlModule = sqlModule;
}
private BoundSql() {
}
private void init() {
Map<String, Object> varMap = new HashMap<>();
if (this.bindParameters != null) {
varMap.putAll(this.bindParameters);
} else {
varMap.putAll(runtimeContext.getVarMap());
}
if (MYBATIS_TAGS.stream().anyMatch(it -> this.sqlOrXml.contains(it))) {
SqlNode sqlNode = MybatisParser.parse(this.sqlOrXml);
this.sqlOrXml = sqlNode.getSql(varMap);
this.parameters = sqlNode.getParameters();
} else {
normal(varMap);
}
}
private void normal(Map<String, Object> varMap) {
this.sqlOrXml = TextSqlNode.parseSql(this.sqlOrXml, varMap, parameters);
this.sqlOrXml = this.sqlOrXml == null ? null : REPLACE_MULTI_WHITE_LINE.matcher(this.sqlOrXml.trim()).replaceAll("\r\n");
}
public SQLModule getSqlModule() {
return sqlModule;
}
BoundSql copy(String newSqlOrXml) {
BoundSql boundSql = new BoundSql();
boundSql.parameters = this.parameters;
boundSql.bindParameters = this.bindParameters;
boundSql.sqlOrXml = newSqlOrXml;
boundSql.excludeColumns = this.excludeColumns;
boundSql.sqlModule = this.sqlModule;
boundSql.runtimeContext = this.runtimeContext;
return boundSql;
}
public Set<String> getExcludeColumns() {
return excludeColumns;
}
public void setExcludeColumns(Set<String> excludeColumns) {
this.excludeColumns = excludeColumns;
}
/**
* 添加SQL参数
*/
public void addParameter(Object value) {
parameters.add(value);
}
/**
* 获取要执行的SQL
*/
public String getSql() {
return sqlOrXml;
}
/**
* 设置要执行的SQL
*/
public void setSql(String sql) {
this.sqlOrXml = sql;
}
/**
* 获取要执行的参数
*/
public Object[] getParameters() {
return parameters.toArray();
}
/**
* 设置要执行的参数
*/
public void setParameters(List<Object> parameters) {
this.parameters = parameters;
}
public RuntimeContext getRuntimeContext() {
return runtimeContext;
}
/**
* 获取缓存值
*/
@SuppressWarnings({"unchecked"})
private <T> T getCacheValue(String sql, Object[] params, Supplier<T> supplier) {
if (sqlModule.getCacheName() == null) {
return supplier.get();
}
String cacheKey = sqlModule.getSqlCache().buildSqlCacheKey(sql, params);
Object cacheValue = sqlModule.getSqlCache().get(sqlModule.getCacheName(), cacheKey);
if (cacheValue != null) {
return (T) cacheValue;
}
T value = supplier.get();
sqlModule.getSqlCache().put(sqlModule.getCacheName(), cacheKey, value, sqlModule.getTtl());
return value;
}
/**
* 获取缓存值
*/
@SuppressWarnings("unchecked")
<T> T execute(List<SQLInterceptor> interceptors, Supplier<T> supplier) {
RequestEntity requestEntity = RequestContext.getRequestEntity();
interceptors.forEach(interceptor -> interceptor.preHandle(this, requestEntity));
Supplier<T> newSupplier = () -> {
Object result;
try {
result = supplier.get();
for (SQLInterceptor interceptor : interceptors) {
result = interceptor.postHandle(this, result, requestEntity);
}
} catch (Throwable e) {
interceptors.forEach(interceptor -> interceptor.handleException(this, e, requestEntity));
throw e;
}
return (T) result;
};
return getCacheValue(this.getSql(), this.getParameters(), newSupplier);
}
}
package org.ssssssss.magicapi.modules.db;
import org.springframework.jdbc.core.RowMapper;
import org.ssssssss.magicapi.modules.db.provider.*;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
/**
* 列名转换适配器
*
* @author mxd
*/
public class ColumnMapperAdapter {
private final Map<String, RowMapper<Map<String, Object>>> columnMapRowMappers = new HashMap<>();
private final Map<String, Function<String, String>> rowMapColumnMappers = new HashMap<>();
private RowMapper<Map<String, Object>> mapRowColumnMapper;
private Function<String, String> rowMapColumnMapper;
public ColumnMapperAdapter() {
setDefault(new DefaultColumnMapperProvider());
add(new CamelColumnMapperProvider());
add(new PascalColumnMapperProvider());
add(new LowerColumnMapperProvider());
add(new UpperColumnMapperProvider());
}
public void add(ColumnMapperProvider columnMapperProvider) {
columnMapRowMappers.put(columnMapperProvider.name(), columnMapperProvider.getColumnMapRowMapper());
rowMapColumnMappers.put(columnMapperProvider.name(), columnMapperProvider.getRowMapColumnMapper());
}
public void setDefault(ColumnMapperProvider columnMapperProvider) {
this.mapRowColumnMapper = columnMapperProvider.getColumnMapRowMapper();
this.rowMapColumnMapper = columnMapperProvider.getRowMapColumnMapper();
add(columnMapperProvider);
}
public void setDefault(String name) {
this.mapRowColumnMapper = getColumnMapRowMapper(name);
this.rowMapColumnMapper = getRowMapColumnMapper(name);
}
public RowMapper<Map<String, Object>> getDefaultColumnMapRowMapper() {
return this.mapRowColumnMapper;
}
public Function<String, String> getDefaultRowMapColumnMapper() {
return this.rowMapColumnMapper;
}
public RowMapper<Map<String, Object>> getColumnMapRowMapper(String name) {
return columnMapRowMappers.getOrDefault(name, mapRowColumnMapper);
}
public Function<String, String> getRowMapColumnMapper(String name) {
return rowMapColumnMappers.getOrDefault(name, rowMapColumnMapper);
}
}
package org.ssssssss.magicapi.modules.db;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.*;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.ssssssss.magicapi.core.model.Options;
import org.ssssssss.magicapi.modules.DynamicModule;
import org.ssssssss.magicapi.modules.db.dialect.DialectAdapter;
import org.ssssssss.magicapi.datasource.model.MagicDynamicDataSource;
import org.ssssssss.magicapi.datasource.model.MagicDynamicDataSource.DataSourceNode;
import org.ssssssss.magicapi.core.annotation.MagicModule;
import org.ssssssss.magicapi.core.context.RequestContext;
import org.ssssssss.magicapi.core.context.RequestEntity;
import org.ssssssss.magicapi.modules.db.cache.SqlCache;
import org.ssssssss.magicapi.modules.db.dialect.Dialect;
import org.ssssssss.magicapi.modules.db.inteceptor.NamedTableInterceptor;
import org.ssssssss.magicapi.modules.db.inteceptor.SQLInterceptor;
import org.ssssssss.magicapi.modules.db.model.Page;
import org.ssssssss.magicapi.modules.db.provider.PageProvider;
import org.ssssssss.magicapi.modules.db.table.NamedTable;
import org.ssssssss.magicapi.core.interceptor.ResultProvider;
import org.ssssssss.magicapi.utils.ScriptManager;
import org.ssssssss.script.MagicScriptContext;
import org.ssssssss.script.annotation.Comment;
import org.ssssssss.script.functions.DynamicAttribute;
import org.ssssssss.script.parsing.ast.statement.ClassConverter;
import org.ssssssss.script.reflection.JavaReflection;
import org.ssssssss.script.runtime.RuntimeContext;
import java.beans.Transient;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 数据库查询模块
*
* @author mxd
*/
@MagicModule("db")
public class SQLModule implements DynamicAttribute<SQLModule, SQLModule>, DynamicModule<SQLModule> {
static {
try {
Field[] fields = Types.class.getFields();
Map<String, Integer> mappings = Stream.of(fields)
.collect(Collectors.toMap(field -> field.getName().toLowerCase(), field -> (Integer) JavaReflection.getFieldValue(Types.class, field)));
ClassConverter.register("sql", (value, params) -> {
if (params == null || params.length == 0) {
return value;
}
if (params[0] instanceof Number) {
return new SqlParameterValue(((Number) params[0]).intValue(), value);
}
String target = Objects.toString(params[0], null);
if (StringUtils.isBlank(target)) {
return value;
}
Integer sqlType = mappings.get(target.toLowerCase());
return sqlType == null ? value : new SqlParameterValue(sqlType, target, value);
});
} catch (Exception ignored) {
}
}
private MagicDynamicDataSource dynamicDataSource;
private DataSourceNode dataSourceNode;
private PageProvider pageProvider;
private ResultProvider resultProvider;
private ColumnMapperAdapter columnMapperAdapter;
private DialectAdapter dialectAdapter;
private RowMapper<Map<String, Object>> columnMapRowMapper;
private Function<String, String> rowMapColumnMapper;
private SqlCache sqlCache;
private String cacheName;
private List<SQLInterceptor> sqlInterceptors;
private List<NamedTableInterceptor> namedTableInterceptors;
private long ttl;
private String logicDeleteColumn;
private String logicDeleteValue;
public SQLModule() {
}
public SQLModule(MagicDynamicDataSource dynamicDataSource) {
this.dynamicDataSource = dynamicDataSource;
}
@Transient
public void setPageProvider(PageProvider pageProvider) {
this.pageProvider = pageProvider;
}
@Transient
public void setResultProvider(ResultProvider resultProvider) {
this.resultProvider = resultProvider;
}
@Transient
public void setColumnMapperProvider(ColumnMapperAdapter columnMapperAdapter) {
this.columnMapperAdapter = columnMapperAdapter;
}
@Transient
public void setDialectAdapter(DialectAdapter dialectAdapter) {
this.dialectAdapter = dialectAdapter;
}
@Transient
public void setColumnMapRowMapper(RowMapper<Map<String, Object>> columnMapRowMapper) {
this.columnMapRowMapper = columnMapRowMapper;
}
@Transient
public void setRowMapColumnMapper(Function<String, String> rowMapColumnMapper) {
this.rowMapColumnMapper = rowMapColumnMapper;
}
@Transient
public void setDynamicDataSource(MagicDynamicDataSource dynamicDataSource) {
this.dynamicDataSource = dynamicDataSource;
}
@Transient
public void setSqlInterceptors(List<SQLInterceptor> sqlInterceptors) {
this.sqlInterceptors = sqlInterceptors;
}
@Transient
public void setNamedTableInterceptors(List<NamedTableInterceptor> namedTableInterceptors) {
this.namedTableInterceptors = namedTableInterceptors;
}
@Transient
public void setDataSourceNode(DataSourceNode dataSourceNode) {
this.dataSourceNode = dataSourceNode;
}
@Transient
public String getCacheName() {
return cacheName;
}
@Transient
public void setCacheName(String cacheName) {
this.cacheName = cacheName;
}
@Transient
public long getTtl() {
return ttl;
}
@Transient
public void setTtl(long ttl) {
this.ttl = ttl;
}
@Transient
public String getLogicDeleteColumn() {
return logicDeleteColumn;
}
@Transient
public void setLogicDeleteColumn(String logicDeleteColumn) {
this.logicDeleteColumn = logicDeleteColumn;
}
@Transient
public String getLogicDeleteValue() {
return logicDeleteValue;
}
@Transient
public void setLogicDeleteValue(String logicDeleteValue) {
this.logicDeleteValue = logicDeleteValue;
}
@Transient
public SqlCache getSqlCache() {
return sqlCache;
}
@Transient
public void setSqlCache(SqlCache sqlCache) {
this.sqlCache = sqlCache;
}
@Transient
public SQLModule cloneSQLModule() {
SQLModule sqlModule = new SQLModule();
sqlModule.setDynamicDataSource(this.dynamicDataSource);
sqlModule.setDataSourceNode(this.dataSourceNode);
sqlModule.setPageProvider(this.pageProvider);
sqlModule.setColumnMapperProvider(this.columnMapperAdapter);
sqlModule.setColumnMapRowMapper(this.columnMapRowMapper);
sqlModule.setRowMapColumnMapper(this.rowMapColumnMapper);
sqlModule.setSqlCache(this.sqlCache);
sqlModule.setTtl(this.ttl);
sqlModule.setCacheName(this.cacheName);
sqlModule.setResultProvider(this.resultProvider);
sqlModule.setDialectAdapter(this.dialectAdapter);
sqlModule.setSqlInterceptors(this.sqlInterceptors);
sqlModule.setLogicDeleteValue(this.logicDeleteValue);
sqlModule.setLogicDeleteColumn(this.logicDeleteColumn);
sqlModule.setNamedTableInterceptors(this.namedTableInterceptors);
return sqlModule;
}
/**
* 开启事务,在一个回调中进行操作
*
* @param function 回调函数
*/
@Comment("开启事务,并在回调中处理")
public Object transaction(@Comment(name = "function", value = "回调函数,如:()=>{....}") Function<?, ?> function) {
// 创建事务
Transaction transaction = transaction();
try {
Object val = function.apply(null);
transaction.commit(); //提交事务
return val;
} catch (Throwable throwable) {
transaction.rollback(); //回滚事务
throw throwable;
}
}
/**
* 开启事务,手动提交和回滚
*/
@Comment("开启事务,返回事务对象")
public Transaction transaction() {
return new Transaction(this.dataSourceNode.getDataSourceTransactionManager());
}
/**
* 使用缓存
*
* @param cacheName 缓存名
* @param ttl 过期时间
*/
@Comment("使用缓存")
public SQLModule cache(@Comment(name = "cacheName", value = "缓存名") String cacheName,
@Comment(name = "ttl", value = "过期时间") long ttl) {
if (cacheName == null) {
return this;
}
SQLModule sqlModule = cloneSQLModule();
sqlModule.setCacheName(cacheName);
sqlModule.setTtl(ttl);
return sqlModule;
}
/**
* 使用缓存(采用默认缓存时间)
*
* @param cacheName 缓冲名
*/
@Comment("使用缓存,过期时间采用默认配置")
public SQLModule cache(@Comment(name = "cacheName", value = "缓存名") String cacheName) {
return cache(cacheName, 0);
}
@Comment("采用驼峰列名")
public SQLModule camel() {
return columnCase("camel");
}
@Comment("采用帕斯卡列名")
public SQLModule pascal() {
return columnCase("pascal");
}
@Comment("采用全小写列名")
public SQLModule lower() {
return columnCase("lower");
}
@Comment("采用全大写列名")
public SQLModule upper() {
return columnCase("upper");
}
@Comment("列名保持原样")
public SQLModule normal() {
return columnCase("default");
}
@Comment("指定列名转换")
public SQLModule columnCase(String name) {
SQLModule sqlModule = cloneSQLModule();
sqlModule.setColumnMapRowMapper(this.columnMapperAdapter.getColumnMapRowMapper(name));
sqlModule.setRowMapColumnMapper(this.columnMapperAdapter.getRowMapColumnMapper(name));
return sqlModule;
}
/**
* 数据源切换
*/
@Override
@Transient
public SQLModule getDynamicAttribute(String key) {
SQLModule sqlModule = cloneSQLModule();
if (key == null) {
sqlModule.setDataSourceNode(dynamicDataSource.getDataSource());
} else {
sqlModule.setDataSourceNode(dynamicDataSource.getDataSource(key));
}
return sqlModule;
}
/**
* 查询List
*/
@Comment("查询SQL,返回List类型结果")
public List<Map<String, Object>> select(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml) {
return select(runtimeContext, sqlOrXml, null);
}
/**
* 查询List,并传入变量信息
*/
@Comment("查询SQL,并传入变量信息,返回List类型结果")
public List<Map<String, Object>> select(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml,
@Comment(name = "params", value = "变量信息") Map<String, Object> params) {
return select(new BoundSql(runtimeContext, sqlOrXml, params, this));
}
@Transient
public List<Map<String, Object>> select(BoundSql boundSql) {
assertDatasourceNotNull();
return boundSql.execute(this.sqlInterceptors, () -> queryForList(boundSql));
}
private List<Map<String, Object>> queryForList(BoundSql boundSql) {
List<Map<String, Object>> list = dataSourceNode.getJdbcTemplate().query(boundSql.getSql(), this.columnMapRowMapper, boundSql.getParameters());
if (boundSql.getExcludeColumns() != null) {
list.forEach(row -> boundSql.getExcludeColumns().forEach(row::remove));
}
return list;
}
private void assertDatasourceNotNull() {
if (dataSourceNode == null) {
throw new NullPointerException("当前数据源未设置");
}
}
/**
* 执行update
*/
@Comment("执行update操作,返回受影响行数")
public int update(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml) {
return update(runtimeContext, sqlOrXml, null);
}
/**
* 执行update,并传入变量信息
*/
@Comment("执行update操作,并传入变量信息,返回受影响行数")
public int update(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml,
@Comment(name = "params", value = "变量信息") Map<String, Object> params) {
return update(new BoundSql(runtimeContext, sqlOrXml, params, this));
}
@Transient
public int update(BoundSql boundSql) {
assertDatasourceNotNull();
RequestEntity requestEntity = RequestContext.getRequestEntity();
sqlInterceptors.forEach(sqlInterceptor -> sqlInterceptor.preHandle(boundSql, requestEntity));
Object value = dataSourceNode.getJdbcTemplate().update(boundSql.getSql(), boundSql.getParameters());
deleteCache(this.cacheName);
for (SQLInterceptor sqlInterceptor : sqlInterceptors) {
value = sqlInterceptor.postHandle(boundSql, value, requestEntity);
}
return (int) value;
}
/**
* 插入并返回主键
*/
@Comment("执行insert操作,返回插入主键")
public Object insert(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml) {
return insert(runtimeContext, sqlOrXml, null, null);
}
/**
* 插入并返回主键,并传入变量信息
*/
@Comment("执行insert操作,并传入变量信息,返回插入主键")
public Object insert(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml,
@Comment(name = "params", value = "变量信息") Map<String, Object> params) {
return insert(runtimeContext, sqlOrXml, null, params);
}
/**
* 插入并返回主键
*/
@Comment("执行insert操作,返回插入主键")
public Object insert(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml,
@Comment(name = "primary", value = "主键列") String primary) {
return insert(runtimeContext, sqlOrXml, primary, null);
}
/**
* 插入并返回主键
*/
@Comment("执行insert操作,并传入主键和变量信息,返回插入主键")
public Object insert(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml,
@Comment(name = "primary", value = "主键列") String primary,
@Comment(name = "params", value = "变量信息") Map<String, Object> params) {
return insert(new BoundSql(runtimeContext, sqlOrXml, params, this), primary);
}
void insert(BoundSql boundSql, MagicKeyHolder keyHolder) {
assertDatasourceNotNull();
dataSourceNode.getJdbcTemplate().update(con -> {
PreparedStatement ps = keyHolder.createPrepareStatement(con, boundSql.getSql());
new ArgumentPreparedStatementSetter(boundSql.getParameters()).setValues(ps);
return ps;
}, keyHolder);
deleteCache(this.cacheName);
}
/**
* 插入并返回主键
*/
@Comment("批量执行操作,返回受影响的行数")
public int batchUpdate(String sql, List<Object[]> args) {
assertDatasourceNotNull();
int[] values = dataSourceNode.getJdbcTemplate().batchUpdate(sql, args);
deleteCache(this.cacheName);
return Arrays.stream(values).sum();
}
@Transient
public JdbcTemplate getJdbcTemplate() {
assertDatasourceNotNull();
return dataSourceNode.getJdbcTemplate();
}
@Comment("删除`SQL`缓存")
public SQLModule deleteCache(@Comment("缓存名称") String name) {
if (StringUtils.isNotBlank(name)) {
sqlCache.delete(name);
}
return this;
}
/**
* 插入并返回主键
*/
@Comment("批量执行操作,返回受影响的行数")
public int batchUpdate(String sql, int batchSize, List<Object[]> args) {
assertDatasourceNotNull();
int[][] values = dataSourceNode.getJdbcTemplate().batchUpdate(sql, args, batchSize, (ps, arguments) -> {
int colIndex = 1;
for (Object value : arguments) {
if (value instanceof SqlParameterValue) {
SqlParameterValue paramValue = (SqlParameterValue) value;
StatementCreatorUtils.setParameterValue(ps, colIndex++, paramValue, paramValue.getValue());
} else {
StatementCreatorUtils.setParameterValue(ps, colIndex++, StatementCreatorUtils.javaTypeToSqlParameterType(value == null ? null : value.getClass()), value);
}
}
});
deleteCache(this.cacheName);
int count = 0;
for (int[] value : values) {
count += Arrays.stream(value).sum();
}
return count;
}
/**
* 插入并返回主键
*/
@Comment("批量执行操作,返回受影响的行数")
public int batchUpdate(@Comment(name = "sqls", value = "`SQL`语句") List<String> sqls) {
assertDatasourceNotNull();
int[] values = dataSourceNode.getJdbcTemplate().batchUpdate(sqls.toArray(new String[0]));
deleteCache(this.cacheName);
return Arrays.stream(values).sum();
}
@Transient
public Object insert(BoundSql boundSql, String primary) {
MagicKeyHolder keyHolder = new MagicKeyHolder(primary);
RequestEntity requestEntity = RequestContext.getRequestEntity();
sqlInterceptors.forEach(sqlInterceptor -> sqlInterceptor.preHandle(boundSql, requestEntity));
insert(boundSql, keyHolder);
Object value = keyHolder.getObjectKey();
for (SQLInterceptor sqlInterceptor : sqlInterceptors) {
value = sqlInterceptor.postHandle(boundSql, value, requestEntity);
}
return value;
}
/**
* 分页查询
*/
@Comment("执行分页查询,分页条件自动获取")
public Object page(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml,
@Comment(name = "params", value = "变量信息") Map<String, Object> params) {
return page(new BoundSql(runtimeContext, sqlOrXml, params, this));
}
/**
* 分页查询,并传入变量信息
*/
@Comment("执行分页查询,并传入变量信息,分页条件自动获取")
public Object page(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml) {
return page(runtimeContext, sqlOrXml, (Map<String, Object>) null);
}
/**
* 分页查询(手动传入limit和offset参数)
*/
@Comment("执行分页查询,分页条件手动传入")
public Object page(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml,
@Comment(name = "limit", value = "限制条数") long limit,
@Comment(name = "offset", value = "跳过条数") long offset) {
return page(runtimeContext, sqlOrXml, limit, offset, null);
}
/**
* 分页查询(手动传入limit和offset参数)
*/
@Comment("执行分页查询,并传入变量信息,分页条件手动传入")
public Object page(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml,
@Comment(name = "limit", value = "限制条数") long limit,
@Comment(name = "offset", value = "跳过条数") long offset,
@Comment(name = "params", value = "变量信息") Map<String, Object> params) {
BoundSql boundSql = new BoundSql(runtimeContext, sqlOrXml, params, this);
return page(boundSql, new Page(limit, offset));
}
@Transient
public Object page(BoundSql boundSql) {
Page page = pageProvider.getPage(boundSql.getRuntimeContext());
return page(boundSql, page);
}
@Transient
public String getDataSourceName() {
return this.dataSourceNode == null ? "unknown" : dataSourceNode.getName();
}
/**
* 分页查询(手动传入分页SQL语句)
*/
@Comment("执行分页查询,分页`SQL`语句手动传入")
public Object page(RuntimeContext runtimeContext,
@Comment(name = "countSqlOrXml", value = "count语句") String countSqlOrXml,
@Comment(name = "sqlOrXml", value = "查询语句") String sqlOrXml) {
return page(runtimeContext, countSqlOrXml, sqlOrXml, null);
}
/**
* 分页查询(手动传入分页SQL语句)
*/
@Comment("执行分页查询,并传入变量信息,分页`SQL`countSqlOrXml")
public Object page(RuntimeContext runtimeContext,
@Comment(name = "countSqlOrXml", value = "count语句") String countSqlOrXml,
@Comment(name = "sqlOrXml", value = "查询语句") String sqlOrXml,
@Comment(name = "params", value = "变量信息") Map<String, Object> params) {
int count = selectInt(new BoundSql(runtimeContext, countSqlOrXml, params, this));
Page page = pageProvider.getPage(runtimeContext);
BoundSql boundSql = new BoundSql(runtimeContext, sqlOrXml, params, this);
return page(count, boundSql, page, null);
}
/**
* 分页查询(手动传入count)
*/
@Comment("执行分页查询,并传入变量信息,分页`SQL`count")
public Object page(RuntimeContext runtimeContext,
@Comment(name = "count", value = "总条数") int count,
@Comment(name = "sqlOrXml", value = "查询语句") String sqlOrXml,
@Comment(name = "limit", value = "限制条数") long limit,
@Comment(name = "offset", value = "跳过条数") long offset,
@Comment(name = "params", value = "变量信息") Map<String, Object> params) {
BoundSql boundSql = new BoundSql(runtimeContext, sqlOrXml, params, this);
return page(count, boundSql, new Page(limit, offset), null);
}
private Object page(int count, BoundSql boundSql, Page page, Dialect dialect) {
List<Map<String, Object>> list = null;
if (count > 0) {
if (dialect == null) {
dialect = dataSourceNode.getDialect(dialectAdapter);
}
BoundSql pageBoundSql = buildPageBoundSql(dialect, boundSql, page.getOffset(), page.getLimit());
list = pageBoundSql.execute(this.sqlInterceptors, () -> queryForList(pageBoundSql));
}
RequestEntity requestEntity = RequestContext.getRequestEntity();
return resultProvider.buildPageResult(requestEntity, page, count, list);
}
@Transient
public Object page(BoundSql boundSql, Page page) {
assertDatasourceNotNull();
Dialect dialect = dataSourceNode.getDialect(dialectAdapter);
BoundSql countBoundSql = boundSql.copy(dialect.getCountSql(boundSql.getSql()));
int count = selectInt(countBoundSql);
return page(count, boundSql, page, dialect);
}
/**
* 查询总条目数
*/
@Comment("查询总条目数")
public Integer count(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml) {
return count(runtimeContext, sqlOrXml, null);
}
/**
* 查询总条目数
*/
@Comment("查询总条目数,并传入变量信息")
public Integer count(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml,
@Comment(name = "params", value = "变量信息") Map<String, Object> params) {
BoundSql boundSql = new BoundSql(runtimeContext, sqlOrXml, params, this);
Dialect dialect = dataSourceNode.getDialect(dialectAdapter);
BoundSql countBoundSql = boundSql.copy(dialect.getCountSql(boundSql.getSql()));
return selectInt(countBoundSql);
}
/**
* 查询int值
*/
@Comment("查询int值,适合单行单列int的结果")
public Integer selectInt(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml) {
return selectInt(runtimeContext, sqlOrXml, null);
}
/**
* 查询int值
*/
@Comment("查询int值,并传入变量信息,适合单行单列int的结果")
public Integer selectInt(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml,
@Comment(name = "params", value = "变量信息") Map<String, Object> params) {
return selectInt(new BoundSql(runtimeContext, sqlOrXml, params, this));
}
@Transient
public Integer selectInt(BoundSql boundSql) {
assertDatasourceNotNull();
return boundSql.execute(this.sqlInterceptors, () -> dataSourceNode.getJdbcTemplate().query(boundSql.getSql(), new SingleRowResultSetExtractor<>(Integer.class), boundSql.getParameters()));
}
/**
* 查询Map
*/
@Comment("查询单条结果,查不到返回null")
public Map<String, Object> selectOne(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml) {
return selectOne(runtimeContext, sqlOrXml, null);
}
/**
* 查询Map,并传入变量信息
*/
@Comment("查询单条结果,并传入变量信息,查不到返回null")
public Map<String, Object> selectOne(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml,
@Comment(name = "params", value = "变量信息") Map<String, Object> params) {
return selectOne(new BoundSql(runtimeContext, sqlOrXml, params, this));
}
@Transient
public Map<String, Object> selectOne(BoundSql boundSql) {
assertDatasourceNotNull();
return boundSql.execute(this.sqlInterceptors, () -> {
Map<String, Object> row = dataSourceNode.getJdbcTemplate().query(boundSql.getSql(), new SingleRowResultSetExtractor<>(this.columnMapRowMapper), boundSql.getParameters());
if (row != null && boundSql.getExcludeColumns() != null) {
boundSql.getExcludeColumns().forEach(row::remove);
}
return row;
});
}
/**
* 查询单行单列的值
*/
@Comment("查询单行单列的值")
public Object selectValue(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml) {
return selectValue(runtimeContext, sqlOrXml, null);
}
/**
* 查询单行单列的值,并传入变量信息
*/
@Comment("查询单行单列的值,并传入变量信息")
public Object selectValue(RuntimeContext runtimeContext,
@Comment(name = "sqlOrXml", value = "`SQL`语句或`xml`") String sqlOrXml,
@Comment(name = "params", value = "变量信息") Map<String, Object> params) {
assertDatasourceNotNull();
BoundSql boundSql = new BoundSql(runtimeContext, sqlOrXml, params, this);
return boundSql.execute(this.sqlInterceptors, () -> dataSourceNode.getJdbcTemplate().query(boundSql.getSql(), new SingleRowResultSetExtractor<>(Object.class), boundSql.getParameters()));
}
@Comment("指定table,进行单表操作")
public NamedTable table(@Comment(name = "tableName", value = "表名") String tableName) {
return new NamedTable(tableName, this, rowMapColumnMapper, namedTableInterceptors);
}
private BoundSql buildPageBoundSql(Dialect dialect, BoundSql boundSql, long offset, long limit) {
String pageSql = dialect.getPageSql(boundSql.getSql(), boundSql, offset, limit);
return boundSql.copy(pageSql);
}
@Transient
@Override
public SQLModule getDynamicModule(MagicScriptContext context) {
String dataSourceKey = context.getString(Options.DEFAULT_DATA_SOURCE.getValue());
if (StringUtils.isEmpty(dataSourceKey)) return this;
SQLModule newSqlModule = cloneSQLModule();
newSqlModule.setDataSourceNode(dynamicDataSource.getDataSource(dataSourceKey));
return newSqlModule;
}
static class MagicKeyHolder extends GeneratedKeyHolder {
private final boolean useGeneratedKeys;
private final String primary;
public MagicKeyHolder() {
this(null);
}
public MagicKeyHolder(String primary) {
this.primary = primary;
this.useGeneratedKeys = StringUtils.isBlank(primary);
}
PreparedStatement createPrepareStatement(Connection connection, String sql) throws SQLException {
if (useGeneratedKeys) {
return connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
}
return connection.prepareStatement(sql, new String[]{primary});
}
public Object getObjectKey() {
List<Map<String, Object>> keyList = getKeyList();
if (keyList.isEmpty()) {
return null;
}
Iterator<Object> keyIterator = keyList.get(0).values().iterator();
Object key = keyIterator.hasNext() ? keyIterator.next() : null;
if (key != null && "oracle.sql.ROWID".equals(key.getClass().getName())) {
return ScriptManager.executeExpression("row.stringValue()", Collections.singletonMap("row", key));
}
return key;
}
}
}
package org.ssssssss.magicapi.modules.db;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.JdbcUtils;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 单行结果抽取
*
* @author mxd
*/
public class SingleRowResultSetExtractor<T> implements ResultSetExtractor<T> {
private final boolean singleColumn;
private final RowMapper<T> mapper;
private final Class<T> requiredType;
public SingleRowResultSetExtractor(RowMapper<T> mapper) {
this(mapper, null, false);
}
public SingleRowResultSetExtractor(Class<T> requiredType) {
this(null, requiredType, true);
}
private SingleRowResultSetExtractor(RowMapper<T> mapper, Class<T> requiredType, boolean singleColumn) {
this.mapper = mapper;
this.requiredType = requiredType;
this.singleColumn = singleColumn;
}
@Override
@SuppressWarnings("unchecked")
public T extractData(ResultSet rs) throws SQLException, DataAccessException {
if (rs.next()) {
if (singleColumn) {
return (T) JdbcUtils.getResultSetValue(rs, 1, requiredType);
}
return mapper.mapRow(rs, 0);
}
return null;
}
}
package org.ssssssss.magicapi.modules.db;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.ssssssss.script.annotation.Comment;
/**
* 事务模块
*
* @author mxd
*/
public class Transaction {
private static final TransactionDefinition TRANSACTION_DEFINITION = new DefaultTransactionDefinition();
private final DataSourceTransactionManager dataSourceTransactionManager;
private final TransactionStatus transactionStatus;
public Transaction(DataSourceTransactionManager dataSourceTransactionManager) {
this.dataSourceTransactionManager = dataSourceTransactionManager;
this.transactionStatus = dataSourceTransactionManager.getTransaction(TRANSACTION_DEFINITION);
}
/**
* 回滚事务
*/
@Comment("回滚事务")
public void rollback() {
this.dataSourceTransactionManager.rollback(this.transactionStatus);
}
/**
* 提交事务
*/
@Comment("提交事务")
public void commit() {
this.dataSourceTransactionManager.commit(this.transactionStatus);
}
}
package org.ssssssss.magicapi.modules.db.cache;
/**
* 默认SQL缓存实现
*
* @author mxd
*/
public class DefaultSqlCache implements SqlCache {
private final LRUCache cache;
public DefaultSqlCache(int capacity, long expire) {
this.cache = new LRUCache(capacity, expire);
}
@Override
public void put(String name, String key, Object value) {
cache.put(name, key, value);
}
@Override
public void put(String name, String key, Object value, long ttl) {
cache.put(name, key, value, ttl);
}
@Override
public Object get(String name, String key) {
return cache.get(name, key);
}
@Override
public void delete(String name) {
cache.delete(name);
}
}
package org.ssssssss.magicapi.modules.db.cache;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* LRU缓存实现
*
* @author mxd
*/
public class LRUCache extends LinkedHashMap<String, LRUCache.ExpireNode<Object>> {
private final String separator = ":";
private final int capacity;
private final long expire;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public LRUCache(int capacity, long expire) {
super((int) Math.ceil(capacity / 0.75) + 1, 0.75f, true);
// 容量
this.capacity = capacity;
// 固定过期时间
this.expire = expire;
}
public void put(String name, String key, Object value) {
// 封装成过期时间节点
put(name, key, value, this.expire);
}
public void put(String name, String key, Object value, long ttl) {
long expireTime = ttl > 0 ? (System.currentTimeMillis() + ttl) : (this.expire > -1 ? System.currentTimeMillis() + this.expire : Long.MAX_VALUE);
lock.writeLock().lock();
try {
// 封装成过期时间节点
put(name + separator + key, new ExpireNode<>(expireTime, value));
} finally {
lock.writeLock().unlock();
}
}
public Object get(String name, String key) {
key = name + separator + key;
lock.readLock().lock();
ExpireNode<Object> expireNode;
try {
expireNode = super.get(key);
} finally {
lock.readLock().unlock();
}
if (expireNode == null) {
return null;
}
// 惰性删除过期的
// if (this.expire > -1L && expireNode.expire < System.currentTimeMillis()) {
if (expireNode.expire < System.currentTimeMillis()) {
try {
lock.writeLock().lock();
super.remove(key);
} finally {
lock.writeLock().unlock();
}
return null;
}
return expireNode.value;
}
public void delete(String name) {
try {
lock.writeLock().lock();
Iterator<Map.Entry<String, ExpireNode<Object>>> iterator = super.entrySet().iterator();
String prefix = name + separator;
// 清除所有key前缀为name + separator的缓存
while (iterator.hasNext()) {
Map.Entry<String, ExpireNode<Object>> entry = iterator.next();
if (entry.getKey().startsWith(prefix)) {
iterator.remove();
}
}
} finally {
lock.writeLock().unlock();
}
}
@Override
protected boolean removeEldestEntry(Map.Entry<String, ExpireNode<Object>> eldest) {
if (this.expire > -1L && size() > capacity) {
clean();
}
// lru淘汰
return size() > this.capacity;
}
/**
* 清理已过期的数据
*/
private void clean() {
try {
lock.writeLock().lock();
Iterator<Map.Entry<String, ExpireNode<Object>>> iterator = super.entrySet().iterator();
long now = System.currentTimeMillis();
while (iterator.hasNext()) {
Map.Entry<String, ExpireNode<Object>> next = iterator.next();
// 判断是否过期
if (next.getValue().expire < now) {
iterator.remove();
}
}
} finally {
lock.writeLock().unlock();
}
}
/**
* 过期时间节点
*/
static class ExpireNode<V> {
long expire;
V value;
ExpireNode(long expire, V value) {
this.expire = expire;
this.value = value;
}
}
}
package org.ssssssss.magicapi.modules.db.cache;
import org.ssssssss.magicapi.utils.MD5Utils;
import java.util.Arrays;
/**
* SQL缓存接口
*
* @author mxd
*/
public interface SqlCache {
/**
* 计算key
*/
default String buildSqlCacheKey(String sql, Object[] params) {
return MD5Utils.encrypt(sql + ":" + Arrays.toString(params));
}
/**
* 存入缓存
*
* @param name 名字
* @param key key
* @param value 值
*/
void put(String name, String key, Object value);
/**
* 存入缓存
*
* @param name 名字
* @param key key
* @param value 值
* @param ttl 有效期
*/
void put(String name, String key, Object value, long ttl);
/**
* 获取缓存
*
* @param name 名字
* @param key key
*/
<T> T get(String name, String key);
/**
* 删除缓存
*
* @param name 名字
*/
void delete(String name);
}
package org.ssssssss.magicapi.modules.db.dialect;
/**
* ClickHouse方言
*
* @author mxd
*/
public class ClickhouseDialect extends MySQLDialect {
@Override
public boolean match(String jdbcUrl) {
return jdbcUrl.contains(":clickhouse:");
}
}
package org.ssssssss.magicapi.modules.db.dialect;
import org.ssssssss.magicapi.modules.db.BoundSql;
/**
* DB2方言
*
* @author mxd
*/
public class DB2Dialect implements Dialect {
@Override
public boolean match(String jdbcUrl) {
return jdbcUrl.contains(":db2:");
}
@Override
public String getPageSql(String sql, BoundSql boundSql, long offset, long limit) {
boundSql.addParameter(offset + 1);
boundSql.addParameter(offset + limit);
return "SELECT * FROM (SELECT TMP_PAGE.*,ROWNUMBER() OVER() AS ROW_ID FROM ( \n" + sql +
"\n ) AS TMP_PAGE) TMP_PAGE WHERE ROW_ID BETWEEN ? AND ?";
}
}
package org.ssssssss.magicapi.modules.db.dialect;
import org.ssssssss.magicapi.modules.db.BoundSql;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.regex.Pattern;
/**
* 数据库方言接口
*
* @author mxd
*/
public interface Dialect {
Pattern REPLACE_ORDER_BY = Pattern.compile("order\\s+by\\s+[^,\\s]+(\\s+asc|\\s+desc)?(\\s*,\\s*[^,\\s]+(\\s+asc|\\s+desc)?)*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
/**
* 根据jdbcUrl匹配
*
* @param jdbcUrl jdbc链接
* @return 是否匹配
*/
default boolean match(String jdbcUrl) {
return false;
}
/**
* 根据Connection匹配
*
* @param connection jdbc连接
* @return 是否匹配
* @throws SQLException 匹配失败时抛出的异常
*/
default boolean match(Connection connection) throws SQLException {
return match(connection.getMetaData().getURL());
}
/**
* 获取查总数的sql
*
* @param sql 原始SQL
* @return 分页 count SQL
*/
default String getCountSql(String sql) {
return "select count(1) from ( \n" + REPLACE_ORDER_BY.matcher(sql).replaceAll("") + "\n ) count_";
}
/**
* 获取分页sql
*
* @param sql 原始SQL
* @param boundSql boundSql对象
* @param offset 跳过条数
* @param limit 限制条数
* @return 返回分页SQL
*/
String getPageSql(String sql, BoundSql boundSql, long offset, long limit);
}
package org.ssssssss.magicapi.modules.db.dialect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* 方言适配器
*
* @author mxd
*/
public class DialectAdapter {
private static final Logger logger = LoggerFactory.getLogger(DialectAdapter.class);
private final List<Dialect> dialectList = new ArrayList<>();
public DialectAdapter() {
add(new MySQLDialect());
add(new OracleDialect());
add(new PostgreSQLDialect());
add(new ClickhouseDialect());
add(new DB2Dialect());
add(new SQLServerDialect());
add(new SQLServer2005Dialect());
add(new DmDialect());
add(new KingbaseSQLDialect());
}
public void add(Dialect dialect) {
this.dialectList.add(0, dialect);
}
public Dialect getDialectFromConnection(Connection connection) {
for (Dialect dialect : dialectList) {
try {
if (dialect.match(connection)) {
return dialect;
}
} catch (SQLException e) {
logger.debug("方言{}匹配失败", dialect, e);
}
}
logger.warn("magic-api在{}中无法获取dialect", connection);
return null;
}
}
package org.ssssssss.magicapi.modules.db.dialect;
import org.ssssssss.magicapi.modules.db.BoundSql;
/**
* @description: 达梦数据库方言
* @author: qijiantuoluowang
* @create: 2020-12-09 19:33
**/
public class DmDialect implements Dialect {
@Override
public boolean match(String jdbcUrl) {
return jdbcUrl.contains(":dm:");
}
@Override
public String getPageSql(String sql, BoundSql boundSql, long offset, long limit) {
limit = (offset >= 1) ? (offset + limit) : limit;
boundSql.addParameter(limit);
boundSql.addParameter(offset);
return "SELECT * FROM ( SELECT TMP.*, ROWNUM ROW_ID FROM ( \n" +
sql + "\n ) TMP WHERE ROWNUM <= ? ) WHERE ROW_ID > ?";
}
}
package org.ssssssss.magicapi.modules.db.dialect;
import org.ssssssss.magicapi.modules.db.BoundSql;
/**
* 人大金仓kingbase方言
*
* @author is_lixy@163.com
*/
public class KingbaseSQLDialect implements Dialect {
@Override
public boolean match(String jdbcUrl) {
return jdbcUrl.contains(":kingbase8:");
}
@Override
public String getPageSql(String sql, BoundSql boundSql, long offset, long limit) {
boundSql.addParameter(limit);
boundSql.addParameter(offset);
return sql + "\n limit ? offset ?";
}
}
package org.ssssssss.magicapi.modules.db.dialect;
import org.ssssssss.magicapi.modules.db.BoundSql;
/**
* mysql 方言
*
* @author mxd
*/
public class MySQLDialect implements Dialect {
@Override
public boolean match(String jdbcUrl) {
return jdbcUrl.contains(":mysql:") || jdbcUrl.contains(":mariadb:") || jdbcUrl.contains(":cobar:");
}
@Override
public String getPageSql(String sql, BoundSql boundSql, long offset, long limit) {
boundSql.addParameter(offset);
boundSql.addParameter(limit);
return sql + "\n limit ?,?";
}
}
package org.ssssssss.magicapi.modules.db.dialect;
import org.ssssssss.magicapi.modules.db.BoundSql;
/**
* Oracle方言
*
* @author mxd
*/
public class OracleDialect implements Dialect {
@Override
public boolean match(String jdbcUrl) {
return jdbcUrl.contains(":oracle:");
}
@Override
public String getPageSql(String sql, BoundSql boundSql, long offset, long limit) {
limit = (offset >= 1) ? (offset + limit) : limit;
boundSql.addParameter(limit);
boundSql.addParameter(offset);
return "SELECT * FROM ( SELECT TMP.*, ROWNUM ROW_ID FROM ( \n" +
sql + "\n ) TMP WHERE ROWNUM <= ? ) WHERE ROW_ID > ?";
}
}
package org.ssssssss.magicapi.modules.db.dialect;
import org.ssssssss.magicapi.modules.db.BoundSql;
/**
* PostgreSQL 方言
*
* @author mxd
*/
public class PostgreSQLDialect implements Dialect {
@Override
public boolean match(String jdbcUrl) {
return jdbcUrl.contains(":postgresql:") || jdbcUrl.contains(":greenplum:");
}
@Override
public String getPageSql(String sql, BoundSql boundSql, long offset, long limit) {
boundSql.addParameter(limit);
boundSql.addParameter(offset);
return sql + "\n limit ? offset ?";
}
}
package org.ssssssss.magicapi.modules.db.dialect;
import org.apache.commons.lang3.StringUtils;
import org.ssssssss.magicapi.modules.db.BoundSql;
/**
* SQLServer2005 方言
*
* @author mxd
*/
public class SQLServer2005Dialect implements Dialect {
@Override
public boolean match(String jdbcUrl) {
return jdbcUrl.contains(":sqlserver:");
}
@Override
public String getPageSql(String sql, BoundSql boundSql, long offset, long limit) {
StringBuilder pagingBuilder = new StringBuilder();
String orderby = getOrderByPart(sql);
String distinctStr = "";
String loweredString = sql.toLowerCase();
String sqlPartString = sql;
if (loweredString.trim().startsWith("select")) {
int index = 6;
if (loweredString.startsWith("select distinct")) {
distinctStr = "DISTINCT ";
index = 15;
}
sqlPartString = sqlPartString.substring(index);
}
pagingBuilder.append(sqlPartString);
// if no ORDER BY is specified use fake ORDER BY field to avoid errors
if (StringUtils.isEmpty(orderby)) {
orderby = "ORDER BY CURRENT_TIMESTAMP";
}
StringBuilder result = new StringBuilder();
result.append("WITH query AS (SELECT ")
.append(distinctStr)
.append("TOP 100 PERCENT ")
.append(" ROW_NUMBER() OVER (")
.append(orderby)
.append(") as __row_number__, ")
.append(pagingBuilder)
.append(") SELECT * FROM query WHERE __row_number__ BETWEEN ? AND ?")
.append(" ORDER BY __row_number__");
boundSql.addParameter(offset + 1);
boundSql.addParameter(offset + limit);
return result.toString();
}
private String getOrderByPart(String sql) {
String loweredString = sql.toLowerCase();
int orderByIndex = loweredString.indexOf("order by");
if (orderByIndex != -1) {
// if we find a new "order by" then we need to ignore
// the previous one since it was probably used for a subquery
return sql.substring(orderByIndex);
} else {
return "";
}
}
}
package org.ssssssss.magicapi.modules.db.dialect;
import org.ssssssss.magicapi.modules.db.BoundSql;
/**
* SQL Server 方言
*
* @author mxd
*/
public class SQLServerDialect implements Dialect {
@Override
public boolean match(String jdbcUrl) {
return jdbcUrl.contains(":sqlserver2012:");
}
@Override
public String getPageSql(String sql, BoundSql boundSql, long offset, long limit) {
boundSql.addParameter(offset);
boundSql.addParameter(limit);
return sql + "\n OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
}
}
package org.ssssssss.magicapi.modules.db.inteceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.ssssssss.magicapi.core.context.RequestEntity;
import org.ssssssss.magicapi.modules.db.BoundSql;
import org.ssssssss.magicapi.modules.db.inteceptor.SQLInterceptor;
import java.util.Arrays;
import java.util.stream.Collectors;
/**
* 默认打印SQL实现
*
* @author mxd
*/
public class DefaultSqlInterceptor implements SQLInterceptor {
private void handleLog(BoundSql boundSql, RequestEntity requestEntity){
Logger logger = LoggerFactory.getLogger(requestEntity == null ? "Unknown" : requestEntity.getMagicScriptContext().getScriptName());
String parameters = Arrays.stream(boundSql.getParameters()).map(it -> {
if (it == null) {
return "null";
}
return it + "(" + it.getClass().getSimpleName() + ")";
}).collect(Collectors.joining(", "));
String dataSourceName = boundSql.getSqlModule().getDataSourceName();
logger.info("执行SQL:{}", boundSql.getSql().trim());
if (dataSourceName != null) {
logger.info("数据源:{}", dataSourceName);
}
if (parameters.length() > 0) {
logger.info("SQL参数:{}", parameters);
}
}
@Override
public Object postHandle(BoundSql boundSql, Object result, RequestEntity requestEntity) {
handleLog(boundSql, requestEntity);
return result;
}
@Override
public void handleException(BoundSql boundSql, Throwable throwable, RequestEntity requestEntity) {
handleLog(boundSql, requestEntity);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment