데이터베이스/JPA

세션과 커넥션

정재익 2026. 1. 9. 22:32

요약

1. 최초에 히카리데이터소스에서 풀엔트리크리에이터 생성 풀 엔트리크리에이터에서 콜() 호출

2. 히카리풀에서 createPoolEntry() 호출 PollBase에서 newPollEntry() 호출 뉴커넥션 호출 히카리데이터소스에서 겟커넥션 호출 커넥션 반환 커넥션풀 만큼 커넥션을 만듬

3. @Tramsctional 달린 메소드 시작 트랜잭션인터셉터 호출

4. TransactionAspectSupport의 invokeWithinTransaction() 호출 createTransactionIfNecessary() 호출 

5. 플랫폼트랜잭션매니저의 구현체인 AbstractPlatformTransactionManager에서 겟트랜잭션시작

6AbstractPlatformTransactionManager을 상속받은 하이버네이트트랜잭션매니저에서 doBegin()

7. 세션(SessionImpl) 이 이미존재하면 세션반환 존재하지 않으면 AbstractSharedSessionContract에서 JdbcCoordintor호출 물리적 커넥션 반환
8. DataSorceUtil에서 prepareConnectionForTransaction() 에 커넥션 삽입 후 격리수준 반환

9. HibernateTransactionObject에 격리수준 삽입 

10. TransactionSynchronizationManager에 데이터소스와 커넥션 삽입

11. 작업진행

12. TransactionAspectSupport에서 commitTransactionAfterReturning() 호출 AbstractPlatformTransactionManager에서 commit호출 트랜잭션끝

 

 

세션안에는 커넥션이 있다.

 

public class JtaTransactionAdapterTransactionManagerImpl implements JtaTransactionAdapter {

    private final TransactionManager transactionManager;

    private boolean initiator;

    public JtaTransactionAdapterTransactionManagerImpl(TransactionManager transactionManager) {
       this.transactionManager = transactionManager;
    }

    @Override
    public void begin() {
       try {
          if ( getStatus() == TransactionStatus.NOT_ACTIVE ) {
             JTA_LOGGER.callingTransactionManagerBegin();
             transactionManager.begin();
             initiator = true;
             JTA_LOGGER.calledTransactionManagerBegin();
          }
          else {
             JTA_LOGGER.skippingTransactionManagerBegin();
          }
       }
       catch (Exception e) {
          throw new TransactionException( "JTA TransactionManager.begin() failed", e );
       }
    }

트랜잭션을 시작하면  jta트랜잭션매니저가 트랜잭션매니저를 호출함

 

트랙잭션매니저를 구현하는 플랫폼트랜잭션매니저를지나 그것을 구현하는 추상플랫폼트랜잭션매니저도착

private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
       boolean nested, boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {

    boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
    DefaultTransactionStatus status = newTransactionStatus(
          definition, transaction, true, newSynchronization, nested, debugEnabled, suspendedResources);
    this.transactionExecutionListeners.forEach(listener -> listener.beforeBegin(status));
    try {
       doBegin(transaction, definition);
    }
    catch (RuntimeException | Error ex) {
       this.transactionExecutionListeners.forEach(listener -> listener.afterBegin(status, ex));
       throw ex;
    }
    prepareSynchronization(status, definition);
    this.transactionExecutionListeners.forEach(listener -> listener.afterBegin(status, null));
    return status;
}

하이버네이트트랜잭션매니저의 dobegin이호출됨

@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
    HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;

