JiwonDev

@Transactional ์˜ ๋™์ž‘์›๋ฆฌ, ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €

by JiwonDev

๐Ÿ’ญ JDBC์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํŠธ๋žœ์žญ์…˜

์ž๋ฐ”์˜ JDBC์—์„œ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ํŠธ๋žœ์žญ์…˜์„ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํ•œ๊ฐ€์ง€ ๋ฐ–์— ์—†์Šต๋‹ˆ๋‹ค.

// ์ปค๋„ฅ์…˜ ํ’€์—์„œ DB์ปค๋„ฅ์…˜์„ ๋ฐ›์•„์™”๋‹ค๊ณ  ๊ฐ€์ •
Connection connection = dataSource.getConnection();

try (connection) {
    connection.setAutoCommit(false); // ์ž๋™ ์ปค๋ฐ‹ off
    // ...DB ์ž‘์—…...
    connection.commit(); // ์„ฑ๊ณต์‹œ ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹
} catch (SQLException e) {
    connection.rollback(); // ์˜ค๋ฅ˜ ๋ฐœ์ƒ์‹œ ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ
}

JDBC๊ฐ€ ์ž‘๋™๋˜๋Š” ๊ณผ์ •. DAO๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ๋งŒ๋“  ์ฝ”๋“œ (DataAccessObject)

์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” Spring์™€ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์—์„œ ์ œ๊ณตํ•ด์ฃผ๋Š” @Transactional๋Š” ์•Œ์•„์„œ ํŠธ๋žœ์žญ์…˜์„ ๊ด€๋ฆฌํ•ด์ฃผ๋Š” ๋งˆ๋ฒ•์˜ ํ‚ค์›Œ๋“œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์ถ”์ƒํ™”ํ•ด์„œ ์‚ฌ์šฉํ•  ๋ฟ์ด์ง€ ์‹ค์ œ๋Š” ์œ„์˜ ์ฝ”๋“œ์ฒ˜๋Ÿผ JDBC ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

 


๐Ÿ’ญSpring์˜ @Transactional

์Šคํ”„๋ง์—์„œ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ ์žํ•˜๋ฉด ์•„๋ž˜์™€ ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ฉ”์„œ๋“œ์— ๋ถ™์ด๋ฉด ๋ฉ๋‹ˆ๋‹ค.

@Transactional(propagation = TransactionDefinition.NESTED, 
        isolation = TransactionDefinition.ISOLATION_READ_UNCOMMITTED)

์ „ํŒŒ์˜ต์…˜ ์˜ˆ์ œ(propagation)

๋”๋ณด๊ธฐ
@SpringBootTest
class UserServiceTest {
    
    /* ... */

    @Autowired
    UserService userService;

    User setUpUser;

    @BeforeEach
    void setUp() {
        setUpUser = userService.save("set-up-nickname", 20);
    }

    @Test
    @DisplayName("Non Transactional")
    void changeNicknameTest() {
        String nickname = "sample-nickname";
        int age = 20;
        User savedUser = userService.save(nickname, age);

        /*
        * INSERT USER
        * */

        String newNickname = "new-nickname";
        User findUser = userService.changeNickname(savedUser.getId(), newNickname);

        /*
        * UPDATE USER
        * */

        /*
        * savedUser = {User@9086}
        *   id = 9
        *   nickname = "sample-nickname"
        *   age = 20
        *
        * findUser = {User@9146}
        *   id = 9
        *   nickname = "new-nickname"
        *   age = 20
        * */

        assertEquals(newNickname, findUser.getNickname());
        assertNotEquals(newNickname, savedUser.getNickname());
        assertEquals(findUser.getId(), savedUser.getId());
        assertNotEquals(findUser, savedUser);

        /*
        * No Rollback
        * */
    }

    @Test
    @DisplayName("Propagation: Required (๊ธฐ๋ณธ ์„ธํŒ… ๊ฐ’. ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์กด์žฌํ•˜๋ฉด ์ด์–ด์„œ ๋ฐ›์•„๋“ค์ด๊ณ , ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘)")
    @Transactional
    void changeNicknameAsRequiredTransactionalTest() {
        String nickname = "sample-nickname";
        int age = 20;
        User savedUser = userService.save(nickname, age);

        /*
        * No INSERT USER
        * */

        String newNickname = "new-nickname";
        User findUser = userService.changeNickname(savedUser.getId(), newNickname);

        /*
        * No UPDATE USER
        * */

        /*
        * savedUser = {User@9055}
        *   id = 11
        *   nickname = "new-nickname"
        *   age = 20
        *
        * findUser = {User@9055}
        *   id = 11
        *   nickname = "new-nickname"
        *   age = 20
        * */

        assertEquals(findUser, savedUser);

        /*
        * Rollback Well
        * */
    }

    @Test
    @DisplayName("Propagation: Supports (ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์กด์žฌํ•˜๋ฉด ์ด์–ด์„œ ๋ฐ›์•„๋“ค์ด๊ณ , ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๊ตณ์ด ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ  Non-ํŠธ๋žœ์žญ์…˜ ์ƒํƒœ๋กœ ์‹คํ–‰)")
    @Transactional(propagation = Propagation.SUPPORTS)
    void changeNicknameAsSupportsTransactionalTest() {
        String nickname = "sample-nickname";
        int age = 20;
        User savedUser = userService.save(nickname, age);

        /*
        * INSERT USER
        * */

        String newNickname = "new-nickname";
        User findUser = userService.changeNickname(savedUser.getId(), newNickname);

        /*
        * UPDATE USER
        * */

        /*
        * savedUser = {User@9086}
        *   id = 6
        *   nickname = "sample-nickname"
        *   age = 20
        *
        * findUser = {User@9151}
        *   id = 6
        *   nickname = "new-nickname"
        *   age = 20
        * */

        assertEquals(newNickname, findUser.getNickname());
        assertNotEquals(newNickname, savedUser.getNickname());
        assertEquals(findUser.getId(), savedUser.getId());
        assertNotEquals(findUser, savedUser);

        /*
        * No Rollback
        * */
    }

    /*
    * ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์กด์žฌํ•˜๋ฉด ์ด์–ด์„œ ๋ฐ›์•„๋“ค์ด๊ณ , ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์˜ˆ์™ธ๋ฅผ ๋˜์ง„๋‹ค.
    * */
    @Test
    @DisplayName("Propagation - Mandatory")
    @Transactional(propagation = Propagation.MANDATORY)
    void changeNicknameAsMandatoryTransactionalTest() {
        /*
        * IllegalTransactionStateException ๋ฐœ์ƒ
        * */
        System.out.println("์‹คํ–‰ ๋ ๊นŒ?");
    }

    @Test
    @DisplayName("Propagation: Requires New (ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์กด์žฌํ•˜๋ฉด ์ค‘๋‹จํ•˜๊ณ , ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘)")
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    void changeNicknameAsRequiresNewTransactionalTest() {
        String nickname = "sample-nickname";
        int age = 20;
        User savedUser = userService.save(nickname, age);

        /*
        * No INSERT USER
        * */

        String newNickname = "new-nickname";
        User findUser = userService.changeNickname(savedUser.getId(), newNickname);

        /*
        * No UPDATE USER
        * */

        /*
        * savedUser = {User@9055}
        *   id = 8
        *   nickname = "new-nickname"
        *   age = 20
        *
        * findUser = {User@9055}
        *   id = 8
        *   nickname = "new-nickname"
        *   age 20
        * */

        assertEquals(findUser, savedUser);

        /*
        * Rollback Well
        * */
    }

