gu.sql2java.TableManagers Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sql2java-manager Show documentation
Show all versions of sql2java-manager Show documentation
sql2java manager class package for accessing database
package gu.sql2java;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.nullToEmpty;
import static gu.sql2java.SimpleLog.log;
import static com.google.common.base.MoreObjects.firstNonNull;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
/**
* {@link TableManager}工具类
* @author guyadong
* @since 3.15.4
*/
public class TableManagers {
private static final int CACHE_MAXIMUMPOOLSIZE = Runtime.getRuntime().availableProcessors();
private static final long CACHE_KEEPALIVETIME = 60L;
private static final int CACHE_QUEUECAPACITY = 1024;
private static final String CACHE_NAMEFORMAT = "sql2java-pool-%d";
private static Boolean debug;
/** 全局线程池(自动退出) */
private static class Singleton{
static final ExecutorService GLOBAL_EXECUTOR = createCachedPool();
}
/** 创建通用任务线程池对象 */
private static final ExecutorService createCachedPool(){
ExecutorService executor = MoreExecutors.getExitingExecutorService(
new ThreadPoolExecutor(
CACHE_MAXIMUMPOOLSIZE,
CACHE_MAXIMUMPOOLSIZE,
CACHE_KEEPALIVETIME,
TimeUnit.SECONDS,
new LinkedBlockingQueue(CACHE_QUEUECAPACITY),
new ThreadFactoryBuilder()
.setNameFormat(CACHE_NAMEFORMAT)
.build())
);
return executor;
}
private static Boolean isDebug() {
return null == debug ? BaseTableManager.isDebug() : debug;
}
public static void setDebug(Boolean debug) {
TableManagers.debug = debug;
}
/**
* 返回线程池对象
*/
public static ExecutorService getExecutorService(){
return Singleton.GLOBAL_EXECUTOR;
}
/**
* 基于{@link QueueProducerAction}异步执行{@link TableManager#loadByJoinWhereForAction(String, String, Object[], int[], int, int, gu.sql2java.TableManager.Action)},
* 查询的数据记录输出到阻塞队列{@link BlockingQueue}
*
* @param manager
* @param join JOIN查询参数
* @param where WHERE查询参数
* @param argList the arguments to use fill given prepared statement,may be null
* @param fieldList table of the field's associated constants
* @param numRows 每次查询返回的记录条数
* @param action
* @param stopped 停止标志,为{@code null}忽略,为{@code true}时结束循环
* @param executor 异步执行对象,为{@code null}则使用默认线程池对象{@link TableManagers.Singleton#GLOBAL_EXECUTOR}
* @see BaseTableManager#loadByJoinWhereForAction(String, String, Object[], int[], int, int, gu.sql2java.TableManager.Action)
*/
public static
void loadByJoinWhereAsync(TableManagermanager,String join,String where,Object[] argList, int[] fieldList, int numRows, TableManager.Action action, AtomicBoolean stopped, Executor executor){
checkArgument(null != manager,"manager is null");
checkArgument(null != action,"action is null");
if(0 == numRows){
return ;
}
firstNonNull(executor,Singleton.GLOBAL_EXECUTOR).execute(()->{
if(numRows < 0){
if(null != stopped && stopped.get()){
return;
}
log(isDebug(),"loadByJoinWhereAsync:join={} where={}\nargs [{}]",join,where,
null == argList? "[]":Joiner.on(",").join(Iterables.transform(Arrays.asList(argList), String::valueOf)));
manager.loadByJoinWhereForAction(join, where, argList, fieldList , 1, -1, action);
}else {
int startRow = 1;
int count = 0;
do{
if(null != stopped && stopped.get()){
break;
}
startRow += count;
log(isDebug(),"loadByJoinWhereAsync:join={} where={},numRows={}\nargs [{}]",join,where,numRows,
null == argList? "[]":Joiner.on(",").join(Iterables.transform(Arrays.asList(argList), String::valueOf)));
count = manager.loadByJoinWhereForAction(join, where, argList, fieldList, startRow, numRows, action);
}while(count == numRows);
}
});
}
/**
* 基于{@link QueueProducerAction}异步执行
* {@link TableManager#loadByJoinWhereForAction(String, String, Object[], int[], int, int, gu.sql2java.TableManager.Action)},
* 查询的数据记录输出到阻塞队列{@link BlockingQueue},
* 与{@link #loadByJoinWhereAsync(TableManager, String, String, Object[], int[], int, int, int, AtomicBoolean, Function, Executor)}不同是阻塞队列由调用层提供,
* 基于此创建{@link QueueProducerAction}实例
* @see #loadByJoinWhereAsync(TableManager, String, String, Object[], int[], int, gu.sql2java.TableManager.Action, AtomicBoolean, Executor)
* @param manager
* @param join 查询参数
* @param where 查询参数
* @param argList the arguments to use fill given prepared statement,may be null
* @param fieldList table of the field's associated constants
* @param numRows 每次查询返回的记录条数
* @param queue {@link QueueProducerAction}构造方法参数,参见 {@link QueueProducerAction#QueueProducerAction(int, int, Function)}
* @param queueTimeout {@link QueueProducerAction}构造方法参数,参见 {@link QueueProducerAction#QueueProducerAction(int, int, Function)}
* @param stopped 停止标志,为{@code null}忽略,为{@code true}时结束循环
* @param transformer {@link QueueProducerAction}构造方法参数,参见 {@link QueueProducerAction#QueueProducerAction(int, int, Function)}
* @param executor 异步执行对象,为{@code null}则使用默认线程池对象{@link TableManagers.Singleton#GLOBAL_EXECUTOR}
*/
public static
void loadByJoinWhereAsync(TableManagermanager,String join,
String where,Object[] argList,int[] fieldList,int numRows, BlockingQueuequeue, int queueTimeout, AtomicBoolean stopped, Functiontransformer, Executor executor){
QueueProducerAction action = new QueueProducerAction<>(queue, queueTimeout, transformer);
loadByJoinWhereAsync(manager,join,where,argList, fieldList, numRows, action, stopped, executor);
}
/**
* 基于{@link QueueProducerAction}异步执行{@link TableManager#loadByJoinWhereForAction(String, String, Object[], int[], int, int, gu.sql2java.TableManager.Action)},
* 查询的数据记录输出到阻塞队列{@link BlockingQueue},
* 与{@link #loadByJoinWhereAsync(TableManager, String, String, Object[], int[], int, BlockingQueue, int, AtomicBoolean, Function, Executor)}不同的是
* 阻塞队列{@link BlockingQueue}根据输入参数创建
* @see #loadByJoinWhereAsync(TableManager, String, String, Object[], int[], int, BlockingQueue, int, AtomicBoolean, Function, Executor)
* @param manager
* @param join 查询参数
* @param where 查询参数
* @param argList the arguments to use fill given prepared statement,may be null
* @param fieldList table of the field's associated constants
* @param numRows 每次查询返回的记录条数
* @param queueCapacity {@link QueueProducerAction}构造方法参数,参见 {@link QueueProducerAction#QueueProducerAction(int, int, Function)}
* @param queueTimeout {@link QueueProducerAction}构造方法参数,参见 {@link QueueProducerAction#QueueProducerAction(int, int, Function)}
* @param stopped 停止标志,为{@code null}忽略,为{@code true}时结束循环
* @param transformer {@link QueueProducerAction}构造方法参数,参见 {@link QueueProducerAction#QueueProducerAction(int, int, Function)}
* @param executor 异步执行对象,为{@code null}则使用默认线程池对象{@link TableManagers.Singleton#GLOBAL_EXECUTOR}
* @return 创建的阻塞队列
*/
public static
BlockingQueue loadByJoinWhereAsync(TableManagermanager,String join,
String where, Object[] argList,int[] fieldList,int numRows, int queueCapacity, int queueTimeout, AtomicBoolean stopped, Functiontransformer, Executor executor){
BlockingQueuequeue = new LinkedBlockingQueue(queueCapacity);
loadByJoinWhereAsync(manager,join,where,argList,fieldList,numRows, queue, queueTimeout, stopped, transformer, executor);
return queue;
}
/**
* {@link #loadByJoinWhereAsync(TableManager, String, String, Object[], int[], int, BlockingQueue, int, AtomicBoolean, Function, Executor)}的简化版,
* 返回队列类型为原始数据库对象类型
* @see #loadByJoinWhereAsync(TableManager, String, String, Object[], int[], int, BlockingQueue, int, AtomicBoolean, Function, Executor)
*/
public static
BlockingQueue loadByJoinWhereAsync(TableManagermanager,String join,
String where, Object[] argList,int numRows, int queueCapacity, int queueTimeout, AtomicBoolean stopped, Executor executor){
return loadByJoinWhereAsync(manager,join,where,argList,null,numRows, queueCapacity, queueTimeout, stopped, b->b, executor);
}
/**
* 聚合(统计)查询
* @param manager
* @param columns SELECT 语句字段输出表达式列表部分
* @param where SELECT 语句 WHERE条件e表达式部分,为{@code null}忽略
* @param groupBy 聚合查询字段名列表
* @param having HAVING 表达式,为{@code null}忽略
* @param orderBy 排序表达式(ORDER BY起始),为{@code null}忽略
* @param argList SQL语句中的参数列表
* @return 聚合查询结果
*/
public static List aggregateAsList(
TableManagermanager,
String columns,String where,String groupBy,String having,String orderBy, Object... argList){
columns = nullToEmpty(columns).trim();
where = nullToEmpty(where).trim();
groupBy = nullToEmpty(groupBy).trim();
having = nullToEmpty(having).trim();
orderBy = nullToEmpty(orderBy).trim();
checkArgument(!columns.isEmpty(),"columns is null or empty");
checkArgument(!groupBy.isEmpty(),"groupBy is null or empty");
where = Pattern.compile("^WHERE",Pattern.CASE_INSENSITIVE).matcher(where).replaceAll("");
groupBy = Pattern.compile("^GROUP +BY",Pattern.CASE_INSENSITIVE).matcher(groupBy).replaceAll("");
having = Pattern.compile("^HAVING",Pattern.CASE_INSENSITIVE).matcher(having).replaceAll("");
orderBy = Pattern.compile("^ORDER +BY",Pattern.CASE_INSENSITIVE).matcher(orderBy).replaceAll("");
String sql = String.format("SELECT %s FROM %s %s %s %s %s",
columns,
tablenameOf(manager),
where.isEmpty() ? "" : "WHERE "+ where,
"GROUP BY " + groupBy,
having.isEmpty() ? "" : "HAVING " + having,
orderBy.isEmpty() ? "" : "ORDER BY " + orderBy);
return checkNotNull(manager,"manager is null").runSqlAsList(sql,argList);
}
private static String tablenameOf(TableManager manager){
if(manager instanceof BaseTableManager){
return ((BaseTableManager)manager).metaData.tablename;
}else {
return manager.createBean().tableName();
}
}
/**
* 更新记录主键
* @param alias
* @param tablename
* @param from
* @param to
* @since 3.30.0
*/
public static void updatePk(String alias, String tablename,Object[] from,Object[] to) {
if(!Arrays.deepEquals(from, to)) {
checkArgument(Arrays.stream(checkNotNull(from,"from is null")).allMatch(e->null!=e),"argument 'from' has null element");
checkArgument(Arrays.stream(checkNotNull(to ,"to is null" )).allMatch(e->null!=e),"argument 'to' has null element");
RowMetaData metaData = RowMetaData.getMetaData(tablename);
checkArgument(metaData.primaryKeyCount==from.length,"%s array length required for 'from'",metaData.primaryKeyCount);
checkArgument(metaData.primaryKeyCount==to.length,"%s array length required for 'to'",metaData.primaryKeyCount);
String sql = String.format("UPDATE %s SET %s WHERE %s",
tablename,
Arrays.stream(metaData.primaryKeyNames).map(s->s+"=?") .reduce((l,r)->l+","+r).get(),
Arrays.stream(metaData.primaryKeyNames).map(s->s+"=?") .reduce((l,r)->l+" AND "+r).get());
Object[] argList = new Object[metaData.primaryKeyCount*2];
System.arraycopy(from, 0, argList, 0, metaData.primaryKeyCount);
System.arraycopy(to, 0, argList, metaData.primaryKeyCount, metaData.primaryKeyCount);
// log("sql={}",sql);
Managers.getSqlRunner(alias).runSql(sql,argList);
CacheManager manager = Managers.getCacheManagerOrNull(tablename);
if(null != manager) {
/** 删除对应缓存数据 */
manager.removeCached(from);
}
}
}
/**
* 更新记录主键(适用只有一个主键的表)
* @param alias
* @param tablename
* @param from
* @param to
* @since 3.30.0
*/
public static void updatePk(String alias, String tablename,Object from,Object to) {
if(!Objects.equal(from, to)) {
RowMetaData metaData = RowMetaData.getMetaData(tablename);
if(metaData.primaryKeyCount != 1) {
throw new UnsupportedOperationException("ONLY ONE primary key required for "+ tablename);
}
Managers.getSqlRunner(alias).runSql(
"UPDATE ${tablename} SET ${pk}=? WHERE ${pk}=?"
.replace("${tablename}", tablename)
.replace("${pk}", metaData.primaryKeyNames[0]),
new Object[] { checkNotNull(to,"to is null"),checkNotNull(from,"from is null")});
CacheManager manager = Managers.getCacheManagerOrNull(tablename);
if(null != manager) {
/** 删除对应缓存数据 */
manager.removeCached(from);
}
}
}
/**
* 更新记录主键(适用只有一个主键的表)
* @param alias
* @param bean
* @param to
* @since 3.30.0
*/
public static void updatePk(String alias, B bean,Object[] to) {
if(null!= bean) {
updatePk(alias,bean.fetchMetaData().tablename,bean.primaryValues(), to);
}
}
/**
* 更新记录主键(适用只有一个主键的表)
* @param alias
* @param bean
* @param to
* @since 3.30.0
*/
public static void updatePk(String alias, B bean,Object to) {
if(null!= bean) {
updatePk(alias,bean.fetchMetaData().tablename,bean.primaryValue(), to);
}
}
}