    if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
       throw new IllegalTransactionStateException(
             "Pre-bound JDBC Connection found! HibernateTransactionManager does not support " +
             "running within DataSourceTransactionManager if told to manage the DataSource itself. " +
             "It is recommended to use a single HibernateTransactionManager for all transactions " +
             "on a single DataSource, no matter whether Hibernate or JDBC access.");
    }

    SessionImplementor session = null;

    try {
       if (!txObject.hasSessionHolder() || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
          Interceptor entityInterceptor = getEntityInterceptor();
          Session newSession = (entityInterceptor != null ?
                obtainSessionFactory().withOptions().interceptor(entityInterceptor).openSession() :
                obtainSessionFactory().openSession());
          if (this.sessionInitializer != null) {
             this.sessionInitializer.accept(newSession);
          }
          if (logger.isDebugEnabled()) {
             logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");
          }
          txObject.setSession(newSession);
       }

       session = txObject.getSessionHolder().getSession().unwrap(SessionImplementor.class);

       boolean holdabilityNeeded = (this.allowResultAccessAfterCompletion && !txObject.isNewSession());
       boolean isolationLevelNeeded = (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT);
       if (holdabilityNeeded || isolationLevelNeeded || definition.isReadOnly()) {
          if (this.prepareConnection && ConnectionReleaseMode.ON_CLOSE.equals(
                session.getJdbcCoordinator().getLogicalConnection().getConnectionHandlingMode().getReleaseMode())) {
             // We're allowed to change the transaction settings of the JDBC Connection.
             if (logger.isDebugEnabled()) {
                logger.debug("Preparing JDBC Connection of Hibernate Session [" + session + "]");
             }
             Connection con = session.getJdbcCoordinator().getLogicalConnection().getPhysicalConnection();
             Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
             txObject.setPreviousIsolationLevel(previousIsolationLevel);
             txObject.setReadOnly(definition.isReadOnly());
             if (holdabilityNeeded) {
                int currentHoldability = con.getHoldability();
                if (currentHoldability != ResultSet.HOLD_CURSORS_OVER_COMMIT) {
                   txObject.setPreviousHoldability(currentHoldability);
                   con.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT);
                }
             }
             txObject.connectionPrepared();
          }
          else {
             // Not allowed to change the transaction settings of the JDBC Connection.
             if (isolationLevelNeeded) {
                // We should set a specific isolation level but are not allowed to...
                throw new InvalidIsolationLevelException(
                      "HibernateTransactionManager is not allowed to support custom isolation levels: " +
                      "make sure that its 'prepareConnection' flag is on (the default) and that the " +
                      "Hibernate connection release mode is set to ON_CLOSE.");
             }
             if (logger.isDebugEnabled()) {
                logger.debug("Not preparing JDBC Connection of Hibernate Session [" + session + "]");
             }
          }
       }

       if (definition.isReadOnly() && txObject.isNewSession()) {
          // Just set to MANUAL in case of a new Session for this transaction.
          session.setHibernateFlushMode(FlushMode.MANUAL);
          // As of 5.1, we're also setting Hibernate's read-only entity mode by default.
          session.setDefaultReadOnly(true);
       }

       if (!definition.isReadOnly() && !txObject.isNewSession()) {
          // We need AUTO or COMMIT for a non-read-only transaction.
          FlushMode flushMode = session.getHibernateFlushMode();
          if (FlushMode.MANUAL.equals(flushMode)) {
             session.setHibernateFlushMode(FlushMode.AUTO);
             txObject.getSessionHolder().setPreviousFlushMode(flushMode);
          }
       }

       Transaction hibTx;

       // Register transaction timeout.
       int timeout = determineTimeout(definition);
       if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
          // Use Hibernate's own transaction timeout mechanism on Hibernate 3.1+
          // Applies to all statements, also to inserts, updates and deletes!
          hibTx = session.getTransaction();
          hibTx.setTimeout(timeout);
          hibTx.begin();
       }
       else {
          // Open a plain Hibernate transaction without specified timeout.
          hibTx = session.beginTransaction();
       }

       // Add the Hibernate transaction to the session holder.
       txObject.getSessionHolder().setTransaction(hibTx);

       // Register the Hibernate Session's JDBC Connection for the DataSource, if set.
       if (getDataSource() != null) {
          final SessionImplementor sessionToUse = session;
          ConnectionHolder conHolder = new ConnectionHolder(
                () -> sessionToUse.getJdbcCoordinator().getLogicalConnection().getPhysicalConnection());
          if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
             conHolder.setTimeoutInSeconds(timeout);
          }
          if (logger.isDebugEnabled()) {
             logger.debug("Exposing Hibernate transaction as JDBC [" + conHolder.getConnectionHandle() + "]");
          }
          TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
          txObject.setConnectionHolder(conHolder);
       }

       // Bind the session holder to the thread.
       if (txObject.isNewSessionHolder()) {
          TransactionSynchronizationManager.bindResource(obtainSessionFactory(), txObject.getSessionHolder());
       }
       txObject.getSessionHolder().setSynchronizedWithTransaction(true);
    }

    catch (Throwable ex) {
       if (txObject.isNewSession()) {
          try {
             if (session != null && session.getTransaction().getStatus() == TransactionStatus.ACTIVE) {
                session.getTransaction().rollback();
             }
          }
          catch (Throwable ex2) {
             logger.debug("Could not rollback Session after failed transaction begin", ex);
          }
          finally {
             SessionFactoryUtils.closeSession(session);
             txObject.setSessionHolder(null);
          }
       }
       throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);
    }
}