    @Test
    @DisplayName("Propagation: Not Supported (ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์กด์žฌํ•˜๋ฉด ์ค‘๋‹จ์‹œํ‚ค๊ณ , Non-ํŠธ๋žœ์žญ์…˜ ์ƒํƒœ๋กœ ์‹คํ–‰)")
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    void changeNicknameAsNotSupportedTransactionalTest() {
        String nickname = "sample-nickname";
        int age = 20;
        User savedUser = userService.save(nickname, age);

        /*
         * INSERT USER
         * */

        String newNickname = "new-nickname";
        User findUser = userService.changeNickname(savedUser.getId(), newNickname);

        /*
         * UPDATE USER
         * */

        /*
         * savedUser = {User@9086}
         *   id = 15
         *   nickname = "sample-nickname"
         *   age = 20
         *
         * findUser = {User@9145}
         *   id = 15
         *   nickname = "new-nickname"
         *   age = 20
         * */

        assertEquals(newNickname, findUser.getNickname());
        assertNotEquals(newNickname, savedUser.getNickname());
        assertEquals(findUser.getId(), savedUser.getId());
        assertNotEquals(findUser, savedUser);

        /*
         * No Rollback
         * */
    }

    @Test
    @DisplayName("Propagation: Never (ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์กด์žฌํ•˜๋ฉด ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๊ณ , Non-ํŠธ๋žœ์žญ์…˜ ์ƒํƒœ๋กœ ์‹คํ–‰)")
    @Transactional(propagation = Propagation.NEVER)
    void changeNicknameAsNeverTransactionalTest() {
        String nickname = "sample-nickname";
        int age = 20;
        User savedUser = userService.save(nickname, age);

        /*
         * INSERT USER
         * */

        String newNickname = "new-nickname";
        User findUser = userService.changeNickname(savedUser.getId(), newNickname);

        /*
         * UPDATE USER
         * */

        /*
         * savedUser = {User@9086}
         *   id = 16
         *   nickname = "sample-nickname"
         *   age = 20
         *
         * findUser = {User@9145}
         *   id = 16
         *   nickname = "new-nickname"
         *   age = 20
         * */

        assertEquals(newNickname, findUser.getNickname());
        assertNotEquals(newNickname, savedUser.getNickname());
        assertEquals(findUser.getId(), savedUser.getId());
        assertNotEquals(findUser, savedUser);

        /*
         * No Rollback
         * */
    }

    @Test
    @DisplayName("Propagation: Nested (ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์กด์žฌํ•˜๋ฉด ์ค‘์ฒฉ๋œ ํŠธ๋žœ์žญ์…˜์„ ์ƒ์„ฑํ•˜์—ฌ ์‹คํ–‰ํ•˜๊ณ , ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด Required์™€ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘)")
    @Transactional(propagation = Propagation.NESTED)
    void changeNicknameAsNestedTransactionalTest() {
        String nickname = "sample-nickname";
        int age = 20;
        User savedUser = userService.save(nickname, age);

        /*
         * No INSERT USER
         * */

        String newNickname = "new-nickname";
        User findUser = userService.changeNickname(savedUser.getId(), newNickname);

        /*
         * No UPDATE USER
         * */

        /*
         * savedUser = {User@9055}
         *   id = 17
         *   nickname = "new-nickname"
         *   age = 20
         *
         * findUser = {User@9055}
         *   id = 17
         *   nickname = "new-nickname"
         *   age 20
         * */

        assertEquals(findUser, savedUser);

        /*
         * Rollback Well
         * */
    }

}

์ด๋ ‡๊ฒŒ ๋‹จ์ˆœํ•œ ์„ค์ •๋ณ€์ˆ˜ ํ•˜๋‚˜๋กœ ํŠธ๋žœ์žญ์…˜์˜ propagation level๊ณผ isolation level์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งˆ๋ฒ•์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ, ๊ฒฐ๊ตญ ์‹ค์ œ๋กœ๋Š” ์ œ์ผ ์ฒ˜์Œ๋ณธ JDBC์ฝ”๋“œ์™€ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

// isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED
connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);

// propagation=TransactionDefinition.NESTED
Savepoint savePoint = connection.setSavepoint();

...
connection.rollback(); // ์˜ค๋ฅ˜ ๋ฐœ์ƒ์‹œ ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ

 


๐Ÿ’ญ์Šคํ”„๋ง์˜ ๋งˆ์ˆ , Transaction Management

๐Ÿ“Œ ์Šคํ”„๋ง์˜ ์ถ”์ƒํ™”, PlatformTranscationManager

์Šคํ”„๋ง์€ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ๋ฅผ TranscationManager ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๊ตฌํ˜„์ฒด๋Š” ๊ฐˆ์•„๋ผ์šธ ์ˆ˜ ์žˆ๊ฒŒ ์ธํ„ฐํŽ˜์ด์Šค์ธ PlatformTranscationManager๊ฐ€ ์ฃผ์ž…๋˜์–ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

// ๊น”-๋”. 
public interface PlatformTransactionManager {
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;

    void commit(TransactionStatus var1) throws TransactionException;

    void rollback(TransactionStatus var1) throws TransactionException;
}

๋ฌผ๋ก  ๊ตฌํ˜„์ฒด๋งˆ๋‹ค ๊ฑฐ์˜ ๋™์ผํ•œ ๋ถ€๋ถ„์ด ์žˆ์„ ์ˆ˜ ์žˆ์–ด์„œ, ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” AbstarctPlatformTxManager๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ ๊ธฐ๋ณธ ๊ตฌํ˜„์ฒด๋Š” DataSourceTxManager์ด๊ณ , SpringDataJPA ์‚ฌ์šฉ์‹œ JpaTxManager๋กœ ์„ค์ •์„ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

 

์ดˆ๊ธฐ ์Šคํ”„๋ง์€ XML์„ ๋นˆ์„ ์ƒ์„ฑํ•˜์—ฌ ๊ด€๋ฆฌํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋”๋ณด๊ธฐ

ํ•˜์ง€๋งŒ ์ด๋Š” ๋งค์šฐ ๋ถˆํŽธํ•˜๊ณ  ์ฝ”๋“œ์™€ ์„ค์ •ํŒŒ์ผ์„ ๋งค๋ฒˆ ๋งž์ถฐ์ฃผ์–ด์•ผํ•ด์„œ, ์ตœ๊ทผ์—๋Š” ๊ฑฐ์˜ ์“ฐ์ง€์•Š๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

</beans> <!-- ์ด๋ ‡๊ฒŒ ์„ค์ •ํ–ˆ์—ˆ๋‹ต๋‹ˆ๋‹ค. ์ตœ๊ทผ์—๋Š” yaml์ด๋‚˜ @์–ด๋…ธํ…Œ์ด์…˜, ์ž๋ฐ”์ฝ”๋“œ๋กœ ์ง์ ‘ ์„ค์ •ํ•˜์ฃ .-->

