Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

탐구개발

MySQL과 JPA를 사용하는 master, slave 설정 - @Transactional 방식 본문

프로젝트

MySQL과 JPA를 사용하는 master, slave 설정 - @Transactional 방식

탐구개발자 2023. 11. 12. 18:49

Docker MySQL 1대 추가 -  3307포트

docker run --name mysql-container-slave -e MYSQL_ALLOW_EMPTY_PASSWORD=true -d -p 3307:3306 mysql:8.0.22

 

MySQL 데이터베이스 서버간의 Replication 설정은 따로 하지 않았고
데이터베이스 서버 2대를 띄워놓기만 했습니다.

 

Application.java 수정

spring:
  datasource:
    master:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: "jdbc:mysql://localhost:3306/somoim_db?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC&tinyInt1isBit=false"
      username: somoim_user
      password: (password)
    slave:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: "jdbc:mysql://localhost:3307/somoim_db?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC&tinyInt1isBit=false"
      username: somoim_user
      password: (password)
    hikari:
      auto-commit: false
      connection-test-query: SELECT 1
      minimum-idle: 10
      maximum-pool-size: 50
      transaction-isolation: TRANSACTION_READ_UNCOMMITTED
      pool-name: pool-jiniworld
  jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    hibernate:
      ddl-auto: create
      naming:
        implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
        physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
    properties:
      hibernate:
        format_sql: true
        use_sql_comments: true
    open-in-view: false
    show-sql: true

logging:
  level:
    org:
      hibernate:
        type:
          descriptor:
            sql: trace

 

DataSourceConfiguration.java

@Configuration
public class DataSourceConfiguration {

    public static final String MASTER_DATASOURCE = "masterDataSource";
    public static final String SLAVE_DATASOURCE = "slaveDataSource";

    @Bean(MASTER_DATASOURCE)
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create()
            .type(HikariDataSource.class)
            .build();
    }

    @Bean(SLAVE_DATASOURCE)
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create()
            .type(HikariDataSource.class)
            .build();
    }

    @Bean
    @DependsOn({MASTER_DATASOURCE, SLAVE_DATASOURCE})
    public DataSource routingDataSource(
            @Qualifier(MASTER_DATASOURCE) DataSource masterDataSource,
            @Qualifier(SLAVE_DATASOURCE) DataSource slaveDataSource) {

        RoutingDataSource routingDataSource = new RoutingDataSource();

        Map<Object, Object> datasourceMap = new HashMap<>() {
            {
                put("master", masterDataSource);
                put("slave", slaveDataSource);
            }
        };

        routingDataSource.setTargetDataSources(datasourceMap);
        routingDataSource.setDefaultTargetDataSource(masterDataSource);

        return routingDataSource;
    }

    @Bean
    @Primary
    @DependsOn("routingDataSource")
    public LazyConnectionDataSourceProxy dataSource(DataSource routingDataSource){
        return new LazyConnectionDataSourceProxy(routingDataSource);
    }

}

 

RoutiongDataSource.java

public class RoutingDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) ? "slave" : "master";
    }

}

 

 

Service.java 수정

@Service
@RequiredArgsConstructor
public class MeetingService {

    private final MeetingRepository meetingRepository;

    private final MeetingConverter meetingConverter;

    @Transactional(readOnly = true)
    public List<Meeting> getMeetings() {
        return meetingRepository.findAll();
    }

    @Transactional
    public Meeting saveMeeting(MeetingSaveDto meetingSaveDto) {
        Meeting meeting = meetingConverter.from(meetingSaveDto);
        return meetingRepository.save(meeting);
    }

}