하이버네이트 트랜잭션 매니저 도착 잘보면 opensession으로 세션을 열고있다.

 

여기서부터는 https://jaeiktech.tistory.com/143를 읽고와도 좋을듯

@Override
public SessionImpl openSession() {
    log.tracef( "Opening Hibernate Session.  tenant=%s", tenantIdentifier );
    return new SessionImpl( sessionFactory, this );
}

세션임플이 세션팩토리를 대상으로 생긴다 

 

 

public SessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) {
    super( factory, options );

    final HibernateMonitoringEvent sessionOpenEvent = getEventManager().beginSessionOpenEvent();

    persistenceContext = createPersistenceContext();
    actionQueue = createActionQueue();

    autoClear = options.shouldAutoClear();
    autoClose = options.shouldAutoClose();

    if ( options instanceof SharedSessionCreationOptions ) {
       final SharedSessionCreationOptions sharedOptions = (SharedSessionCreationOptions) options;
       final ActionQueue.TransactionCompletionProcesses transactionCompletionProcesses
             = sharedOptions.getTransactionCompletionProcesses();
       if ( sharedOptions.isTransactionCoordinatorShared() && transactionCompletionProcesses != null ) {
          actionQueue.setTransactionCompletionProcesses(
                transactionCompletionProcesses,
                true
          );
       }
    }

    loadQueryInfluencers = new LoadQueryInfluencers( factory, options );

    final StatisticsImplementor statistics = factory.getStatistics();
    if ( statistics.isStatisticsEnabled() ) {
       statistics.openSession();
    }

    if ( properties != null ) {
       //There might be custom properties for this session that affect the LockOptions state
       applyPropertiesToLockOptions( properties, this::getLockOptionsForWrite );
    }
    setCacheMode( fastSessionServices.initialSessionCacheMode );

    // NOTE : pulse() already handles auto-join-ability correctly
    getTransactionCoordinator().pulse();

    // do not override explicitly set flush mode ( SessionBuilder#flushMode() )
    if ( getHibernateFlushMode() == null ) {
       setHibernateFlushMode( getInitialFlushMode() );
    }

    setUpMultitenancy( factory, loadQueryInfluencers );

    if ( log.isTraceEnabled() ) {
       log.tracef( "Opened Session [%s] at timestamp: %s", getSessionIdentifier(), currentTimeMillis() );
    }

    getEventManager().completeSessionOpenEvent( sessionOpenEvent, this );
}

세션임플의 생성자