<!-- dataSource ์„ค์ •, spring-jdbc ๋ชจ๋“ˆ ์‚ฌ์šฉ, spring ์—์„œ jdbc ๋ฅผ ํ†ตํ•ด mysql ์— ์ ‘์†ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•จ -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> 
    <property name="url" value="jdbc:mysql://35.200.109.72:3306/testdb?useSSL=false"></property>
    <property name="username" value="root"></property>
    <property name="password" value="๋น„~๋ฐ€"></property>
</bean>		
 
   <bean id="jdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
     <constructor-arg ref="dataSource"></constructor-arg>  
   </bean>
   
   <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   	<property name="dataSource" ref="dataSource"/>
   </bean>
</beans>

 

 

์ตœ๊ทผ์—๋Š” yaml ๊ฐ™์€ ์„ค์ • ํŒŒ์ผ๊ณผ ์ž๋ฐ”์ฝ”๋“œ๋กœ ์‰ฝ๊ฒŒ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์ฃ .

spring:
  datasource:
    url: jdbc:h2:tcp://์ฃผ์†Œ:ํฌํŠธ/...
    username: ์•„์ด๋””
    password: ๋น„๋ฐ€๋ฒˆํ˜ธ
    driver-class-name: org.h2.Driver
@Configuration
public class DatabaseConfiguration {

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        var factoryBean = new SqlSessionFactoryBean();
        var configuration = new org.apache.ibatis.session.Configuration();
        
        configuration.setMapUnderscoreToCamelCase(true);
        factoryBean.setDataSource(dataSource);
        factoryBean.setConfiguration(configuration);
        
        var resolver = new PathMatchingResourcePatternResolver();
        factoryBean.setMapperLocations(resolver.getResources("classpath:mapper/xml/*.xml"));
        return factoryBean.getObject();
    }

}

 

์ฐธ๊ณ ๋กœ ํ…Œ์ŠคํŠธ ์šฉ๋„๋กœ ์“ฐ์ด๋Š” ์ธ๋ฉ”๋ชจ๋ฆฌ DB๋Š”, Datasource๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์•„๋„ ๋ฐ”๋กœ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์ด๋Š” SpringBoot์— ์žˆ๋Š” @AutoConfiguration์— ์˜ํ•ด DataSource์™€ JdbcTemplate์ด ๋นˆ์œผ๋กœ ์ƒ์„ฑ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
๋ฌผ๋ก  ํ•ด๋‹น ์ธ๋ฉ”๋ชจ๋ฆฌ DB Driver ์˜์กด์„ฑ์€ ์žˆ์–ด์•ผ ์ž๋™์„ค์ • ๋ฉ๋‹ˆ๋‹ค. ์Šคํ”„๋ง์€ ๋งˆ์ˆ ์ด ์•„๋‹ˆ์—์š”.
์ƒ์„ฑ๋œ DataSource๋Š” ์˜์กด์„ฑ์œผ๋กœ ์ฃผ์ž…๋ฐ›์•„์„œ ํ™•์ธํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Slf4j
@Component
/* ์ฐธ๊ณ ๋กœ ์Šคํ”„๋ง ์•ฑ ์‹คํ–‰ํ›„, ์–ด๋– ํ•œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ApplicationRunner.run()์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. */
public class TestRunner implements ApplicationRunner {
    @Autowired
    DataSource dataSource;

    @Override
    public void run(
        ApplicationArguments args) throws Exception {
        Connection connection = dataSource.getConnection();

        log.info("Url: " + connection.getMetaData().getURL());
        log.info("UserName: " + connection.getMetaData().getUserName()); 
        // INFO --- [ main] TestRunner : Url: jdbc:h2:mem:testdb
        // INFO --- [ main] TestRunner : UserName: SA
    }
}

 

 

๐Ÿ“Œ ์‚ฌ์šฉ๋ฒ•1 - TransactionTemplate ์‚ฌ์šฉ

์Šคํ”„๋ง์˜ IoC, AOP๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , ์ฝ”๋“œ๋กœ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋ณดํ†ต TransactionTemplate์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. (๊ธฐ์–ตํ•˜์„ธ์š”, ์ด ๊ฐ์ฒด๋Š” ๋‚ด๋ถ€์—์„œ PlatformTransactionManager๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.)

@Service
public class UserService {
    @Autowired
    private TransactionTemplate template;

    public Long registerUser(User user) {
        Long id = template.execute(status -> {
            // SQL ์‹คํ–‰
            return id;
        });
    }
}

์ด ๋ฐฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™์€ ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์Šคํ”„๋ง IoC๋ฅผ ํ†ตํ•ด ์„ค์ •์˜์—ญ๊ณผ DB์‹คํ–‰์ฝ”๋“œ๊ฐ€ ๋ถ„๋ฆฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์„ค์ •์„ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜์„ ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ด€๋ฆฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž๋Š” ํŠธ๋žœ์žญ์…˜ ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ๋งŒ ๊ตฌํ˜„ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • ์ €์ˆ˜์ค€์˜ SQLExceptions์„ ์Šคํ”„๋ง์—์„œ ์žก์•„ ์ถ”์ƒํ™”๋œ ๋Ÿฐํƒ€์ž„ ์˜ˆ์™ธ๋กœ ํฌ์žฅํ•ด์ค๋‹ˆ๋‹ค.

 

๋ฌผ๋ก  @Transcational์ด ์ƒ๋‹นํžˆ ํŽธํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ž˜ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์•„๋‹™๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋‹ค์ด๋‚˜๋ฏน ํ”„๋ก์‹œ์˜ ์›๋ฆฌ์ƒ, @Transcational์˜ ๊ฒฝ์šฐ ํด๋ž˜์Šค๋‚˜ ๋ฉ”์„œ๋“œ ๋‹จ์œ„๋กœ ๋ฐ–์— ๊ฑธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด [์ƒํ’ˆ๊ตฌ๋งค-์ด๋ฉ”์ผ๋ฐœ์†ก]์ธ๋ฐ, ์ด๋ฉ”์ผ ๋ฐœ์†ก์ด ์ทจ์†Œ๋˜์—ˆ๋‹ค๊ณ  ํŠธ๋žœ์žญ์…˜์— ์˜ํ•ด ๋กค๋ฐฑ๋˜๋ฉด ์•ˆ๋ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฐ ๊ฒฝ์šฐ @Transcational ์œผ๋กœ ํ•ด๊ฒฐํ•˜๊ธฐ์—” ์• ๋งคํ•œ ์ƒํ™ฉ์ด ๋‚˜์˜ต๋‹ˆ๋‹ค. DIP๋ฅผ ๋ฌด์‹œํ•˜๊ณ  ๋„๋ฉ”์ธ์— ๋ถ™์ธ๋‹ค๊ฑฐ๋‚˜..

public class TransactionInvoker {

    private A1Dao a1dao;
    private A2Dao a2dao;

