package org.ssssssss.magicapi.functions; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.datasource.DataSourceUtils; import org.ssssssss.magicapi.cache.SqlCache; import org.ssssssss.magicapi.config.DynamicDataSource; import org.ssssssss.magicapi.config.DynamicDataSource.DataSourceNode; import org.ssssssss.magicapi.config.MagicModule; import org.ssssssss.magicapi.dialect.Dialect; import org.ssssssss.magicapi.dialect.DialectUtils; import org.ssssssss.magicapi.exception.MagicAPIException; import org.ssssssss.magicapi.model.Page; import org.ssssssss.magicapi.provider.PageProvider; import org.ssssssss.magicapi.provider.ResultProvider; import org.ssssssss.script.MagicScriptContext; import org.ssssssss.script.annotation.UnableCall; import org.ssssssss.script.functions.StreamExtension; import org.ssssssss.script.parsing.GenericTokenParser; import org.ssssssss.script.parsing.Parser; import org.ssssssss.script.parsing.TokenStream; import org.ssssssss.script.parsing.Tokenizer; import java.sql.Connection; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; public class DatabaseQuery extends HashMap implements MagicModule { @UnableCall private DynamicDataSource dynamicDataSource; @UnableCall private DataSourceNode dataSourceNode; @UnableCall private PageProvider pageProvider; @UnableCall private ResultProvider resultProvider; @UnableCall private RowMapper> rowMapper; @UnableCall private SqlCache sqlCache; @UnableCall private String cacheName; @UnableCall private long ttl; public DatabaseQuery() { } public DatabaseQuery(DynamicDataSource dynamicDataSource) { this.dynamicDataSource = dynamicDataSource; this.dataSourceNode = dynamicDataSource.getDataSource(); } @UnableCall public void setPageProvider(PageProvider pageProvider) { this.pageProvider = pageProvider; } @UnableCall public void setResultProvider(ResultProvider resultProvider) { this.resultProvider = resultProvider; } @UnableCall public void setRowMapper(RowMapper> rowMapper) { this.rowMapper = rowMapper; } @UnableCall public void setDynamicDataSource(DynamicDataSource dynamicDataSource) { this.dynamicDataSource = dynamicDataSource; } @UnableCall public void setSqlCache(SqlCache sqlCache) { this.sqlCache = sqlCache; } @UnableCall public void setDataSourceNode(DataSourceNode dataSourceNode) { this.dataSourceNode = dataSourceNode; } @UnableCall public void setCacheName(String cacheName) { this.cacheName = cacheName; } @UnableCall public void setTtl(long ttl) { this.ttl = ttl; } @UnableCall public DatabaseQuery cloneQuery() { DatabaseQuery query = new DatabaseQuery(); query.setDynamicDataSource(this.dynamicDataSource); query.setDataSourceNode(this.dataSourceNode); query.setPageProvider(this.pageProvider); query.setRowMapper(this.rowMapper); query.setSqlCache(this.sqlCache); query.setTtl(this.ttl); query.setResultProvider(this.resultProvider); return query; } /** * 开启事务,在一个回调中进行操作 * @param function 回调函数 * @return */ public Object transaction(Function function) { Transaction transaction = transaction(); //创建事务 try { Object val = function.apply(null); transaction.commit(); //提交事务 return val; } catch (Throwable throwable) { transaction.rollback(); //回滚事务 throw throwable; } } /** * 开启事务,手动提交和回滚 * @return */ public Transaction transaction() { return new Transaction(this.dataSourceNode.getDataSourceTransactionManager()); } /** * 添加至缓存 * @param value 缓存名 */ @UnableCall private T putCacheValue(T value, BoundSql boundSql) { if (this.cacheName != null) { this.sqlCache.put(this.cacheName, boundSql.getCacheKey(this.sqlCache), value, this.ttl); } return value; } /** * 使用缓存 * @param cacheName 缓存名 * @param ttl 过期时间 * @return */ public DatabaseQuery cache(String cacheName, long ttl) { if (cacheName == null) { return this; } DatabaseQuery query = cloneQuery(); query.setCacheName(cacheName); query.setTtl(ttl); return query; } /** * 使用缓存(采用默认缓存时间) * @param cacheName 缓冲名 * @return */ public DatabaseQuery cache(String cacheName) { return cache(cacheName, 0); } /** * 数据源切换 */ @Override public DatabaseQuery get(Object key) { DatabaseQuery query = cloneQuery(); if (key == null) { query.setDataSourceNode(dynamicDataSource.getDataSource()); } else { query.setDataSourceNode(dynamicDataSource.getDataSource(key.toString())); } return query; } /** * 查询List */ public List> select(String sql) { BoundSql boundSql = new BoundSql(sql); return (List>) boundSql.getCacheValue(this.sqlCache, this.cacheName) .orElseGet(() -> putCacheValue(dataSourceNode.getJdbcTemplate().query(boundSql.getSql(), this.rowMapper, boundSql.getParameters()), boundSql)); } /** * 执行update */ public int update(String sql) { BoundSql boundSql = new BoundSql(sql); int value = dataSourceNode.getJdbcTemplate().update(boundSql.getSql(), boundSql.getParameters()); if (this.cacheName != null) { this.sqlCache.delete(this.cacheName); } return value; } /** * 分页查询 */ public Object page(String sql) { Page page = pageProvider.getPage(MagicScriptContext.get()); return page(sql, page.getLimit(), page.getOffset()); } /** * 分页查询(手动传入limit和offset参数) */ public Object page(String sql, long limit, long offset) { BoundSql boundSql = new BoundSql(sql); Connection connection = null; Dialect dialect; try { connection = dataSourceNode.getJdbcTemplate().getDataSource().getConnection(); dialect = DialectUtils.getDialectFromUrl(connection.getMetaData().getURL()); } catch (Exception e) { throw new MagicAPIException("自动获取数据库方言失败", e); } finally { DataSourceUtils.releaseConnection(connection, dataSourceNode.getJdbcTemplate().getDataSource()); } if (dialect == null) { throw new MagicAPIException("自动获取数据库方言失败"); } int count = (int) boundSql.getCacheValue(this.sqlCache, this.cacheName) .orElseGet(() -> putCacheValue(dataSourceNode.getJdbcTemplate().queryForObject(dialect.getCountSql(boundSql.getSql()), Integer.class, boundSql.getParameters()), boundSql)); List list = null; if (count > 0) { String pageSql = dialect.getPageSql(boundSql.getSql(), boundSql, offset, limit); list = (List) boundSql.removeCacheKey().getCacheValue(this.sqlCache, this.cacheName) .orElseGet(() -> putCacheValue(dataSourceNode.getJdbcTemplate().query(pageSql, this.rowMapper, boundSql.getParameters()), boundSql)); } return resultProvider.buildPageResult(count, list); } /** * 查询int值 */ public Integer selectInt(String sql) { BoundSql boundSql = new BoundSql(sql); return (Integer) boundSql.getCacheValue(this.sqlCache, this.cacheName) .orElseGet(() -> putCacheValue(dataSourceNode.getJdbcTemplate().queryForObject(boundSql.getSql(), boundSql.getParameters(), Integer.class), boundSql)); } /** * 查询Map */ public Map selectOne(String sql) { BoundSql boundSql = new BoundSql(sql); return (Map) boundSql.getCacheValue(this.sqlCache, this.cacheName) .orElseGet(() -> { List> list = dataSourceNode.getJdbcTemplate().query(boundSql.getSql(), this.rowMapper, boundSql.getParameters()); return list != null && list.size() > 0 ? list.get(0) : null; }); } /** * 查询单行单列的值 */ public Object selectValue(String sql) { BoundSql boundSql = new BoundSql(sql); return boundSql.getCacheValue(this.sqlCache, this.cacheName) .orElseGet(() -> putCacheValue(dataSourceNode.getJdbcTemplate().queryForObject(boundSql.getSql(), boundSql.getParameters(), Object.class), boundSql)); } private static Tokenizer tokenizer = new Tokenizer(); private static GenericTokenParser concatTokenParser = new GenericTokenParser("${", "}", false); private static GenericTokenParser replaceTokenParser = new GenericTokenParser("#{", "}", true); private static GenericTokenParser ifTokenParser = new GenericTokenParser("?{", "}", true); private static GenericTokenParser ifParamTokenParser = new GenericTokenParser("?{", ",", true); @UnableCall @Override public String getModuleName() { return "db"; } public static class BoundSql { private String sql; private List parameters = new ArrayList<>(); private String cacheKey; BoundSql(String sql) { MagicScriptContext context = MagicScriptContext.get(); // 处理?{}参数 this.sql = ifTokenParser.parse(sql.trim(), text -> { AtomicBoolean ifTrue = new AtomicBoolean(false); String val = ifParamTokenParser.parse("?{" + text, param -> { Object result = Parser.parseExpression(new TokenStream(tokenizer.tokenize(param))).evaluate(context); //如果是String则判断是否是空,否则和判断值是否为true if (result != null) { if (result instanceof String) { ifTrue.set(!result.toString().isEmpty()); } else { ifTrue.set(!Objects.equals(false, result)); } } return null; }); if (ifTrue.get()) { return val; } return ""; }); // 处理${}参数 this.sql = concatTokenParser.parse(this.sql, text -> String.valueOf(Parser.parseExpression(new TokenStream(tokenizer.tokenize(text))).evaluate(context))); // 处理#{}参数 this.sql = replaceTokenParser.parse(this.sql, text -> { Object value = Parser.parseExpression(new TokenStream(tokenizer.tokenize(text))).evaluate(context); try { //对集合自动展开 List objects = StreamExtension.arrayLikeToList(value); StringBuilder sb = new StringBuilder(); for (int i = 0, size = objects.size(); i < size; i++) { sb.append("?"); if (i + 1 < size) { sb.append(","); } parameters.add(objects.get(i)); } return sb.toString(); } catch (Exception e) { parameters.add(value); return "?"; } }); } /** * 添加SQL参数 */ public void addParameter(Object value) { parameters.add(value); } /** * 获取要执行的SQL */ public String getSql() { return sql; } /** * 获取要执行的参数 */ public Object[] getParameters() { return parameters.toArray(); } /** * 清空缓存key */ public BoundSql removeCacheKey() { this.cacheKey = null; return this; } /** * 获取缓存key */ public String getCacheKey(SqlCache sqlCache) { if (cacheKey == null) { cacheKey = sqlCache.buildSqlCacheKey(this); } return cacheKey; } /** * 获取缓存值 */ public Optional getCacheValue(SqlCache sqlCache, String cacheName) { return Optional.ofNullable(cacheName == null ? null : sqlCache.get(cacheName, getCacheKey(sqlCache))); } } }