public AbstractSharedSessionContract(SessionFactoryImpl factory, SessionCreationOptions options) {
    this.factory = factory;

    fastSessionServices = factory.getFastSessionServices();
    cacheTransactionSync = factory.getCache().getRegionFactory().createTransactionContext( this );
    flushMode = options.getInitialSessionFlushMode();
    tenantIdentifier = getTenantId( factory, options );
    interceptor = interpret( options.getInterceptor() );
    jdbcTimeZone = options.getJdbcTimeZone();
    sessionEventsManager = createSessionEventsManager(options);
    entityNameResolver = new CoordinatingEntityNameResolver( factory, interceptor );
    setCriteriaCopyTreeEnabled( factory.getSessionFactoryOptions().isCriteriaCopyTreeEnabled() );
    setNativeJdbcParametersIgnored( factory.getSessionFactoryOptions().getNativeJdbcParametersIgnored() );

    final StatementInspector statementInspector = interpret( options.getStatementInspector() );

    isTransactionCoordinatorShared = isTransactionCoordinatorShared( options );
    if ( isTransactionCoordinatorShared ) {
       final SharedSessionCreationOptions sharedOptions = (SharedSessionCreationOptions) options;
       if ( options.getConnection() != null ) {
          throw new SessionException( "Cannot simultaneously share transaction context and specify connection" );
       }
       transactionCoordinator = sharedOptions.getTransactionCoordinator();
       jdbcCoordinator = sharedOptions.getJdbcCoordinator();
       // todo : "wrap" the transaction to no-op commit/rollback attempts?
       currentHibernateTransaction = sharedOptions.getTransaction();
       connectionHandlingMode = jdbcCoordinator.getLogicalConnection().getConnectionHandlingMode();
       autoJoinTransactions = false;
       jdbcSessionContext = createJdbcSessionContext( statementInspector );
       logInconsistentOptions( sharedOptions );
       addSharedSessionTransactionObserver( transactionCoordinator );
    }
    else {
       autoJoinTransactions = options.shouldAutoJoinTransactions();
       connectionHandlingMode = options.getPhysicalConnectionHandlingMode();
       jdbcSessionContext = createJdbcSessionContext( statementInspector );
       // This must happen *after* the JdbcSessionContext was initialized,
       // because some calls retrieve this context indirectly via Session getters.
       jdbcCoordinator = createJdbcCoordinator( options );
       transactionCoordinator = fastSessionServices.transactionCoordinatorBuilder
             .buildTransactionCoordinator( jdbcCoordinator, this );
    }
}

추상공유세션 클래스에서 jdbc코디네이터가 보인다 

 

public JdbcCoordinatorImpl(
       Connection userSuppliedConnection,
       JdbcSessionOwner owner,
       JdbcServices jdbcServices) {
    this.isUserSuppliedConnection = userSuppliedConnection != null;

    final ResourceRegistry resourceRegistry = new ResourceRegistryStandardImpl(
          owner.getJdbcSessionContext().getObserver()
    );
    if ( isUserSuppliedConnection ) {
       this.logicalConnection = new LogicalConnectionProvidedImpl( userSuppliedConnection, resourceRegistry );
    }
    else {
       this.logicalConnection = new LogicalConnectionManagedImpl(
             owner.getJdbcConnectionAccess(),
             owner.getJdbcSessionContext(),
             owner.getSqlExceptionHelper(),
             resourceRegistry
       );
    }
    this.owner = owner;
    this.jdbcServices = jdbcServices;
}

여기서 논리적 커넥션 생성

 

