if (isolationLevelNeeded || definition.isReadOnly()) { if (this.prepareConnection) { preparedCon = HibernateConnectionHandle.doGetConnection(session); previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(preparedCon, definition); } elseif (isolationLevelNeeded) { thrownewInvalidIsolationLevelException(getClass().getSimpleName() + " does not support custom isolation levels since the 'prepareConnection' flag is off."); } }
// Standard JPA transaction begin call for full JPA context setup... entityManager.getTransaction().begin();
// Adapt flush mode and store previous isolation level, if any. FlushModepreviousFlushMode= prepareFlushMode(session, definition.isReadOnly()); // ... }
@Nullable protected FlushMode prepareFlushMode(Session session, boolean readOnly)throws PersistenceException { FlushModeflushMode= (FlushMode) ReflectionUtils.invokeMethod(getFlushMode, session); Assert.state(flushMode != null, "No FlushMode from Session"); if (readOnly) { // We should suppress flushing for a read-only transaction. if (!flushMode.equals(FlushMode.MANUAL)) { session.setFlushMode(FlushMode.MANUAL); return flushMode; } } // ... }
Transaction의 설정이 readOnly = true라면 Hibernate Session의 FlushMode를 MANUAL(명시적으로 EntityManager.flush() 메서드를 호출하기 전까지 flush 되지 않음)로 강제하고 있다.
@Nullable protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation)throws Throwable { // ... if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) { // Standard transaction demarcation with getTransaction and commit/rollback calls. TransactionInfotxInfo= createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal; try { // This is an around advice: Invoke the next interceptor in the chain. // This will normally result in a target object being invoked. retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // target invocation exception completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); }
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) { // Set rollback-only in case of Vavr failure matching our rollback rules... TransactionStatusstatus= txInfo.getTransactionStatus(); if (status != null && txAttr != null) { retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); } }
try { if ( doFlush ) { managedFlush(); } } catch (RuntimeException re) { throw ExceptionMapperStandardImpl.INSTANCE.mapManagedFlushFailure( "error during managed flush", re, this ); } }
위에서 readOnly이면 Hibernate Session의 Flush 모드를 MANUAL로 강제했기 때문에 getHibernateFlushMode()는 MANUAL이 나오기 때문에 getHibernateFlushMode() != FlushMode.MANUAL는 false이기 때문에 doFlush는 false라서 managedFlush 메서드를 호출하지 않아서 실질적으로 flush가 호출되지 않는다.
@Transaction(readOnly = true)에 의해 시작된 트랜잭션도 종료를 해야하기 때문에 커밋을 한다.