여러 데이터 베이스 사용 시 Transaction

2024. 7. 10. 09:01Spring Boot

보통 서비스에 @Transactional이라는 어노테이션을 붙여서 오류시 롤백하는 트랙잭션을 구현한다.

 

다만 보통은 어노테이션만 붙이면 바로 구현이되나

DB를 여러개 연결하여 연결을 직접 설정한 경우에는

TransactionManager를 따로 지정 해 주어야 한다

 

이번 예제에는 MySQL을 직접 설정한 경우의 예시를 보여주겠다.

 

package com.example.demo.mySQL;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionManager;

import java.util.*;

@EnableJpaRepositories(
    basePackages = "com.example.demo.mySQL",
    entityManagerFactoryRef = "mySQLManagerFactory",
    transactionManagerRef = "mySQLTransactionManager"
)
@Configuration
@PropertySource("classpath:mySQL.properties")
public class mySQLConfig {

    @Value("${url}")
    private String url;

    @Value("${name}")
    private String name;

    @Value("${password}")
    private String password;

    @Bean
    public DataSource mySQLSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl(url);
        dataSource.setUsername(name);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean mySQLManagerFactory() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(mySQLSource());
        em.setPackagesToScan(new String[] {"com.example.demo.mySQL"});

        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        adapter.setShowSql(true);
        adapter.setGenerateDdl(true);
        em.setJpaVendorAdapter(adapter);

        HashMap<String, Object> prop = new HashMap<>();
        prop.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
        prop.put("hibernate.hbm2ddl.auto", "update");
        prop.put("hibernate.format_sql", true);
        em.setJpaPropertyMap(prop);

        return em;
    }
    

    @Bean
    @Qualifier("myTran")
    public PlatformTransactionManager mySQLTransactionManager() {
        JpaTransactionManager manager = new JpaTransactionManager();
        manager.setEntityManagerFactory(mySQLManagerFactory().getObject());
        return manager;
    }

    @Qualifier("myTemplate")
    @Bean
    public JdbcTemplate myTemplate() {
        return new JdbcTemplate(mySQLSource());
    }
}

 

PlatformTransactionaManager에 @Qualifier를 이용하여 식별자를 추가 해 주었다.

 

package com.example.demo.mySQL.transaction;

import javax.sql.DataSource;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import jakarta.annotation.Resource;

@Repository
public interface tranRepository extends JpaRepository<tranTest, Long> {
}

 

 

package com.example.demo.mySQL.transaction;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class tranService {
    
    @Autowired
    tranRepository repo;


    @Transactional(transactionManager = "myTran")
    public void test(String name) {

        repo.save(new tranTest(name));
        if(name.equals("error")) {
            log.info(name);
            throw new RuntimeException("testing");
        }
    }
}

 

그 후 해당 연결을 이용하는 repository의 service의 method에 @Transactional을 붙이고

transactioanManager에 설정한 식별자를 넣어주었다.

 

해당 메소드는 이름으로 error가 들어오면 에러를 발생시키는 메소드이다

 

package com.example.demo.mySQL.transaction;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import lombok.extern.slf4j.Slf4j;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Slf4j
@RestController
@RequestMapping("/mysql/tran")
public class tranController {
    @Autowired
    tranService service;


    @GetMapping("/test")
    public String getMethodName(@RequestParam String name){
        try {
            service.test(name);
            return name;
        
        } catch (Exception e){
            return "input is error";
        }
        
    }
    
}

 

간단한 controller를 만들어서 테스트를 해보면

 

 

name으로 error를 넘겨줄 시 save함수가 실행 되었으나 rollback되어서 database에 저장이 안 되었음을 알 수 있다.

 

 

 

 

 

 

 

 

error말고 다른 값을 넣으면 정상적으로 commit되어서 값이 잘 들어감도 확인해 보았다.

'Spring Boot' 카테고리의 다른 글

Lazy Loading  (0) 2024.07.31
Spring AOP  (0) 2024.07.10
R2DBC  (0) 2024.06.17
Spring WebFlux  (0) 2024.03.18
Spring WebClient  (0) 2024.03.10