요약
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");
그다음 스레드로컬에 바인딩한다
'데이터베이스 > JPA' 카테고리의 다른 글
| 지연로딩 vs 즉시로딩 vs 패치조인 vs JPQL일반조인 vs 일반조인 (0) | 2026.01.12 |
|---|---|
| 스레드로컬과 세션 (0) | 2026.01.09 |