All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gu.sql2java.TableManagers Maven / Gradle / Ivy

There is a newer version: 5.2.0
Show newest version
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); } } }