package jef.database; import java.io.StringReader; import java.lang.reflect.Type; import java.sql.CallableStatement; 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.Map; import jef.common.log.LogUtil; import jef.common.wrapper.IntRange; import jef.database.Session.PopulateStrategy; import jef.database.Transaction.TransactionFlag; import jef.database.dialect.DatabaseDialect; import jef.database.dialect.type.AutoIncrementMapping; import jef.database.innerpool.IConnection; import jef.database.innerpool.IManagedConnectionPool; import jef.database.innerpool.PartitionSupport; import jef.database.jdbc.GenerateKeyReturnOper; import jef.database.jdbc.JDBCTarget; import jef.database.jdbc.result.IResultSet; import jef.database.jdbc.result.ResultSetContainer; import jef.database.jdbc.result.ResultSetHolder; import jef.database.jdbc.result.ResultSetImpl; import jef.database.jdbc.result.ResultSetWrapper; import jef.database.jdbc.statement.ProcessablePreparedStatement; import jef.database.jdbc.statement.ProcessableStatement; import jef.database.jdbc.statement.ResultSetLaterProcess; import jef.database.jsqlparser.SqlFunctionlocalization; import jef.database.jsqlparser.parser.ParseException; import jef.database.jsqlparser.parser.StSqlParser; import jef.database.jsqlparser.visitor.SelectItem; import jef.database.meta.DbProperty; import jef.database.meta.ITableMetadata; import jef.database.query.EntityMappingProvider; import jef.database.query.SqlExpression; import jef.database.routing.jdbc.UpdateReturn; import jef.database.routing.sql.InMemoryOperateProvider; import jef.database.support.SqlLog; import jef.database.wrapper.ResultIterator; import jef.database.wrapper.clause.BindSql; import jef.database.wrapper.populator.AbstractResultSetTransformer; import jef.database.wrapper.populator.ResultSetExtractor; import jef.database.wrapper.populator.Transformer; import jef.database.wrapper.variable.BindVariableContext; import jef.tools.MathUtils; import jef.tools.StringUtils; /** * OperateTarge描述了一个带有状态的数据库操作对象。 这一情况发生在支持路由的多数据源的场合下。 * * * @author jiyi * */ public class OperateTarget implements SqlTemplate, JDBCTarget { private Session session; private String dbkey; private DatabaseDialect profile; private IConnection conn; public String getDbkey() { return dbkey; } public Session getSession() { return session; } public Sequence getSequence(AutoIncrementMapping mapping) throws SQLException { return session.getNoTransactionSession().getSequenceManager().getSequence(mapping, this); } /** * 获得Sequence对象 * * @param name * 名称 * @param len * Sequence长度(位数) * @return sequence * @throws SQLException */ public Sequence getSequence(String name, int len) throws SQLException { return session.getNoTransactionSession().getSequenceManager().getSequence(name, this, len); } public OperateTarget(Session tx, String key) { this.session = tx; this.dbkey = key; this.profile = session.getProfile(key); } public DatabaseDialect getProfile() { return profile; } /** * 释放连接,不再持有。相当于关闭 */ public void releaseConnection() { if (conn != null) { session.releaseConnection(conn); conn = null; } } public String getTransactionId() { return this.session.getTransactionId(dbkey); } public void notifyDisconnect(SQLException e) { IConnection conn = getConnection(dbkey); if (getProfile().isIOError(e)) { LogUtil.warn("IO error on connection detected. closing current connection to refersh a new db connection."); conn.closePhysical(); if (session.getPool() instanceof IManagedConnectionPool) { ((IManagedConnectionPool) session.getPool()).notifyDbDisconnect(); } } } public Statement createStatement() throws SQLException { return profile.wrap(getConnection(dbkey).createStatement(), isJpaTx()); } public Statement createStatement(ResultSetLaterProcess rslp, boolean isUpdatable) throws SQLException { Statement st; int rsType = (isUpdatable) ? ResultSet.TYPE_SCROLL_INSENSITIVE : ResultSet.TYPE_FORWARD_ONLY; int rsUpdate = isUpdatable ? ResultSet.CONCUR_UPDATABLE : ResultSet.CONCUR_READ_ONLY; st = getConnection(dbkey).createStatement(rsType, rsUpdate); if (rslp != null) { st = new ProcessableStatement(st, rslp); } return profile.wrap(st, isJpaTx()); } /* * 准备执行SQL */ public PreparedStatement prepareStatement(String sql) throws SQLException { return profile.wrap(getConnection(dbkey).prepareStatement(sql), isJpaTx()); } /* * 准备执行SQL,插入 */ public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { return profile.wrap(getConnection(dbkey).prepareStatement(sql, columnNames), isJpaTx()); } /* * 准备执行SQL,插入 */ public PreparedStatement prepareStatement(String sql, int generateKeys) throws SQLException { return profile.wrap(getConnection(dbkey).prepareStatement(sql, generateKeys), isJpaTx()); } /* * 准备执行SQL,插入 */ public PreparedStatement prepareStatement(String sql, int[] columnIndexs) throws SQLException { return profile.wrap(getConnection(dbkey).prepareStatement(sql, columnIndexs), isJpaTx()); } /* * 准备执行SQL,查询 */ public PreparedStatement prepareStatement(String sql, ResultSetLaterProcess rslp, boolean isUpdatable) throws SQLException { PreparedStatement st; int rsType = (isUpdatable) ? ResultSet.TYPE_SCROLL_INSENSITIVE : ResultSet.TYPE_FORWARD_ONLY; int rsUpdate = isUpdatable ? ResultSet.CONCUR_UPDATABLE : ResultSet.CONCUR_READ_ONLY; st = getConnection(dbkey).prepareStatement(sql, rsType, rsUpdate); if (rslp != null) { st = new ProcessablePreparedStatement(st, rslp); } return profile.wrap(st, isJpaTx()); } /* * 准备执行SQL,查询 */ public PreparedStatement prepareStatement(String sql, int rsType, int concurType, int hold) throws SQLException { return profile.wrap(getConnection(dbkey).prepareStatement(sql, rsType, concurType, hold), isJpaTx()); } public CallableStatement prepareCall(String sql) throws SQLException { return getConnection(dbkey).prepareCall(sql); } public boolean isResultSetHolderTransaction() { if (session instanceof Transaction) { Transaction trans = (Transaction) session; return TransactionFlag.ResultHolder == trans.getTransactionFlag(); } return false; } public void closeTx() { if (session instanceof Transaction) { Transaction tx = (Transaction) session; if (tx.isReadonly()) { tx.close(); } else { tx.commit(true); } } } IConnection getRawConnection() { return getConnection(dbkey); } <T> List<T> populateResultSet(IResultSet rsw, EntityMappingProvider mapping, Transformer transformer) throws SQLException { return session.populateResultSet(rsw, mapping, transformer); } private IConnection getConnection(String dbkey2) { if (conn == null) { try { conn = session.getConnection(); } catch (SQLException e) { throw new RuntimeException(e); } } conn.setKey(dbkey2); return conn; } public String getDbName() { return session.getDbName(dbkey); } public int executeSqlBatch(String sql, List<?>... params) throws SQLException { return innerExecuteSqlBatch(sql, params); } final int innerExecuteSqlBatch(String sql, List<?>... params) throws SQLException { SqlLog log = ORMConfig.getInstance().newLogger(sql.length() + 64); log.append(sql); PreparedStatement st = null; long start = System.currentTimeMillis(); try { st = prepareStatement(sql); st.setQueryTimeout(ORMConfig.getInstance().getUpdateTimeout() * 2);// 批量操作允许更多的时间。 int maxBatchlog = ORMConfig.getInstance().getMaxBatchLog(); for (int i = 0; i < params.length; i++) { session.getListener().beforeSqlExecute(sql, params[i]); BindVariableContext context = new BindVariableContext(st, getProfile(), log); context.setVariables(params[i]); st.addBatch(); if (log.isDebug()) { log.output(); if (i >= maxBatchlog) { log = SqlLog.DUMMY; } } session.checkCacheUpdate(sql, params[i]); } int[] result = st.executeBatch(); long dbAccess = System.currentTimeMillis(); int total = MathUtils.sum(result); log.directLog(StringUtils.concat("Executed:", String.valueOf(total), "\t Time cost([DbAccess]:", String.valueOf(dbAccess - start), "ms) |", getTransactionId())); // 执行回调 for (int i = 0; i < params.length; i++) { session.getListener().afterSqlExecuted(sql, i < result.length ? result[i] : 1, params[i]); } return total; } catch (SQLException e) { DbUtils.processError(e, sql, this); throw e; } finally { if (st != null) st.close(); releaseConnection(); } } public final UpdateReturn innerExecuteUpdate(String sql, List<Object> ps, GenerateKeyReturnOper keyOper) throws SQLException { Object[] params = ps.toArray(); session.getListener().beforeSqlExecute(sql, params); SqlLog sb = ORMConfig.getInstance().newLogger(); long start = System.currentTimeMillis(); PreparedStatement st = null; UpdateReturn result; long dbAccess; int total; sb.append(sql).append(this); try { st = keyOper.prepareStatement(this, sql); st.setQueryTimeout(ORMConfig.getInstance().getUpdateTimeout()); if (!ps.isEmpty()) { BindVariableContext context = new BindVariableContext(st, getProfile(), sb); context.setVariables(ps); } total = st.executeUpdate(); result = new UpdateReturn(total); dbAccess = System.currentTimeMillis(); keyOper.getGeneratedKey(result, st); if (total > 0) { session.checkCacheUpdate(sql, ps); } } catch (SQLException e) { DbUtils.processError(e, sql, this); throw e; } finally { sb.output(); DbUtils.close(st); releaseConnection(); } sb.directLog(StringUtils.concat("Executed:", String.valueOf(total), "\t Time cost([DbAccess]:", String.valueOf(dbAccess - start), "ms) |", getTransactionId())); session.getListener().afterSqlExecuted(sql, total, params); return result; } public final <T> T innerSelectBySql(String sql, ResultSetExtractor<T> rst, List<?> objs, InMemoryOperateProvider lazy) throws SQLException { PreparedStatement st = null; ResultSet rs = null; SqlLog sb = ORMConfig.getInstance().newLogger(); try { sb.ensureCapacity(sql.length() + 30 + objs.size() * 20); sb.append(sql).append(this); long start = System.currentTimeMillis(); ResultSetLaterProcess isReverse = lazy == null ? null : lazy.getRsLaterProcessor(); st = prepareStatement(sql, isReverse, false); BindVariableContext context = new BindVariableContext(st, getProfile(), sb); context.setVariables(objs); rst.apply(st); rs = st.executeQuery(); long dbAccessed = System.currentTimeMillis(); if (lazy != null && lazy.hasInMemoryOperate()) { rs = ResultSetContainer.toInMemoryProcessorResultSet(lazy, new ResultSetHolder(this, st, rs)); } T t; if (rst.autoClose()) { t = rst.transformer(new ResultSetImpl(rs, getProfile())); } else { t = rst.transformer(new ResultSetWrapper(this, st, rs)); } // if(session.isRoutingDataSource()){ rst.appendLog(sb, t); sb.append("\tTime cost([DbAccess]:", dbAccessed - start).append("ms, [Populate]:", System.currentTimeMillis() - dbAccessed).append("ms").append(this); // } return t; } catch (SQLException e) { DbUtils.processError(e, sql, this); throw e; } finally { sb.output(); if (rst.autoClose()) { DbUtils.close(rs); DbUtils.close(st); releaseConnection(); } } } // ///////////////////////////////SqlTemplate ///////////////////////// public <T> NativeQuery<T> createNativeQuery(String sqlString, Class<T> clz) { return new NativeQuery<T>(this, sqlString, new Transformer(clz),false); } <T> NativeQuery<T> createNativeQuery(NQEntry nc, Class<T> resultClz) { return new NativeQuery<T>(this, nc.get(this.profile.getName()), new Transformer(resultClz)); } public <T> NativeQuery<T> createNativeQuery(String sqlString, ITableMetadata meta) { NativeQuery<T> q = new NativeQuery<T>(this, sqlString, new Transformer(meta),false); return q; } <T> NativeQuery<T> createNativeQuery(NQEntry nc, ITableMetadata meta) { NativeQuery<T> q = new NativeQuery<T>(this, nc.get(this.profile.getName()), new Transformer(meta)); return q; } public NativeCall createNativeCall(String procedureName, Type... paramClass) throws SQLException { return new NativeCall(this, procedureName, paramClass, false); } public NativeCall createAnonymousNativeCall(String callString, Type... paramClass) throws SQLException { return new NativeCall(this, callString, paramClass, true); } public <T> NativeQuery<T> createQuery(String jpql, Class<T> resultClass) throws SQLException { NativeQuery<T> query = new NativeQuery<T>(this, jpql, new Transformer(resultClass),true); query.setIsNative(false); return query; } public <T> PagingIterator<T> pageSelectBySql(String sql, Class<T> clz, int pageSize) throws SQLException { return new PagingIteratorSqlImpl<T>(sql, pageSize, new Transformer(clz), this); } public <T> PagingIterator<T> pageSelectBySql(String sql, ITableMetadata meta, int pageSize) throws SQLException { return new PagingIteratorSqlImpl<T>(sql, pageSize, new Transformer(meta), this); } public int executeJPQL(String jpql, Map<String, Object> params) throws SQLException { NativeQuery<?> nq = this.createQuery(jpql, null); if (params != null) { nq.setParameterMap(params); } return nq.executeUpdate(); } public <T> List<T> selectByJPQL(String jpql, Class<T> resultClz, Map<String, Object> params) throws SQLException { NativeQuery<T> nq = this.createQuery(jpql, resultClz); if (params != null) { nq.setParameterMap(params); } return nq.getResultList(); } public long countBySql(String countSql, Object... params) throws SQLException { long start = System.currentTimeMillis(); Long num = innerSelectBySql(countSql, ResultSetExtractor.COUNT_EXTRACTER, Arrays.asList(params), null); if (ORMConfig.getInstance().isDebugMode()) { long dbAccess = System.currentTimeMillis(); LogUtil.show(StringUtils.concat("Count:", String.valueOf(num), "\t [DbAccess]:", String.valueOf(dbAccess - start), "ms) |", getTransactionId())); } return num; } public int executeSql(String sql, Object... params) throws SQLException { return innerExecuteUpdate(sql, Arrays.asList(params), GenerateKeyReturnOper.NONE).getAffectedRows(); } public final <T> List<T> selectBySql(String sql, Class<T> resultClz, Object... params) throws SQLException { return selectBySql(sql, new Transformer(resultClz), null, params); } public final <T> List<T> selectBySql(String sql, Transformer transformer, IntRange range, Object... params) throws SQLException { BindSql bs = range == null ? new BindSql(sql) : getProfile().getLimitHandler().toPageSQL(sql, range.toStartLimitSpan()); long start = System.currentTimeMillis(); TransformerAdapter<T> sqlTransformer = new TransformerAdapter<T>(transformer, this); List<T> list = innerSelectBySql(bs.getSql(), sqlTransformer, Arrays.asList(params), bs); if (ORMConfig.getInstance().isDebugMode()) { long dbAccess = sqlTransformer.dbAccess; LogUtil.show(StringUtils.concat("Result Count:", String.valueOf(list.size()), "\t Time cost([DbAccess]:", String.valueOf(dbAccess - start), "ms, [Populate]:", String.valueOf(System.currentTimeMillis() - dbAccess), "ms) |", getTransactionId())); } return list; } public final <T> T loadBySql(String sql, Class<T> t, Object... params) throws SQLException { TransformerAdapter<T> rst = new TransformerAdapter<T>(new Transformer(t), this); rst.setMaxRows(2); long start = System.currentTimeMillis(); List<T> result = innerSelectBySql(sql, rst, Arrays.asList(params), null); if (ORMConfig.getInstance().isDebugMode()) { long dbAccess = rst.dbAccess; LogUtil.show(StringUtils.concat("Result Count:", String.valueOf(result.size()), "\t Time cost([DbAccess]:", String.valueOf(dbAccess - start), "ms, [Populate]:", String.valueOf(System.currentTimeMillis() - dbAccess), "ms) |", getTransactionId())); } if (result.size() > 1) { throw new IllegalArgumentException("得到多个结果:" + result.size()); } else if (result.isEmpty()) { return null; } else { return result.get(0); } } public final <T> ResultIterator<T> iteratorBySql(String sql, Transformer transformers, int maxReturn, int fetchSize, Object... objs) throws SQLException { TransformerIteratrAdapter<T> t = new TransformerIteratrAdapter<T>(transformers, this); t.setMaxRows(maxReturn); t.setFetchSize(fetchSize); return this.innerSelectBySql(sql, t, Arrays.asList(objs), null); } final <T> ResultIterator<T> iteratorBySql(String sql, Transformer transformers, int maxReturn, int fetchSize, InMemoryOperateProvider lazy, Object... objs) throws SQLException { TransformerIteratrAdapter<T> t = new TransformerIteratrAdapter<T>(transformers, this); t.setMaxRows(maxReturn); t.setFetchSize(fetchSize); return this.innerSelectBySql(sql, t, Arrays.asList(objs), lazy); } public DbMetaData getMetaData() { return session.getNoTransactionSession().getMetaData(dbkey); } /** * 在这里支持内存混合处理 * * @author jiyi * * @param <T> */ public static class TransformerAdapter<T> extends AbstractResultSetTransformer<List<T>> { final Transformer transformers; public long dbAccess; private OperateTarget db; TransformerAdapter(Transformer t, OperateTarget db) { this.transformers = t; this.db = db; } @Override public PopulateStrategy[] getStrategy() { return transformers.getStrategy(); } public List<T> transformer(IResultSet rs) throws SQLException { dbAccess = System.currentTimeMillis(); return db.populateResultSet(rs, null, transformers); } public Session getSession() { return db.session; } @Override public void appendLog(SqlLog log, List<T> result) { log.append("\nResult Count:", result.size()); } } public static class TransformerIteratrAdapter<T> extends AbstractResultSetTransformer<ResultIterator<T>> { final Transformer transformers; private OperateTarget db; TransformerIteratrAdapter(Transformer t, OperateTarget db) { this.transformers = t; this.db = db; } @Override public PopulateStrategy[] getStrategy() { return transformers.getStrategy(); } @SuppressWarnings("unchecked") @Override public ResultIterator<T> transformer(IResultSet rs) throws SQLException { return new ResultIterator.Impl<T>(db.session.iterateResultSet(rs, null, transformers), rs); } @Override public boolean autoClose() { return false; } @Override public void appendLog(SqlLog log, ResultIterator<T> result) { log.append("\nResult: Iterator"); } } public <T> T getExpressionValue(DbFunction func, Class<T> clz, Object... params) throws SQLException { SqlExpression ex = func(func, params); return getExpressionValue(ex.toString(), clz); } public <T> T getExpressionValue(String expression, Class<T> clz, Object... params) throws SQLException { String sql = "select " + expression + " from dual"; StSqlParser parser = new StSqlParser(new StringReader(sql)); List<SelectItem> sts; try { sts = parser.PlainSelect().getSelectItems(); } catch (ParseException e) { throw new SQLException("ParseError:[" + sql + "] Detail:" + e.getMessage()); } // 进行本地语言转化 DatabaseDialect dialect = this.profile; SqlFunctionlocalization visitor = new SqlFunctionlocalization(dialect, this); for (SelectItem item : sts) { item.accept(visitor); } // 形成语句 String template = dialect.getProperty(DbProperty.SELECT_EXPRESSION); String exps = StringUtils.join(sts, ','); if (template == null) { sql = "SELECT " + exps; } else { sql = String.format(template, exps); } return this.loadBySql(sql, clz, params); } public SqlExpression func(DbFunction func, Object... params) { return new SqlExpression(getProfile().getFunction(func, params)); } private boolean isJpaTx() { return session.isJpaTx(); } public PartitionSupport getPartitionSupport() { return session.getPartitionSupport(); } public OperateTarget getTarget(String database) { if (StringUtils.equals(dbkey, database)) { return this; } return session.selectTarget(database); } public DatabaseDialect getDialectOf(String database) { return session.getProfile(database); } }