    @Transactional
    public void doInternalTransaction() throws Exception{
        a1dao.insertA1();
        a2dao.insertA2();
    }
}
  • ํ”„๋ก์‹œ์˜ ํŠน์„ฑ์ƒ, ํŠธ๋žœ์žญ์…˜์€ ์™ธ๋ถ€์—์„œ doInternalTranscation()์„ ํ˜ธ์ถœํ•  ๋•Œ๋งŒ ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค.
  • ์Šคํ”„๋ง ํŠธ๋žœ์žญ์…˜์€ ๋ฉ”์„œ๋“œ ์‹œ์ž‘์‹œ ์ปค๋„ฅ์…˜์ด ์ƒ์„ฑ๋˜๊ณ , ๋ฉ”์„œ๋“œ ์ข…๋ฃŒ์‹œ์— ์ปค๋„ฅ์…˜์„ ํ’€์— ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

ํŠธ๋žœ์žญ์…˜์„ ์„ธ๋ถ€์ ์œผ๋กœ ๊ฑธ๊ณ ์‹ถ๋‹ค๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด TranscationTemplate์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

@Transcational์—์„œ ์ง€์›ํ•˜๋Š” ์˜ต์…˜์€ ๋‹น์—ฐํžˆ Template์—๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
propagation : ํŠธ๋žœ์žญ์…˜ ์ „ํŒŒ ๊ทœ์น™ ์„ค์ •
isolation : ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ๋ ˆ๋ฒจ ์„ค์ •
readOnly : ์ฝ๊ธฐ ์ „์šฉ ์—ฌ๋ถ€ ์„ค์ •
rollbackFor : ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ•  ์˜ˆ์™ธ ํƒ€์ž… ์„ค์ •
noRollbackFor : ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ•˜์ง€ ์•Š์„ ์˜ˆ์™ธ ํƒ€์ž… ์„ค์ •
timeout : ํŠธ๋žœ์žญ์…˜ ํƒ€์ž„์•„์›ƒ ์‹œ๊ฐ„ ์„ค์ •
public class TransactionInvoker {

    private A1Dao a1dao;
    private A2Dao a2dao;
    private TransactionTemplate transactionTemplate;

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        // @Transcational(...)์˜ ์„ค์ •์€ ์ด๋ ‡๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.
        this.transactionTemplate = new TransactionTemplate(transactionManager);
        this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        this.transactionTemplate.setTimeout(30);
    }

    private void doInternalTransaction() throws Exception {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            public void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    a1dao.insertA1();
                    a2dao.insertA2();
                } catch (Exception e) {
                    status.setRollbackOnly(); // ์ฝœ๋ฐฑ์—์„œ ์‹คํ–‰๋œ ์ฝ”๋“œ๋งŒ ๋กค๋ฐฑ
                }
            }
        });
    }
}

 

์ฐธ๊ณ ๋กœ ๋ฐ˜ํ™˜๊ฐ’์ด ์žˆ๋Š” ํŠธ๋žœ์žญ์…˜(TranscationCallback)์€ .execute() ๋ฉ”์„œ๋“œ์˜ ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

๋žŒ๋‹คํ‘œํ˜„์‹์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ๊ฒฝ์šฐ, ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋ฉด ๋” ๊ฐ„๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

transactionTemplate.execute((TransactionCallback) status -> {
    try {
        a1dao.insertA1();
        a2dao.insertA2();
    } catch (Exception e) {
        status.setRollbackOnly();
    }
    return "Hello"; // tanscationTemplate.execute() ์˜ ๋ฐ˜ํ™˜๊ฐ’
});

 

์ด๋Ÿฐ ๊ฒฝ์šฐ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// ํ”„๋ก์‹œ์™€ ๋‹ค๋ฅด๊ฒŒ ์›ํ•˜๋Š” ์ง€์ ๋งŒ ๊ฑธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ง ๊ทธ๋Œ€๋กœ ์ง์ ‘ ํŠธ๋žœ์žญ์…˜์„ ๊ฑฐ๋Š”๊ฑฐ๋‹ˆ๊นŒ์š”.
transactionTemplate.execute {
  val person = Person(name = "Suhwan Jee")
  personRepository.save(person) /* Spring Data JPA */

  try {
    transactionTemplate.execute {
      throw Exception("some unexpected exception")
    }
  } catch (e: Exception) {
  }
}


๋ฌผ๋ก  @Transcational์ด๋‚˜ TranscationTemplate์ด๋‚˜ ๊นŒ๋ณด๋ฉด PlatformTxManager๋กœ ์‹คํ–‰ํ•˜๋Š”๊ฑด ๋˜‘๊ฐ™์Šต๋‹ˆ๋‹ค.
๋”ฐ๋กœ ๋™์ž‘ํ•˜๋Š”๊ฒŒ ์•„๋‹™๋‹ˆ๋‹ค. + Spring Transcation Template์„ ์‚ฌ์šฉํ•  ๋•Œ ์‹ค์ˆ˜ํ•˜๊ธฐ ์‰ฌ์šด ๋ถ€๋ถ„๋“ค

 

Spring Transaction ์‚ฌ์šฉ ์‹œ ์ฃผ์˜ํ•  ์ 

๊ฐœ์š” ์ตœ๊ทผ ๋ช‡ ๋‹ฌ ๊ฐ„ ๋‚ด๊ฐ€ Spring์—์„œ ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•  ๋•Œ ๊ฒช์—ˆ๋˜ ์—ฌ๋Ÿฌ ๋ฌธ์ œ ์ƒํ™ฉ์— ๋Œ€ํ•ด์„œ ์ด์•ผ๊ธฐํ•˜๋ ค๊ณ  ํ•œ๋‹ค. ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ํŠธ๋žœ์žญ์…˜์„ ์ƒˆ๋กœ ์—ฌ๋Š” ๊ฒฝ์šฐ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜์„

suhwan.dev

// TranscationTemplate์˜ ๋‚ด๋ถ€์ฝ”๋“œ
public class TransactionTemplate extends DefaultTransactionDefinition 
                                 implements TransactionOperations, InitializingBean {
    protected final Log logger = LogFactory.getLog(this.getClass());
    @Nullable
    private PlatformTransactionManager transactionManager;

    @Nullable
    public <T> T execute(TransactionCallback<T> action) throws TransactionException {
        Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
        if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
            return ((CallbackPreferringPlatformTransactionManager)this.transactionManager).execute(this, action);
        } else {
			// PlatfromTranscationManger.getTransaction()์„ ํ†ตํ•ด ํŠธ๋žœ์žญ์…˜์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
            TransactionStatus status = this.transactionManager.getTransaction(this);

            Object result;
            try {
                result = action.doInTransaction(status);
            } catch (Error | RuntimeException var5) {
                this.rollbackOnException(status, var5);
                throw var5;
            } catch (Throwable var6) {
                this.rollbackOnException(status, var6);
                throw new UndeclaredThrowableException(var6, "TransactionCallback threw undeclared checked exception");
            }

            this.transactionManager.commit(status);
            return result;
        }
    }

 


@ ํŠธ๋žœ์žญ์…˜ ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ (@EnableTransactionManagement)

๋Œ€๋ถ€๋ถ„์˜ ๊ฐœ๋ฐœ์ž๋“ค์€ ์ด ๋ฐฉ์‹์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค. @Transactional์ด ๋‹ฌ๋ฆฐ ๋ฉ”์„œ๋“œ๋Š” ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ๊ฐ€ ๋˜๋Š”๊ฑฐ์ฃ .