public LogicalConnectionManagedImpl(
       JdbcConnectionAccess jdbcConnectionAccess,
       JdbcSessionContext jdbcSessionContext,
       SqlExceptionHelper sqlExceptionHelper,
       ResourceRegistry resourceRegistry) {
    this.jdbcConnectionAccess = jdbcConnectionAccess;
    this.observer = jdbcSessionContext.getObserver();
    this.resourceRegistry = resourceRegistry;

    this.connectionHandlingMode = determineConnectionHandlingMode(
          jdbcSessionContext.getPhysicalConnectionHandlingMode(),
          jdbcConnectionAccess );

    this.sqlExceptionHelper = sqlExceptionHelper;

    if ( connectionHandlingMode.getAcquisitionMode() == IMMEDIATELY ) {
       acquireConnectionIfNeeded();
    }

    this.providerDisablesAutoCommit = jdbcSessionContext.doesConnectionProviderDisableAutoCommit();
    if ( providerDisablesAutoCommit ) {
       log.debug(
             "`hibernate.connection.provider_disables_autocommit` was enabled.  This setting should only be " +
                   "enabled when you are certain that the Connections given to Hibernate by the " +
                   "ConnectionProvider have auto-commit disabled.  Enabling this setting when the " +
                   "Connections do not have auto-commit disabled will lead to Hibernate executing " +
                   "SQL operations outside of any JDBC/SQL transaction."
       );
    }
}

논리적 커넥션에는 리소스레지스트리(스테이트랑 리졸트들 들어있는곳)도 포함되어있다.

jdbc세션콘텍스트는 세션의 설정값

jdbc커넥션액세스는 물리커넥션을 가지고온다

 

public JdbcConnectionAccessConnectionProviderImpl(ConnectionProvider connectionProvider) {
    this.connectionProvider = connectionProvider;

    try {
       this.jdbcConnection = connectionProvider.getConnection();
    }
    catch (SQLException e) {
       throw new PersistenceException( "Unable to obtain JDBC Connection", e );
    }

    boolean wasInitiallyAutoCommit;
    try {
       wasInitiallyAutoCommit = jdbcConnection.getAutoCommit();
       if ( !wasInitiallyAutoCommit ) {
          try {
             jdbcConnection.setAutoCommit( true );
          }
          catch (SQLException e) {
             throw new PersistenceException(
                   String.format(
                         "Could not set provided connection [%s] to auto-commit mode" +
                               " (needed for schema generation)",
                         jdbcConnection
                   ),
                   e
             );
          }
       }
    }
    catch (SQLException ignore) {
       wasInitiallyAutoCommit = false;
    }

    log.debugf( "wasInitiallyAutoCommit=%s", wasInitiallyAutoCommit );
    this.wasInitiallyAutoCommit = wasInitiallyAutoCommit;
}

겟 커넥션으로 물리커넥션을 가지고오는걸 볼수있다

@Override
public Connection getConnection() throws SQLException {
    if ( !available ) {
       throw new HibernateException( "Provider is closed" );
    }
    return useCredentials ? dataSource.getConnection( user, pass ) : dataSource.getConnection();
}

데이터소스에서 커넥션갖고옴

 

처음의 dobegin의 마지막

public static void bindResource(Object key, Object value) throws IllegalStateException {
    Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
    Assert.notNull(value, "Value must not be null");
    Map<Object, Object> map = resources.get();
    // set ThreadLocal Map if none found
    if (map == null) {
       map = new HashMap<>();
       resources.set(map);
    }
    Object oldValue = map.put(actualKey, value);
    // Transparently suppress a ResourceHolder that was marked as void...
    if (oldValue instanceof ResourceHolder resourceHolder && resourceHolder.isVoid()) {
       oldValue = null;
    }
    if (oldValue != null) {
       throw new IllegalStateException(
             "Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread");
    }
}

TransactionSynchroniztionManager에서 리소스 바인딩

 

private static final ThreadLocal<Map<Object, Object>> resources =
       new NamedThreadLocal<>("Transactional resources");

private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
       new NamedThreadLocal<>("Transaction synchronizations");

private static final ThreadLocal<String> currentTransactionName =
       new NamedThreadLocal<>("Current transaction name");

private static final ThreadLocal<Boolean> currentTransactionReadOnly =
       new NamedThreadLocal<>("Current transaction read-only status");

private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
       new NamedThreadLocal<>("Current transaction isolation level");

private static final ThreadLocal<Boolean> actualTransactionActive =
       new NamedThreadLocal<>("Actual transaction active");

그다음 스레드로컬에 바인딩한다