package jef.database; import java.sql.SQLException; import javax.persistence.PersistenceException; import jef.common.log.LogUtil; import jef.database.cache.CacheChain; import jef.database.cache.CacheImpl; import jef.database.innerpool.IConnection; import jef.database.support.TransactionTimedOutException; import org.easyframe.enterprise.spring.TransactionMode; import org.omg.CORBA.SystemException; public class TransactionImpl extends Transaction { private boolean dirty; /** * 当前事务为自动提交状态 */ private boolean autoCommit; private boolean rollbackOnly = false; /** * 当前事务隔离级别 */ private int isolationLevel = ISOLATION_DEFAULT; /** * 事务超时截止时间 */ private Long deadline; /** * 当前事务只读 */ private boolean readOnly; /** * jef在执行以下两类操作时,会强制要求在一个事务中执行: <li>Batch 批操作</li> <li>Cascade 级联操作</li> * jef会检查要执行的操作是否已经在一个事务上执行,如果此时用户没有开启事务。 * 那么jef会为了上述操作单独开启一个事务,并在上述操作完成的时候自动提交这个事务。 * 为了将这种内部自动生成的事务和用户事务加以区别,所以通过这个字符串表名事务的类别。 并且可以体现在日志中。 */ TransactionFlag innerFlag; private boolean isJpa; public void setReadonly(boolean flag) { if (this.readOnly == flag) return; readOnly = flag; if (conn != null) try { conn.setReadOnly(flag); } catch (SQLException e) { LogUtil.exception(e); } } public boolean isReadonly() { return this.readOnly; } /** * 是否限制回滚 * * @return */ public boolean isRollbackOnly() { return rollbackOnly; } /** * 设置限制为回滚事务 * * @param rollbackOnly */ public void setRollbackOnly(boolean rollbackOnly) { log.debug("Transaction {} was marked as rollback only", this); this.rollbackOnly = rollbackOnly; } /** * 构造事务 * * @param parent * DbClient * @param flag * 隐含事务标记,传入null表示为普通事务,其他为隐含事务,具体参见{@link TransactionFlag} * @param readOnly * 只读事务 * @throws SQLException * @see TransactionFlag */ TransactionImpl(DbClient parent, TransactionFlag flag, boolean readOnly) { this(parent, 0, -1, readOnly, false); this.innerFlag = flag; } /** * 构造事务 * * @param parent * DbClient * @param timeout * 事务超时时间 * @param isolationLevel * 事务隔离级别 * @param readOnly * 只读事务 * @param noCache * 取消一级缓存 */ TransactionImpl(DbClient parent, int timeout, int isolationLevel, boolean readOnly, boolean noCache) { super(); this.parent = parent; this.isolationLevel = isolationLevel; this.readOnly = readOnly; this.preProcessor = parent.preProcessor; this.selectp = parent.selectp; this.insertp = parent.insertp; this.updatep = parent.updatep; this.deletep = parent.deletep; if (noCache || !ORMConfig.getInstance().isCacheLevel1()) { // 没有自己的缓存 cache = parent.getCache(); } else if (parent.getCache().isDummy()) { // 自己有缓存,上级无缓存 cache = new CacheImpl(preProcessor, selectp, 0, "L1"); } else { // 两级缓存都启用的情况下 cache = new CacheChain(new CacheImpl(preProcessor, selectp, 0, "L1"), parent.getCache()); } getListener().newTransaction(this); if (timeout > 0) { this.deadline = System.currentTimeMillis() + timeout * 1000; } this.isJpa = parent.getTxType() == TransactionMode.JPA; log.debug("[JPA DEBUG]:Transaction {} started ,mode is {}.", this, parent.getTxType()); } /** * 提交当前事务 * * @param close * If true, close the transaction after commit; */ public void commit(boolean close) { // 如果已经关闭什么也不作 if (parent == null) return; try { if (dirty && !autoCommit) { if (rollbackOnly) { log.warn("[JPA WARN]:Transaction {} has been marked as rollback-only,so will rollback the transaction.", this); doRollback(); } else { doCommit(); } } } catch (SQLException e) { throw new PersistenceException(e.getSQLState(), e); } finally { if (close) doClose(); } } /** * 回滚当前事务中的所有操作 * * @param close * If true, close the transaction after rollback; */ public void rollback(boolean close) { // 如果已经关闭什么也不作 if (parent == null) return; try { if (dirty && !autoCommit) { doRollback(); } } catch (SQLException e) { throw new PersistenceException(e.getSQLState(), e); } finally { if (close) doClose(); } } public boolean isOpen() { return parent != null; } public boolean isAutoCommit() { return autoCommit; } public Transaction setAutoCommit(boolean autoCommit) { if (autoCommit == this.autoCommit || parent.getTxType() == TransactionMode.JTA) { return this; } this.autoCommit = autoCommit; if (conn != null) { try { this.conn.setAutoCommit(autoCommit); } catch (SQLException e) { throw new PersistenceException(e); } } return this; } private void doRollback() throws SQLException { if (conn != null && parent.getTxType() != TransactionMode.JTA) { getListener().beforeRollback(this); long start = System.currentTimeMillis(); conn.rollback(); dirty = false; log.debug("[JPA DEBUG]:Transaction {} rollback. cost {}ms.", this, (System.currentTimeMillis() - start)); getListener().postRollback(this); } } private void doCommit() throws SQLException { if (conn != null && parent.getTxType() != TransactionMode.JTA) { getListener().beforeCommit(this); long start = System.currentTimeMillis(); conn.commit(); dirty = false; log.debug("[JPA DEBUG]:Transaction {} commited. cost {}ms.", this, System.currentTimeMillis() - start); getListener().postCommit(this); } } private void doClose() { if (parent == null) return; if (conn != null) { try { if (!autoCommit && parent.getTxType() != TransactionMode.JTA) { conn.setAutoCommit(true); } // 注意,readOnly必须在AutoCommit设置完成后才能设置,因为在Postgres上,autocommit=false时,认为是在事务中,而事务中不允许修改readOnly属性 if (readOnly) { conn.setReadOnly(false); } } catch (SQLException e) { LogUtil.exception(e); } conn.close(); conn = null; } parentName = parent.getDbName(null); try { getListener().tracsactionClose(this); } catch (Throwable t) { log.error("", t); } parent = null; } @Override public void close() { cache.evictAll(); if (parent != null) { if (!readOnly && dirty && !autoCommit) { rollback(); } doClose(); } } protected IConnection getConnection() throws SQLException { if (parent == null) { throw new IllegalStateException("Current transaction is closed!|" + getTransactionId(null)); } if (deadline != null) { checkTimeToLiveInMillis(); } if (conn == null) { IConnection conn = parent.getPool().getConnection(this); if (parent.getTxType() != TransactionMode.JTA) { conn.setAutoCommit(autoCommit); if (readOnly) { conn.setReadOnly(true); } if (isolationLevel >= 0 && ORMConfig.getInstance().isSetTxIsolation()) { conn.setTransactionIsolation(isolationLevel); } } this.conn = conn; } dirty = true; return conn; } private void checkTimeToLiveInMillis() throws TransactionTimedOutException { long timeToLive = this.deadline - System.currentTimeMillis(); boolean deadlineReached = timeToLive <= 0; if (deadlineReached) { setRollbackOnly(true); throw new TransactionTimedOutException("Transaction timed out: deadline was " + this.deadline); } } /** * 事务的超时设置 * * @param timeout单位为秒 * @throws SystemException */ public void setTimeout(int timeout) { if (timeout > TIMEOUT_DEFAULT) { this.deadline = System.currentTimeMillis() + timeout * 1000; } } /** * Return whether this object has an associated timeout. */ public boolean hasTimeout() { return (this.deadline != null); } public int getIsolationLevel() { return isolationLevel; } public void setIsolationLevel(int isolationLevel) { this.isolationLevel = isolationLevel; } @Override public TransactionFlag getTransactionFlag() { return innerFlag; } @Override protected TransactionMode getTxType() { return parent.getTxType(); } @Override protected boolean isJpaTx() { return isJpa; } }