@Service
public class UserService {
    @Transactional
    public Long registerUser(User user) {
        // SQL ์‹คํ–‰
        userDao.save(user);
        return id;
    }
}
  • ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ง€๋จผํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ์œ„ํ•ด์„œ๋Š” ์ปจํ…Œ์ด๋„ˆ ๊ฐ์ฒด์— @EnableTransactionManagement๋ฅผ ๋ถ™์—ฌ์•ผํ•ฉ๋‹ˆ๋‹ค.
    (์ตœ๊ทผ์—๋Š” ์Šคํ”„๋ง๋ถ€ํŠธ์˜ @AutoConfiguration์•ˆ์— ๊ธฐ๋ณธ ํฌํ•จ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ƒ๋žตํ•ด๋„ ๋™์ž‘ํ•˜๋Š”๊ฑฐ์ฃ .)
@Configuration
@EnableTransactionManagement
public class AppConfig {
    @Bean // ๋ชจ์–‘๋งŒ ๋‹ค๋ฅผ๋ฟ, 1๋ฒˆ ๋ฐฉ์‹๊ณผ ๋˜‘๊ฐ™์€ ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    public PlatformTransactionManager txManager() {
        return yourTxManager; // more on that later 
    }
}โ€‹
  • ๊ทธ๋Ÿฌ๋ฉด ์Šคํ”„๋ง์—์„œ๋Š” @Transactional ์–ด๋…ธํ…Œ์ด์…˜์ด ๋‹ฌ๋ฆฐ public ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•ด ์•„๋ž˜์™€ ๊ฐ™์€ ํŠธ๋žœ์žญ์…˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์‹คํ–‰ํ•ด์ค๋‹ˆ๋‹ค.
// ์‹ค์ œ ์Šคํ”„๋ง ๋‚ด๋ถ€์—์„œ ๋™์ž‘ํ•˜๋Š” JDBC ์ฝ”๋“œ
public class UserService {
    public Long registerUser(User user) {
        Connection connection = dataSource.getConnection();
        
        try (connection) {
            connection.setAutoCommit(false);
            connection.commit();
            
        } catch (SQLException e) {
            connection.rollback();
        }
    }
}

 

๋ฌผ๋ก  ์Šคํ”„๋ง์ด ํ”„๋ก์‹œ์— ํŠน์ • dataSource๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

PlatfromTxManager๋ฅผ ํ†ตํ•ด DIP๋ฅผ ์ ์šฉ์‹œ์ผœ๋†จ์ฃ . ๊ตฌํ˜„์ฒด๋ฅผ ์ž์œ ๋กญ๊ฒŒ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


# ์Šคํ”„๋ง์ด ํŠธ๋žœ์žญ์…˜ ์ฝ”๋“œ๋ฅผ ๋‚ด ๊ฐ์ฒด์— ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•

์ด์ œ ์‚ด์‚ด ๋Š๋‚Œ์ด ์˜ค์‹œ์ฃ ? ๋„ค. ์Šคํ”„๋ง์—์„œ๋Š” UserService ๊ฐ์ฒด์˜ ํ”„๋ก์‹œ์— ํŠธ๋žœ์žญ์…˜ ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž…ํ•ฉ๋‹ˆ๋‹ค. ์•ž์—์„œ ๋ฐฐ์šด ์Šคํ”„๋ง AOP๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ง์ด์ฃ .

 

๋‹ค๋งŒ ์—ฌ๊ธฐ์—์„œ ํ•œ๊ฐ€์ง€ ์ฐจ์ด์ ์€ ํ”„๋ก์‹œ ์ž์ฒด์—์„œ ํŠธ๋žœ์žญ์…˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฑด ์•„๋‹ˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์Šคํ”„๋ง AOP๋Š” ํ”„๋ก์‹œ ๊ฐ์ฒด์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด [Transaction Manager ๊ฐ์ฒด]์—๊ฒŒ ์œ„์ž„ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

  • [Transaction Manager ๊ฐ์ฒด]์—๋Š” doBegin() ๊ณผ doCommit() ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์œ„์ž„ํ•ฉ๋‹ˆ๋‹ค.
// ์ดํ•ด๋ฅผ ๋•๊ธฐ์œ„ํ•œ ํŠธ๋žœ์žญ์…˜๋งค๋‹ˆ์ € ๊ฐ์ฒด์˜ ์ฝ”๋“œ. ์‹ค์ œ ์ฝ”๋“œ๋Š” ํ›จ์”ฌ ๋ณต์žกํ•˜๋‹ค!
public class DataSourceTransactionManager implements PlatformTransactionManager {
    @Override
    protected void doBegin(Object transaction, TransactionDefinition definition) {
        //... ๋ณต์žกํ•œ ๊ณผ์ • ์ƒ๋žต
        Connection newCon = obtainDataSource().getConnection();
        newCon.setAutoCommit(false);
    }

    @Override
    protected void doCommit(DefaultTransactionStatus status) {
        //... ๋ณต์žกํ•œ ๊ณผ์ • ์ƒ๋žต
        Connection connection = status.getTransaction().getConnectionHolder().getConnection();
        try {
            con.commit();
        } catch (SQLException ex) {
            throw new TransactionSystemException("Could not commit JDBC transaction", ex);
        }
    }
}

 

@ ์ฆ‰ ์ •๋ฆฌํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. ์Šคํ”„๋ง์€ @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ๊ฐ€์ง„ ๋ฉ”์„œ๋“œ๋ฅผ ๋ฐœ๊ฒฌํ•˜๋ฉด, ๋‹ค์ด๋‚˜๋ฏน ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  2. ํ•ด๋‹น ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” [TransactionManager ๊ฐ์ฒด]์—๊ฒŒ ํŠธ๋žœ์žญ์…˜ ๋™์ž‘์„ ์œ„์ž„ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.
  3. ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋Š” ์ฒ˜์Œ์— ๋ดค๋˜ ๊ฒƒ ์ฒ˜๋Ÿผ JDBC ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ํŠธ๋žœ์žญ์…˜์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์Šคํ”„๋ง์˜ @Transcational์€ ๋งˆ๋ฒ•์˜ ํ‚ค์›Œ๋“œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ JDBC๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์Šคํ”„๋ง์—์„œ๋Š” @Transactional ํƒœ๊ทธ๋ฅผ ์ด์šฉํ•œ ๋…ผ๋ฆฌ์  ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•˜๊ณ , ์‹ค์ œ ๋ฌผ๋ฆฌ์  ํŠธ๋žœ์žญ์…˜์€ JDBC๋กœ ์ฒ˜๋ฆฌ๋œ๋‹ค๋ผ๊ณ  ํ‘œํ˜„ํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

 


# @Transactional์˜ Propagtion์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•

์‚ฌ์‹ค ํŠธ๋žœ์žญ์…˜์˜ isolation level์€ JDBC์—์„œ๋„ ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค๋งŒ ํŠธ๋žœ์žญ์…˜ ๋„์ค‘ isolation level์„ ์ „ํ™˜ํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด JDBC ๋“œ๋ผ์ด๋ฒ„์—์„œ ๊ธฐ๋Šฅ์ด ์ง€์›๋˜๋Š”์ง€ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. 

// isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED
connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);

 

๊ทธ๋ ‡๋‹ค๋ฉด Propagation์€ ๋Œ€์ฒด ์–ด๋–ป๊ฒŒ JDBC๋กœ ๋งŒ๋“  ๊ฒƒ์ผ๊นŒ์š”? ๋ชจ๋“ ๊ฒƒ์˜ ๋น„๋ฐ€์€ ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•œ Lazyํ•œ ๋™์ž‘์— ์žˆ์Šต๋‹ˆ๋‹ค.

์ถœ์ฒ˜&amp;amp;amp;nbsp;https://jeong-pro.tistory.com/228

 

# (๋ฒˆ์™ธ) @Transcational(readOnly = true)์˜ ๋™์ž‘

์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” @Transcational(readOnly=true)๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š” ๊ฑธ๊นŒ?

๋ฌผ๋ก  readOnly๋ฅผ ๋‹จ๋‹ค๊ณ  ํ•ด์„œ ํŠน๋ณ„ํ•˜๊ฒŒ ๋‹ค๋ฅธ ๋™์ž‘์ด ์ผ์–ด๋‚˜์ง„ ์•Š๋Š”๋‹ค.

์–ด์ฐจํ”ผ @Transcational์„ ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์— setAutoCommit์ด false๊ฐ€ ๋˜๋ฉด์„œ ์—ฌ๋Ÿฌ ์ž‘์—…์„ ํ•œ ํŠธ๋žœ์žญ์…˜์— ๋ฌถ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

// ์ดํ•ด๋ฅผ ๋•๊ธฐ์œ„ํ•œ ํŠธ๋žœ์žญ์…˜๋งค๋‹ˆ์ € ๊ฐ์ฒด์˜ ์ฝ”๋“œ. ์‹ค์ œ ์ฝ”๋“œ๋Š” ํ›จ์”ฌ ๋ณต์žกํ•˜๋‹ค!
public class DataSourceTransactionManager implements PlatformTransactionManager {
    @Override
    protected void doBegin(Object transaction, TransactionDefinition definition) {
        //... ๋ณต์žกํ•œ ๊ณผ์ • ์ƒ๋žต
        Connection newCon = obtainDataSource().getConnection();
        newCon.setAutoCommit(false);
    }
    ...
}

๋‹ค๋งŒ readOnly ๋ผ๋Š” ํžŒํŠธ๋ฅผ ์ฃผ๋ฉด, ์Šคํ”„๋ง์—์„œ ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•œ ์ถ”๊ฐ€์ ์ธ ์ž‘์—…์„ ์•ฝ๊ฐ„ ํ•œ๋‹ค.(์„ฑ๋Šฅํ–ฅ์ƒ์ด ํฌ์ง€๋Š” ์•Š๋‹ค.)

JDBC ๋“œ๋ผ์ด๋ฒ„์— ๋”ฐ๋ผ ๋‹ค๋ฅด์ง€๋งŒ. readOnly๋ผ๊ณ  ํŒ๋‹จ๋˜๋ฉด ์ฟผ๋ฆฌ๋ฅผ ์ตœ์ ํ™”ํ•˜๋Š” ๊ธฐ๋Šฅ๋„ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค. ์ฟผ๋ฆฌ ํžŒํŠธ ๊ฐ™์€ ๋Š๋‚Œ

 

ํŠนํžˆ JPA์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ, SpringDataJpa์—์„œ๋Š” readOnly๊ฐ€ ์„ค์ •๋˜์–ด์žˆ๋‹ค๋ฉด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ํ”Œ๋Ÿฌ์‹œํ•˜์ง€ ์•Š๋Š”๋‹ค.

์ฆ‰ ํ”Œ๋Ÿฌ์‹œ ๋ชจ๋“œ๊ฐ€ ๊ฐ•์ œ๋กœ Manual๋กœ ๋ฐ”๋€Œ์–ด๋ฒ„๋ฆฐ๋‹ค. (์ฐธ๊ณ ๋กœ Manual์˜ ๊ณผ๊ฑฐ์ด๋ฆ„์€ Never์˜€๋‹ค. ํ”Œ๋Ÿฌ์‹œ๋ฅผ ํ•˜์ง€ ์•Š์Œ.)

entityManager.setFlushMode(FlushModeType.MANUAL);

MANUAL = ๋‚œ ํ”Œ๋Ÿฌ์‹œ๋ฅผ ์•ˆํ•œ๋‹ค. ๋‹ˆ๊ฐ€ ์•Œ์•„์„œ ์ˆ˜๋™์œผ๋กœ ํ•ด๋ผ

 

์ข€ ๋” ์‰ฝ๊ฒŒ๋งํ•˜๋ฉด, ์ฝ๊ธฐ ์ž‘์—…๋งŒ ๊ฐ€๋Šฅํ•  ๋ฟ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ˆ˜์ •๋‚ด์—ญ์„ ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค.

 

(2022.08.15 ์ถ”๊ฐ€)

* ์ฐธ๊ณ ๋กœ ๋”ํ‹ฐ์ฒดํ‚น๊ณผ flush๋ฅผ ์•ˆํ•œ๋‹ค๊ณ  ํ–ˆ์ง€, ํŠธ๋žœ์žญ์…˜์˜ readOnly๊ฐ€ ์ˆ˜์ •/์‚ญ์ œ๋ฅผ ๋ง‰๋Š” ๊ธฐ๋Šฅ์ด ์žˆ์ง€๋Š” ์•Š๋‹ค.
save()๋ฅผ ์‹คํ–‰ํ•ด๋„ ์ž˜ ๋™์ž‘ํ•˜๊ณ , updated์˜ ๊ฒฝ์šฐ์—๋„ ์ง์ ‘ flush ์‹œ์ผœ์ฃผ๋ฉด ์‹ค์ œ DB์— ์ฟผ๋ฆฌ๊ฐ€ ์ฐํžŒ๋‹ค.

 

์•ฝ๊ฐ„ ๋น„์Šทํ•œ ๋งฅ๋ฝ์œผ๋กœ, @Transcational( SUPPORTS )๋กœ ์„ค์ •ํ•˜๋ฉด ์ถ”๊ฐ€๋˜๋Š” ํŠธ๋žœ์žญ์…˜ ๊ด€๋ จ JDBC ์ฝ”๋“œ๋Š” ์•„๋ฌด๊ฒƒ๋„ ์—†๋‹ค.

๊ทธ๋ž˜์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆœ์ˆ˜ JDBC ์ฝ”๋“œ๋ฅผ ์„ž์–ด์„œ ์จ๋„ ์ถฉ๋Œ(์˜ˆ์™ธ)์—†์ด ์ •์ƒ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ „์†ก๋จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

@Transactional(readOnly = true, propagation=Propagation.SUPPORTS)
public long insertTrade(TradeData trade) throws Exception {
   //JDBC Code...
}

 

๊ฒฐ๊ตญ ์ „๋ถ€๋‹ค ์ž๋ฐ”์™€ JDBC ์ฝ”๋“œ๋กœ ๋Œ์•„๊ฐ€๋Š”๊ฑฐ๋ผ์„œ ๊ทธ๋ ‡๋‹ค. ๋‚ด๋ถ€๋™์ž‘์„ ์•Œ๊ณ ์žˆ๋‹ค๋ฉด ํฌ๊ฒŒ ๋†€๋ผ์šด ๋ถ€๋ถ„๋„ ์•„๋‹ˆ๋‹ค.


