package jef.database.wrapper.executor; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Arrays; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import javax.persistence.PersistenceException; import jef.common.log.LogUtil; import jef.database.DbUtils; import jef.database.DebugUtil; import jef.database.ORMConfig; import jef.database.dialect.DatabaseDialect; import jef.database.innerpool.IConnection; import jef.database.innerpool.IUserManagedPool; import jef.database.jdbc.result.CloseableResultSet; import jef.database.support.SqlLog; import jef.database.wrapper.variable.BindVariableContext; import jef.tools.StringUtils; import jef.tools.ThreadUtils; /** * 在新的线程中运行若干DDL语句的执行器。 之所以要用新线程,是为了方式被JTA事务所包含。 * * 注意!一定要在finally中关闭 DDLExecutor executor=createExecutor(); try{ //work() * xxxxxxxx }finally{ executor.close(); } * * @author jiyi * */ public class ExecutorJTAImpl implements Runnable, StatementExecutor { IConnection conn; Statement st; private IUserManagedPool parent; private String dbkey; private String txId; private DatabaseDialect profile; /** * 构造并在新线程中创建连接和Statement * * @param parent * @param dbkey * @param txId */ public ExecutorJTAImpl(IUserManagedPool parent, String dbkey, String txId, DatabaseDialect dialect) { LogUtil.debug("The sqlExecutor {} was starting.",this); this.parent = parent; this.dbkey = dbkey; this.txId = txId; this.cl = new CountDownLatch(1); // 初始化检测器 this.profile = dialect; DbUtils.es.execute(this); ThreadUtils.await(cl);// 等待连接在新线程中初始化完成后,构造方法才退出。 cl = null; // 构造方法退出后,可以用isReady检测Executor是否处于可用状态 if (exception != null) { // 如果在初始化过程中失败,则构造方法直接抛出异常。 throw new PersistenceException(exception); } if (!isReady()) { throw new IllegalStateException("Not ready"); } } /** * 当前的SQL任务 */ private final BlockingQueue<String> sqltask = new ArrayBlockingQueue<String>(10); private volatile CountDownLatch cl; /** * 记录当前出现的异常 */ private SQLException exception; /** * 记录当前执行器是否关闭 */ private volatile boolean close; public boolean isReady() { return st != null; } @Override public void run() { try { if (init()) { try { execute(); } finally { DbUtils.close(st); DbUtils.closeConnection(conn); LogUtil.debug("The sqlExecutor {} was finished. connection was released.",this); } } } catch (Throwable e) { LogUtil.exception(e); } } private boolean init() { try { conn = parent.poll(); conn.setKey(dbkey); // if(!conn.get AutoCommit()){ // conn.set AutoCommit(true); // } st = conn.createStatement(); return true; } catch (SQLException e) { exception = e; DbUtils.closeConnection(conn);// If error at create statement // then close connection. conn = null; return false; } finally { if (cl != null) { cl.countDown(); } } } private void execute() { while (!close) { try { String sql = sqltask.take(); if(StringUtils.isNotEmpty(sql)){ doSql(st, txId, sql); } } catch (InterruptedException e) { LogUtil.exception(e); }finally{ if(cl!=null){ cl.countDown(); } } } } private void doSql(Statement st, String txId, String sql) { this.exception = null;// 清理掉上次的异常记录,以免本次运行误判 SqlLog log=ORMConfig.getInstance().newLogger(sql.length()+60); try { long start=System.currentTimeMillis(); log.append(sql).append(" | ",txId); st.executeUpdate(sql); log.append("\nExecuted: ",System.currentTimeMillis()-start).append("ms"); } catch (SQLException e) { DebugUtil.setSqlState(e, sql); this.exception = e; }finally{ log.output(); } } /** * 执行的DDL语句 同步方式 * * @param ddls * @throws SQLException */ public void executeSql(String... ddls) throws SQLException { if (this.cl != null) { throw new IllegalStateException("There's sql executing now..."); } this.cl = new CountDownLatch(ddls.length); for(String s:ddls){ sqltask.add(s); } boolean flag = ThreadUtils.await(cl, 60000); cl=null;//取消占用 if (!flag) { // 运行超时就抛出异常 throw new IllegalStateException("the countdownlatch not return until timeout." + Arrays.toString(ddls)); } if (exception != null) { throw exception; } } /** * 执行执行的DDL语句 同步方式 * * @param ddls * @throws SQLException */ public void executeSql(List<String> ddls) throws SQLException { executeSql(ddls.toArray(new String[ddls.size()])); } public SQLException getException() { return exception; } /** * 关闭运行器 */ public void close() { if (!sqltask.isEmpty() ) { throw new IllegalStateException("There's sql executing now..."); } this.close = true; this.sqltask.add(""); } @Override public void setQueryTimeout(int seconds) throws SQLException { st.setQueryTimeout(seconds); } @Override public ResultSet executeQuery(String sql, Object... params) throws SQLException { PreparedStatement ps = conn.prepareStatement(sql); for (int i = 0; i < params.length; i++) { ps.setObject(i + 1, params[i]); } ResultSet rs = ps.executeQuery(); return new CloseableResultSet(ps, rs); } public int executeUpdate(String sql, Object... params) throws SQLException { SqlLog sb = ORMConfig.getInstance().newLogger().append(sql).append("\t|",txId); PreparedStatement ps = null; try { ps = conn.prepareStatement(sql); if (params.length > 0) { BindVariableContext context = new BindVariableContext(ps, profile, sb); context.setVariables(Arrays.asList(params)); } int total = ps.executeUpdate(); sb.append("\nExecuted:", total).append("\t |", txId); return total; } finally { sb.output(); DbUtils.close(ps); } } }