DB ์ด์คํ๋ฅผ ๊ตฌ์ถํ๋ค๋ฉด ์ดํ๋ฆฌ์ผ์ด์ ์์๋ ์ฝ๊ธฐ ์ ์ฉ DB๋ฅผ ๊ตฌ๋ถํด์ ์ฟผ๋ฆฌ๋ฅผ ๋ถ์ฐํด์ผ ํ๋ค. ๋จผ์ ์ฟผ๋ฆฌ ๋ถ์ฐ์ ๋ํ ์ ์ฑ ์ ์ค์ ํด์ผ ํ๋๋ฐ, ๋ค์๊ณผ ๊ฐ์ ํํ๋ก ์ฟผ๋ฆฌ ๋ถ์ฐ์ ์์ผ๋ณผ ์์ ์ด๋ค.
๊ท์น 1. @RouteDataSource(type)์ ์ด์ฉํด DB๋ฅผ ๊ฒฐ์ (type: MASTER | SLAVE)
๊ท์น 2. @Transactional(readOnly)์ ์ด์ฉํด DB๋ฅผ ๊ฒฐ์ (readOnly: true | false)
@RouteDataSource(MASTER)
public service() {
// ...
}
@Transactional(readOnly=true)
public service() {
// ...
}
Datasource ์ ๋ณด ๋ฑ๋ก
๋จผ์ yaml ํ์ผ ์์ฑ์ ํด์ผ ํ๋๋ฐ, ํน๋ณํ ๊ฒ์ ์๊ณ Master, Slave์ ๋ํ DataSource ์ ๋ณด๋ฅผ ์์ฑํด์ฃผ๋ฉด ๋๋ค.
`application.yml`
spring:
datasource:
master:
hikari:
username: ${MASTER_USERNAME}
password: ${MASTER_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: ${MASTER_JDBC_URL}
slave:
hikari:
username: ${SLAVE_USERNAME}
password: ${SLAVE_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: ${SLAVE_JDBC_URL}
์ฐธ๊ณ ๋ก jdbc-url์ `jdbc:mysql://{RDS ์๋ํฌ์ธํธ}/{DB ์ด๋ฆ}` ํ์์ผ๋ก ์์ฑํ๋ฉด๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋์ Master DB์ ํต์ ํ Bean ๊ฐ์ฒด์ Slave DB์ ํต์ ํ Bean ๊ฐ์ฒด๋ฅผ ๋ฑ๋กํด์ผ ํ๋ค.
`DataSourceConfig.java`
package com.anchor.global.db;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
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.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
public class DataSourceConfig {
private final String MASTER_DATASOURCE = "masterDataSource";
private final String SLAVE_DATASOURCE = "slaveDataSource";
private final String ROUTING_DATASOURCE = "routingDataSource";
private final String LAZY_ROUTING_DATASOURCE = "lazyRoutingDataSource";
private final HibernateProperties hibernateProperties;
private final JpaProperties jpaProperties;
public DataSourceConfig(JpaProperties jpaProperties) {
this.jpaProperties = jpaProperties;
this.hibernateProperties = new HibernateProperties();
hibernateProperties.setDdlAuto("create-drop");
}
/*
* yml ํ์ผ์ ์ ์ํ Master DB ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ DataSource Bean ๋ฑ๋ก
*/
@Bean(MASTER_DATASOURCE)
@ConfigurationProperties(prefix = "spring.datasource.hikari.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create()
.build();
}
/*
* yml ํ์ผ์ ์ ์ํ Slave DB ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ DataSource Bean ๋ฑ๋ก
*/
@Bean(SLAVE_DATASOURCE)
@ConfigurationProperties(prefix = "spring.datasource.hikari.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create()
.build();
}
@DependsOn({MASTER_DATASOURCE, SLAVE_DATASOURCE})
@Bean(ROUTING_DATASOURCE)
public AbstractRoutingDataSource routingDataSource(
@Qualifier(MASTER_DATASOURCE) DataSource masterDataSource,
@Qualifier(SLAVE_DATASOURCE) DataSource slaveDataSource
) {
AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() {
@Override
protected Object determineCurrentLookupKey() {
return RouteDataSourceManager.getCurrentDataSourceName();
}
};
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(RouteDataSource.DataSourceType.MASTER, masterDataSource);
targetDataSources.put(RouteDataSource.DataSourceType.SLAVE, slaveDataSource);
routingDataSource.setTargetDataSources(targetDataSources);
routingDataSource.setDefaultTargetDataSource(masterDataSource);
return routingDataSource;
}
@DependsOn(ROUTING_DATASOURCE)
@Bean(LAZY_ROUTING_DATASOURCE)
public LazyConnectionDataSourceProxy lazyRoutingDataSource(
@Qualifier(ROUTING_DATASOURCE) DataSource routingDataSource) {
return new LazyConnectionDataSourceProxy(routingDataSource);
}
@Bean
public EntityManagerFactoryBuilder entityManagerFactoryBuilder() {
return new EntityManagerFactoryBuilder(new HibernateJpaVendorAdapter(), new HashMap<>(), null);
}
@DependsOn(LAZY_ROUTING_DATASOURCE)
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier(LAZY_ROUTING_DATASOURCE) DataSource lazyRoutingDataSource) {
var props = hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(),
new HibernateSettings());
return builder
.dataSource(lazyRoutingDataSource)
.packages("com.anchor.domain.**.domain")
.properties(props)
.persistenceUnit("common")
.build();
}
@DependsOn(LAZY_ROUTING_DATASOURCE)
@Bean
public JdbcTemplate jdbcTemplate(@Qualifier(LAZY_ROUTING_DATASOURCE) DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean
public PlatformTransactionManager transactionManager(
EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
@Bean
public JPAQueryFactory JPAQueryFactory(EntityManager entityManager) {
return new JPAQueryFactory(entityManager);
}
}
์์ฑํด์ผ ํ๋ ํด๋์ค๋ค์ด ๋ ์๋๋ฐ, ํต์ฌ์ ์ธ ํด๋์ค์ ๋ํ ์ค๋ช ๊ณผ ํจ๊ป ์๋์ ์ฝ๋๋ฅผ ์ถ๊ฐํ๊ฒ ๋ค.
๊ด๋ จ ํด๋์ค ๋ถ์
1. DataSourceConfig
Spring Data JPA์ Auto Configuration์ ์ฌ์ฉํ์ง ์๊ณ ๋ณ๋์ ์ค์ ์ ํ๊ธฐ ์ํ ์ค์ ํด๋์ค์ด๋ค.
2. AbstractRoutingDataSource
DataSource์ ๋ํ Routing ์ ์ฑ ์ ๊ฒฐ์ ํ๋ ์ญํ ์ ํ๋ ์ถ์ ํด๋์ค์ด๋ค. ๊ฐ์ฅ ๋จผ์ determineCurrentLookupKey()๋ฅผ ์ ์ํด์ผ ํ๋๋ฐ Lookup Key๋ DataSource๋ฅผ ๊ฒฐ์ ํ๋ ์๋ณ์ ์ญํ ์ ํ๋ค. ์ฆ, determineCurrentLookupKey()๋ฅผ ์ด๋ป๊ฒ ์ ์ํ๋๋์ ๋ฐ๋ผ ์ฌ์ฉํ DB๋ฅผ ์ ํํ๋ ๋ฐฉ๋ฒ์ด ๋ฌ๋ผ์ง๋ค.
์ดํ setTaretDataSources()๋ฅผ ํตํด LookupKey์ DataSoucre Bean์ ๋ฑ๋กํด์ฃผ๋ฉด initialize()๋ฅผ ํตํด resolvedDataSources๋ก ๋ณต์ฌ๋๋ค.
JDBC Connection์ ํ์๋ก ํ ๋ determineTargetLookupKey()์ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ DataSource๊ฐ ๋ฐํ๋๋ค.
3. RouteDataSourceManager +
`RouteDataSourceManager`
package com.anchor.global.db;
import static com.anchor.global.db.RouteDataSource.DataSourceType.MASTER;
import static com.anchor.global.db.RouteDataSource.DataSourceType.SLAVE;
import org.springframework.transaction.support.TransactionSynchronizationManager;
public class RouteDataSourceManager {
private static final ThreadLocal<RouteDataSource.DataSourceType> currentDataSourceName = new ThreadLocal<>();
public static RouteDataSource.DataSourceType getCurrentDataSourceName() {
if (currentDataSourceName.get() == null) {
return TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? SLAVE : MASTER;
}
return currentDataSourceName.get();
}
public static void setCurrentDataSourceName(RouteDataSource.DataSourceType dataSourceType) {
currentDataSourceName.set(dataSourceType);
}
public static void removeCurrentDataSourceName() {
currentDataSourceName.remove();
}
}
Routing ์ ์ฑ ์ ์ค์ ๋ก ์ ์ํ ์ปค์คํ ํด๋์ค์ด๋ค. DB ํ์ ์ ์ธํ ํ๊ณ ์ง์ธ ์ ์๋ ๋ฉ์๋๋ฅผ ํฌํจํ๊ณ ์์ผ๋ฉฐ, ThreadLocal์ ํตํด ํด๋น ๊ฐ์ ์ ์ฅํ ์ ์๋ค. ThreadLocal์ ์ค๋ ๋์๊ฒ ๋ ๋ฆฝ์ ์ผ๋ก ๊ฐ์ ๊ด๋ฆฌํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ํด๋์ค์ด๋ค. ์์ฒญ ์ค๋ ๋๋ณ๋ก ํธ์ถ๋ ๋ฉ์๋์ DataSource ํ์ ์ด ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ThreadLocal์ ํตํด DataSourceType์ ๋ ๋ฆฝ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์๋ค.
`RouteDataSource`
package com.anchor.global.db;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RouteDataSource {
DataSourceType dataSourceType();
enum DataSourceType {
MASTER, SLAVE
}
}
๋ค์์ผ๋ก ์๋น์ค ๋ฉ์๋์ DB๋ฅผ ๊ตฌ๋ถํ๊ธฐ ์ํด ์ปค์คํ ์ด๋ ธํ ์ด์ ๊ณผ DB ํ์ ์ ์ ์ํด์คฌ๋ค.
`RouteDataSourceAspect`
package com.anchor.global.db;
import static com.anchor.global.db.RouteDataSource.DataSourceType.MASTER;
import static com.anchor.global.db.RouteDataSource.DataSourceType.SLAVE;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class RouteDataSourceAspect {
@Before("@annotation(com.anchor.global.db.RouteDataSource) && @annotation(target)")
public void setDataSource(RouteDataSource target) throws Exception {
if (target.dataSourceType() == MASTER
|| target.dataSourceType() == SLAVE) {
RouteDataSourceManager.setCurrentDataSourceName(target.dataSourceType());
} else {
throw new Exception("Wrong DataSource Type : Should Check Exception");
}
}
@After("@annotation(com.anchor.global.db.RouteDataSource)")
public void clearDataSource() {
RouteDataSourceManager.removeCurrentDataSourceName();
}
}
๊ทธ๋ฆฌ๊ณ ๋์ ์ผ๋ก ์๋น์ค ๋ฉ์๋ ์์ ๋ถ์ DB ํ์ ์ ํฌํจํ๋ ์ด๋ ธํ ์ด์ ์ ํ๋ํ๊ณ , ์ด๋ฅผ RouteDataSourceManager์ ๋ฑ๋กํ ์ ์๋๋ก Spring AOP๋ฅผ ํ์ฉํ ํด๋์ค๋ฅผ ์ ์ํ๋ค.
4. LazyConnectionDataSourceProxy
LazyConnectionDataSourceProxy๋ ์ด๋ฆ ๊ทธ๋๋ก DataSource์ ๋ํ Connection์ ๋์ค์ ๊ฐ์ ธ์ค๋๋ก ํ๋ ํ๋ก์ ํด๋์ค์ด๋ค. Connection ํ๋์ ๋ฏธ๋ค์ผ ๋๋ ์ด์ ๋ LazyConnectionDataSourceProxy์์ด ๋ฉ์๋๋ฅผ ํธ์ถํจ์ผ๋ก์จ ํ์ธํ ์ ์๋ค.
์ ์ฉ ์
์๋๋ ๋๋ฒ๊น ์ ํตํด CallStack์ ํธ์ถ๋ ๋ฉ์๋ ์์์ด๋ค.
1. Transaction์ ์์ํ๊ณ `DatasourceConnectionProviderImpl.getConnection()`์ ํธ์ถํ๋ค.
2. Spring CGLIB์ ์ํด ์์ฑ๋ DataSourceConfig ํ๋ก์ ๊ฐ์ฒด๋ฅผ ํตํด Connection ํ๋์ ์๋ํ๋ค.
3. ์ดํ `AbstractRoutingSource.determineTargetDataSource()`๋ฅผ ํตํด DataSource๋ฅผ ๊ฐ์ ธ์จ๋ค.
4. ๋ด๋ถ์ ์ผ๋ก `AbstractRoutingSource.determineCurrentLookupKey()`์ RouteDataSourceManager ๋ด์ ์๋ `TransactionSynchronizationManager.isCurrentTransactionReadOnly()`๊ฐ ์์๋๋ก ํธ์ถ๋๋ค.
5. `TransactionSynchronizationManager.setCurrentTransactionReadOnly()`์ด ํธ์ถ๋๋ฉด์ Transaction์ ReadOnly ํน์ฑ์ ์ธํ ํ๋ค.
์ด ์ํ๋ผ๋ฉด readOnly๊ฐ true์ด๋ false์ด๋ ํญ์ `TransactionSynchronizationManager.isCurrentTransactionReadOnly()`์ ๊ฒฐ๊ณผ๋ false๊ฐ ๋์ฌ ๊ฒ์ด๋ค. ์๋ํ๋๋ก ๋์ํ๋ ค๋ฉด `TransactionSynchronizationManager.setCurrentTransactionReadOnly()`๊ฐ `AbstractRoutingSource.determineDataSource()`๋ณด๋ค ๋จผ์ ํธ์ถ๋ผ์ผ ํ๋ค.
์ด๋ฌํ ์ด์ ๋ก AbstractRoutingDataSource๋ฅผ LazyConnectionDataSourceProxy๋ก ๊ฐ์ธ์ฃผ๊ฒ ๋๋ฉด Connection Proxy๋ฅผ ์ฐ์ ๊ฐ์ ธ์ค๊ณ , Connection์ ์ง์ ์ฌ์ฉํ ๋๊ฐ ๋์์ผ DataSource๋ก๋ถํฐ Connection์ ๊ฐ์ ธ์จ๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก DataSourceConfig ํ๋ก์ ๊ฐ์ฒด๋ฅผ ํตํด Connection ํ๋์ ๋ค๋ก ๋ฏธ๋ค Transaction์ ReadOnly ํน์ฑ์ ๋จผ์ ์ธํ ํด์ค ์ ์๋ค.
์ ์ฉ ํ
๋ค์๊ณผ ๊ฐ์ด ์์๊ฐ ๋ณ๊ฒฝ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
1. Transaction์ ์์ํ๊ณ `DatasourceConnectionProviderImpl.getConnection()`์ ํธ์ถํ๋ค.
2.`TransactionSynchronizationManager.setCurrentTransactionReadOnly()`์ด ํธ์ถ๋๋ฉด์ Transaction์ ReadOnly ํน์ฑ์ ์ธํ ํ๋ค.
3. Spring CGLIB์ ์ํด ์์ฑ๋ DataSourceConfig ํ๋ก์ ๊ฐ์ฒด๋ฅผ ํตํด Connection ํ๋์ ์๋ํ๋ค.
4. ์ดํ `AbstractRoutingSource.determineTargetDataSource()`๋ฅผ ํตํด DataSource๋ฅผ ๊ฐ์ ธ์จ๋ค.
5. ๋ด๋ถ์ ์ผ๋ก `AbstractRoutingSource.determineCurrentLookupKey()`์ RouteDataSourceManager ๋ด์ ์๋ `TransactionSynchronizationManager.isCurrentTransactionReadOnly()`๊ฐ ์์๋๋ก ํธ์ถ๋๋ค.
๋ฐ์ํ ์ด์
์ด์1. p6spy ํธํ์ฑ ๋ฌธ์
์์ธ ๋ถ์
์ฐ๋ฆฌ๋ ๊ฐ๋ฐ๊ณผ์ ์์ p6spy-spring-boot-starter๋ฅผ ์ด์ฉํด ์ฟผ๋ฆฌ ๋ก๊ทธ๋ฅผ Console์ ์ถ๋ ฅํ๋ ๋ฐฉ์์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ํ์ธํ๋ค. ๊ทธ๋ฆฌ๊ณ DB ์ด์คํ ์ค์ ์ ์ ์ฉํ๊ณ ๋ ๋ค ๋์ผํ ์ฟผ๋ฆฌ ๋ก๊ทธ๊ฐ 3๋ฒ ์ฐํ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
๋๋ฒ๊น ๊ฒฐ๊ณผ, `P6DataSource.getConnection()` ๋ฉ์๋๊ฐ 3๋ฒ ํธ์ถ๋๋ฉด์ 3๊ฐ์ ์๋ก ๋ค๋ฅธ 3๊ฐ์ DataSource๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
์์ธ์ ํ์ ํ๊ธฐ ์ํด์๋ P6Spy์ ๋์๊ณผ์ ์ ์ดํดํ ํ์๊ฐ ์๋ค. P6Spy๋ ๊ตฌ์กฐ์ ์ผ๋ก JDBC ๋๋ผ์ด๋ฒ ์์ P6Spy ๋๋ผ์ด๋ฒ๋ฅผ ์์น์์ผ ๋ฐ์ํ๋ ์ฟผ๋ฆฌ๋ฅผ ๊ฐ์งํ๊ณ ๋ก๊ทธ๋ฅผ ๋จ๊ธด๋ค.
์ข ๋ ์์ธํ ๋์๊ณผ์ ์ ์ดํด๋ณด๊ธฐ ์ํด P6DataSource ํด๋์ค์ ์์ฑ์์ Break Point๋ฅผ ์ฐ๊ณ ๋๋ฒ๊น ์ ๋๋ ค๋ณด์๋ค.
๋จผ์ ๋ฑ๋ก๋ Bean์ ์ด๊ธฐํํ๊ณ `DataSourceDecoratorBeanPostProcessor.class`์ ์ํด PostProcessing์ ์งํํ๋ค.
ํธ์ถ๋ `postProcess...` ๋ฉ์๋๋ฅผ ๋ณด๋ฉด `P6SpyDataSourceDecorator.decorate()` ๋ฉ์๋์ ์ํด ๋ฑ๋ก๋ Bean์ด P6DataSource๋ก ๋ฐํ๋๋ค. ์๋ ์ฌ์ง๊ณผ ๊ฐ์ด ๋ชจ๋ DataSource Bean๋ค์ด ์ ๊ณผ์ ์ ๊ฑฐ์น๊ธฐ ๋๋ฌธ์ ์ด 4๊ฐ์ P6DataSource๊ฐ ์์ฑ๋๋ค. ๋ฌธ์ ์ํฉ์ ์ด๋ฌํ ์ด์ ๋ก ๋ฐ์ํ๋ค.
์กฐ๊ธ๋ง ๋ ์ถ๊ฐํ์๋ฉด, DataSource Bean๋ค์ ์์ฑ๋ P6DataSource ๋ด์ realDataSource๋ก ๋ฑ๋ก๋๋ค. ๊ทธ๋ฆฌ๊ณ P6Proxy ๊ตฌํ์ฒด(ConnectionWrapper, StatementWrapper, ...)๋ฑ์ ์ํด wrapping๋จ์ผ๋ก์จ ์ฟผ๋ฆฌ๋ฅผ ๊ฐ์งํ ์ ์๊ฒ ๋๋ ๊ฒ์ด๋ค.
ํด๊ฒฐ ๋ฐฉ๋ฒ
ํน์ DataSource์ ๋ํด์๋ง Decorater๊ฐ ์ ์ฉ๋๋๋ก CustomBeanPostProcessor๋ฅผ ๊ตฌํํ๋ ๋ฑ์ ์ฌ๋ฌ ์๋๋ฅผ ํด๋ดค์ง๋ง ์คํจํ๋ค. p6spy-spring-boot-starter ์์กด์ฑ์ ์ถ๊ฐํ๋ฉด `DataSourceDecoratorBeanPostProcessor`ํด๋์ค๋ฅผ AutoConfiguration์ผ๋ก ๋ฑ๋กํ๊ธฐ ๋๋ฌธ์ ๋ฑํ ๋ฐฉ๋ฒ์ด ์๋๋ฏ ํ๋ค.
๋ฟ๋ง ์๋๋ผ ์ค๋ณต ๋ก๊ทธ๊ฐ ์ถ๋ ฅ๋๋ ๋ฌธ์ ์ธ์๋ LazyConnectionDataSourceProxy๊ฐ ์๋๋๋ก ์ ์ฉ๋์ง ์๋ ๋ฌธ์ ๋ ์์๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ํด๊ฒฐํ์ง ๋ชปํ๋ค๋ฉด ์ฌ์ฉ์ ํ์ง ์๋ ๊ฒ์ด ์ณ๋ค๊ณ ํ๋จํ๋ค.
์๋ ๊ตฌ์ฑ์ ์ฌ์ฉํ์ง ์๋ p6spy ์์กด์ฑ์ ์ถ๊ฐํด์ ํ ์ ์์๊ฒ ๊ฐ์ง๋ง, ๊ผญ p6spy๊ฐ ์๋๋๋ผ๋ Hibernate๋ฅผ ํตํด์ ๋ณ๋์ ๋ก๊น ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๊ธฐ ๋๋ฌธ์ ์ผ๋จ์ p6spy ์์กด์ฑ์ ์ ๊ฑฐํ๋ ๋ฐฉํฅ์ผ๋ก ๊ฒฐ์ ํ๋ค.
์ด์2. ddl-auto ๋ฏธ๋์
Master - Slave ๋ฐฉ์์ผ๋ก Bean์ ๋ฑ๋กํ๋ฉด `spring.jpa.hibernate.ddl-auto`๋ฅผ ์ ์ฉํด๋ ์คํค๋ง๊ฐ ์์ฑ๋์ง ์๋๋ค. ๊ทธ ์ด์ ๋ `spring.jpa.hibernate` ์์ฑ์ JpaBaseConfiguration์ ์์ํ HibernateJpaConfiguration ํด๋์ค์ ์ํด ์ ์ฉ๋๋๋ฐ, LocalContainerEntityManagerFactoryBean์ ์ง์ ๋ฑ๋กํด์ ํด๋น Bean์ ๋ํ auto configuration์ด ๋์ํ์ง ์๋๋ค.
๋ฐ๋ผ์ LocalContainerEntityManagerFactoryBean์ ์์ฑํ ๋ HibernateProperties ํด๋์ค์ determineHibernateProperties() ๋ฉ์๋๋ก properties๋ฅผ ๊ตฌ์ฑํด์ ์ฃผ์ ํด์ผ ํ๋ค. ์ด๋ฏธ ์์ ์ฝ๋์ ์์ฑ๋ผ์๊ธฐ ๋๋ฌธ์ ์ฝ๋๋ฅผ ์ถ๊ฐํ์ง ์๊ฒ ๋ค.
๋ง๋ฌด๋ฆฌ
p6spy๋ฅผ ์ง์์ผ๋ก ์ธํด ํ ์คํธ๋ Master DB์๋ ์๊ณ , Slave DB์๋ง ์๋ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๋ ๋ฐฉ์์ผ๋ก ์งํํ๋ค. ์์ ์๋ํ ๊ท์น๋๋ก ์ ๋์ํ๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค. ์๊ฐํ๋ ๊ฒ๋ณด๋ค ๋ง์ ๋ด์ฉ๋ค์ด ๋ค์์ฌ ์์ด์ ์๊ฐ์ด ๊ฝค ๊ฑธ๋ ธ๋๋ฐ ์ข์ ๊ฒฝํ์ด ๋์๋ ๊ฒ ๊ฐ๋ค.
'๐ง๐ปโ๐ป ๊ฐ๋ฐ > Java & Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Scouter ๋ชจ๋ํฐ๋ง ํด ์ฌ์ฉํด๋ณด๊ธฐ (0) | 2024.02.14 |
---|---|
Spring Boot ๋ก๊ทธ ํ์ผ ๋จ๊ธฐ๊ธฐ (with Log4j2) (0) | 2024.02.08 |
DB ์ด์คํ ๊ตฌ์ถํ๊ธฐ (with RDS) (0) | 2024.02.05 |
CI/CD ํ๊ฒฝ ๊ตฌ์ถํ๊ธฐ 2ํธ (with Github Actions) (0) | 2024.02.01 |
CI/CD ํ๊ฒฝ ๊ตฌ์ถํ๊ธฐ 1ํธ (with Github Actions) (0) | 2024.01.29 |