# Spring๊ณผ JPA์—์„œ ๊ฐ™์€ @Transactional ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ด์œ 

์–ด? ์ƒ๊ฐํ•ด๋ณด๋ฉด ์ด์ƒํ•ฉ๋‹ˆ๋‹ค. ์Šคํ”„๋ง์—์„œ๋„ @Transactional ํƒœ๊ทธ๋ฅผ ํ†ตํ•œ ํ”„๋ก์‹œ ์ƒ์„ฑ์ด ์žˆ์ง€๋งŒ, JPA์—๋„ ๋…๋ฆฝ์ ์œผ๋กœ ํ•ด๋‹น @Transactional ํƒœ๊ทธ๊ฐ€ ์กด์žฌํ•˜๊ณ , ๋‹ค๋ฅธ ์ฒ˜๋ฆฌ๋ฐฉ์‹์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๋Š” [์Šคํ”„๋ง + JPA] ์กฐํ•ฉ์„ ๋ฌธ์ œ์—†์ด ์‚ฌ์šฉํ•˜๋Š”๋ฐ ์–ด๋–ป๊ฒŒ ๋™๊ธฐํ™”ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ผ๊นŒ์š”?

  • ํ•˜์ด๋ฒ„๋„ค์ดํŠธ(JPA)๋Š” ์Šคํ”„๋ง์˜ @Transactional์˜ ๋™์ž‘๋ฐฉ์‹์„ ๋ชจ๋ฆ…๋‹ˆ๋‹ค.
  • ์Šคํ”„๋ง์€ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ(JPA)๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” @Transcational์˜ ๋™์ž‘๋ฐฉ์‹์„ ๋ชจ๋ฆ…๋‹ˆ๋‹ค.
public class UserService {
    @Autowired 
    private SessionFactory sessionFactory;
    
    // ํ•˜์ด๋ฒ„๋„ค์ดํŠธ(JPA)์˜ ์ฝ”๋“œ
    public void registerUser(User user) {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        session.save(user);
        session.getTransaction().commit();
        session.close();
    }
}

 

๋†€๋ผ์šด ์‚ฌ์‹ค์€, ์‚ฌ์‹ค ๋ณ„ ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค๋Š” ๊ฒ๋‹ˆ๋‹ค. ์ด๋ฏธ ์Šคํ”„๋ง์—์„œ JPA์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ž๋™ํ™” ํ•ด๋†“์•˜์Šต๋‹ˆ๋‹ค. ์ด๊ฒŒ ์–ด๋ ต์ง€ ์•Š๊ฒŒ ๊ฐ€๋Šฅํ•œ ๊ฒƒ์€ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋˜ ์Šคํ”„๋ง์ด๋˜ ๊ฒฐ๊ตญ ๋‘˜ ๋‹ค JDBC์˜ ๊ธฐ๋ณธ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ด์ฃ .

์ถ”์ƒํ™”ํ•ด์„œ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”๊ฑฐ์ง€ ๋งˆ์ˆ ์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋ชจ๋“ ๊ฒƒ์€ JDBC๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค

 


# Spring๊ณผ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์—์„œ ์ œ๊ณตํ•˜๋Š” SessionFactory๋ฅผ ์Šคํ”„๋ง์—์„œ ์‚ฌ์šฉํ•˜๊ณ , @Transactional๋ฅผ ๋‹ฌ์•„๋ด…์‹œ๋‹ค.์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด ์Šคํ”„๋ง์—์„œ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜์ง€ ์•Š๊ณ , ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ๊ด€๋ฆฌํ•˜๋„๋ก ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

@Service
public class UserService {
    @Autowired 
    private SessionFactory sessionFactory;

    @Transactional // ์Šคํ”„๋ง๊ณผ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ๊ฐ™์€ ํƒœ๊ทธ ์‚ฌ์šฉ.
    public void registerUser(User user) {
        sessionFactory.getCurrentSession().save(user);
    }
}

์›๋ฆฌ๋Š” ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ์Šคํ”„๋ง์—์„œ ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค ๋•Œ ์Šคํ”„๋ง์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํŠธ๋žœ์žญ์…˜๋งค๋‹ˆ์ € (PlatformTransactionManager)์—๊ฒŒ ์œ„์ž„ํ•˜์ง€ ์•Š๊ณ  ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์˜ ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ € (HibernateTransactionManager)์—๊ฒŒ ์œ„์ž„ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ JPA๋ฅผ ํ†ตํ•ด์„œ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ € ๊ฐ์ฒด๋กœ (JpaTransactionManager)๋ฅผ ์“ฐ๋ฉด ๋˜์ฃ .

 

์ด๋Š” ์›๋ž˜ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ํŠธ๋žœ์žญ์…˜๋งค๋‹ˆ์ € ๊ฐ์ฒด๋ฅผ ์ ์ ˆํ•˜๊ฒŒ ์ง€์ •ํ•ด์ฃผ์–ด์•ผํ–ˆ์ง€๋งŒ, ์ง€๊ธˆ์€ ์Šคํ”„๋ง ๋ถ€ํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (spring-boot-starter-data-jpa)๋ฅผ ์“ฐ๋ฉด ์ž๋™์œผ๋กœ Jpa ํŠธ๋žœ์žญ์…˜๋งค๋‹ˆ์ €๋ฅผ ์“ฐ๋„๋ก ์„ค์ •์„ ๋ฐ”๊ฟ”์ค๋‹ˆ๋‹ค.

 

์Šคํ”„๋ง ๋‚ด๋ถ€ ์ฝ”๋“œ๋ฅผ ๋ณด์ง€์•Š์•˜์ง€๋งŒ, ์‚ด์ง ๊ฐ์ด์˜ค์‹œ์ง€ ์•Š๋‚˜์š”? [ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ € ๊ฐ์ฒด] ๋˜ํ•œ IoC๊ฐ€ ๊ตฌํ˜„๋˜์–ด ์œ ์—ฐํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์„๊ฑฐ๋ผ๋Š” ๋Š๋‚Œ์ด์š”. ์‹ค์ œ๋กœ ์Šคํ”„๋ง์€ PlatformTransactionManager ๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค์—๋งŒ ์˜์กดํ•˜๊ณ  ์žˆ๊ณ , ๊ทธ ๊ตฌํ˜„์ฒด๋Š” ์œ ๋™์ ์œผ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์Šคํ”„๋ง์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ตฌํ˜„์ฒด๋Š” DataSourceTransactionManager ์ž…๋‹ˆ๋‹ค.

 


# ๊ฒฐ๋ก 

๋งˆ๋ฒ•์ด ์•„๋‹™๋‹ˆ๋‹ค. ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋ฅผ ์“ฐ๋˜ ์Šคํ”„๋ง์„ ์“ฐ๋˜ @Transactional์„ ์“ฐ๋˜ ๊ฒฐ๊ตญ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐ์ž‘์€ JDBC์˜ ๊ธฐ๋ณธ์ฝ”๋“œ (getConnection, setAutoCommit, Commit)์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. 

 

๋‹ค๋งŒ ์ด์ฒ˜๋Ÿผ ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ƒ๊ธฐ๋Š” ์Šคํ”„๋ง์˜ ํŠธ๋žœ์žญ์…˜ ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ด๋Š” ๋‹ค๋ฅธ ๊ธ€์—์„œ ๋”ฐ๋กœ ๋‹ค๋ค„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. 2021.08.18 - [Backend/Spring Core] - DB์™€ @Transactional์„ ์‚ฌ์šฉํ• ๋•Œ ์ž์ฃผ ๋‚˜์˜ค๋Š” ์‹ค์ˆ˜

 

DB์™€ @Transactional์„ ์‚ฌ์šฉํ• ๋•Œ ์ž์ฃผ ๋‚˜์˜ค๋Š” ์‹ค์ˆ˜

DB๋ฅผ ์‚ฌ์šฉํ• ๋•Œ ํ”ํžˆํ•˜๋Š” ์ฝ”๋”ฉ์‹ค์ˆ˜, ์œ ํŠœ๋ธŒ ์„ค๋ช…์ด ๋‚˜์€ ๊ฒƒ ๊ฐ™์•„์„œ ๊ธ€์€ ๋Œ€์ถฉ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค :) https://www.youtube.com/watch?v=n85UzIReFjY 1. Primary , Replica (Master-Slave ์šฉ์–ด๊ฐ€ ์˜๋ฏธ๋„ ์ด์ƒํ•˜๊ณ , ํ‘..

jiwondev.tistory.com

 

 

 


# ๋ ˆํผ๋Ÿฐ์Šค

https://www.marcobehler.com/guides/spring-transaction-management-transactional-in-depth?fbclid=IwAR1PsHPKHyLGmiKORaTsvXV6EwIwe5f2RTCkz52QLZFnDdI7QzArXLil4PQ
https://jeong-pro.tistory.com/228

 

 

# 2022.03.01 ์ถ”๊ฐ€

๐Ÿ“ŒSpring ์€ @Transactional ์–ด๋…ธํ…Œ์ด์…˜ ๋ถ™์ธ ๋ฉ”์†Œ๋“œ๋ฅผ ์–ด๋–ป๊ฒŒ ์ฐพ๊ณ  ํŠธ๋žœ์žญ์…˜์„ ์ฒ˜๋ฆฌํ•˜๋‚˜์š”? ๊ทธ ๋‚ด๋ถ€ ๊ตฌํ˜„์„ ์ƒ์„ธํ•˜๊ฒŒ ์„ค๋ช…ํ•ด ์ฃผ์„ธ์š”.

์ผ๋‹จ @Transcational์€ ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์˜ ๋Œ€์ƒ์ด ์•„๋‹™๋‹ˆ๋‹ค.

์ด๋Š” @EnableTranscationManagement ๋ฅผ ๊ธฐ์ ์œผ๋กœ, ํ•˜์œ„ ํŒจํ‚ค์ง€๋“ค์„ ํƒ์ƒ‰ํ•œ๋‹ค.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({TransactionManagementConfigurationSelector.class})
public @interface EnableTransactionManagement {
    boolean proxyTargetClass() default false;
    AdviceMode mode() default AdviceMode.PROXY;
    int order() default 2147483647;
}

@Import(TransactionManagementConfigurationSelector.class) โžก Selector๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค.
Selector๋Š” 3๊ฐ€์ง€ ์ข…๋ฅ˜์˜ @Transcational ์–ด๋…ธํ…Œ์ด์…˜(javax, spring, aspectj)๋ฅผ ๋ถ„๋ฅ˜ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
    public TransactionManagementConfigurationSelector() {
    }

    protected String[] selectImports(AdviceMode adviceMode) {
        switch(adviceMode) {
        case PROXY:
            return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
        case ASPECTJ:
            return new String[]{this.determineTransactionAspectClass()};
        default:
            return null;
        }
    }

    private String determineTransactionAspectClass() {
        return ClassUtils.isPresent("javax.transaction.Transactional", this.getClass().getClassLoader()) ? "org.springframework.transaction.aspectj.AspectJJtaTransactionManagementConfiguration" : "org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration";
    }
}

@EnableTranscationManagement๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ PlatformTxManager ํƒ€์ž…์œผ๋กœ ๋“ฑ๋ก๋œ ๋นˆ์„ ์ฐพ์•„์„œ ์‚ฌ์šฉํ•œ๋‹ค.

์Šคํ”„๋ง๋ถ€ํŠธ๋Š” DataSourceTxManager๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ๋“ฑ๋กํ•ด์ฃผ๊ณ , SpringDataJPA ์‚ฌ์šฉ์‹œ JpaDataSourceTxManager๋ฅผ ๋“ฑ๋กํ•œ๋‹ค. ๋งŒ์•ฝ TranscationTemplate์ฒ˜๋Ÿผ, ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €์˜ ๊ตฌํ˜„์ฒด๋ฅผ ๋ฐ”๊พธ๊ณ  ์‹ถ๊ฑฐ๋‚˜, ์—ฌ๋Ÿฌ๊ฐœ๋ฅผ ์“ด๋‹ค๋ฉด ์ง์ ‘ ์ง€์ •ํ•˜๋ฉด ๋œ๋‹ค.

 

์„ค์ • ๊ฐ์ฒด์— TranscationConfigurer ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

@Configuration
@EnableTransactionManagement
public class DBConfig implements TransactionManagementConfigurer {

 // ์ƒ๋žต...

 @Override
 public PlatformTransactionManager annotationDrivenTransactionManager() {
     return transactionManger();
 }

 @Bean
 public PlatformTransactionManager transactionManger() {
     return new DataSourceTransactionManager(dataSource());
 }

์ฐธ๊ณ ๋กœ ์Šคํ”„๋ง4.2๋ถ€ํ„ฐ๋Š” ์Šคํ”„๋ง ์–ด๋…ธํ…Œ์ด์…˜์ธ @Transcational("๋นˆ ์ด๋ฆ„")์œผ๋กœ๋„ ๊ตฌํ˜„์ฒด๋ฅผ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค.

public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";
    ... ์ƒ๋žต
}

์ดํ›„ @Transcational์ด ์ ์šฉ๋œ ํด๋ž˜์Šค๋ฅผ ํ”„๋ก์‹œ๋กœ ๋ฐ”๊ฟ”, PlatformTxmanager๋ฅผ ์ฃผ์ž…ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๊ณต์‹๋ฌธ์„œ์—๋„ ๋‚˜์™€์žˆ์ง€๋งŒ PlatformTxManager๋Š” ๋ฉ”์„œ๋“œ๋‚ด์—์„œ ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ๋ฅผ ๋งŒ๋“ค์–ด ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ์€ ํŠธ๋žœ์žญ์…˜์„ ๊ฑธ์–ด์ฃผ์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•œ๋‹ค. (= ๊ทธ ์Šค๋ ˆ๋“œ๋Š” ๋กค๋ฐฑ ์•ˆ๋จ) ์–ด์ฐŒ๋ณด๋ฉด ๋‹น์—ฐํ•œ ์‚ฌ์‹ค.

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