Spring Dynamic DataSource Routing(AbstractRoutingDataSource)
AbstractRoutingDataSource.java 클래스에 abstract 메소드로 determineCurrentLookupKey 존재한다.
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
| public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
@Override public Connection getConnection() throws SQLException { return determineTargetDataSource().getConnection(); }
@Override public Connection getConnection(String username, String password) throws SQLException { return determineTargetDataSource().getConnection(username, password); }
protected DataSource determineTargetDataSource() { Assert.notNull(this.resolvedDataSources, "DataSource router not initialized"); Object lookupKey = determineCurrentLookupKey(); DataSource dataSource = this.resolvedDataSources.get(lookupKey); if (dataSource == null && (this.lenientFallback || lookupKey == null)) { dataSource = this.resolvedDefaultDataSource; } if (dataSource == null) { throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]"); } return dataSource; } @Nullable protected abstract Object determineCurrentLookupKey(); }
|
대충 코드를 보면 위와같은 내용인데 getConnection 할때 determineCurrentLookupKey를 통해 resolvedDataSources 데이터에 있는 connection을 찾아서 해당 커넥션을 넘기는 방법이다.
그럼 간단한 예제를 보면 이해하기 쉬울것이다.
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| public enum RoutingDataSourceLookupKey{ READ,WRITE }
public class ReplicationRoutingDataSource extends AbstractRoutingDataSource {
@Override protected Object determineCurrentLookupKey() { return TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? RoutingDataSourceLookupKey.READ : RoutingDataSourceLookupKey.WRITE; } }
@Configuration public class DataSourceConfigration{ @Bean @ConfigurationProperties(prefix = "test.datasource-read") public DataSource readDataSource() { return DataSourceBuilder.create().type(HikariDataSource.class).build(); }
@Bean @ConfigurationProperties(prefix = "test.datasource-write") public DataSource writeDataSource() { return DataSourceBuilder.create().type(HikariDataSource.class).build(); }
@DependsOn({"readDataSource","writeDataSource"}) @Bean public DataSource routingDataSource(DataSource writeDataSource, DataSource readDataSource) {
var routingDataSource = new ReplicationRoutingDataSource(); var dataSourceMap = new HashMap<>(); dataSourceMap.put(RoutingDataSourceLookupKey.WRITE, writeDataSource); dataSourceMap.put(RoutingDataSourceLookupKey.READ, readDataSource); routingDataSource.setTargetDataSources(dataSourceMap); routingDataSource.setDefaultTargetDataSource(readDataSource);
return routingDataSource; } @DependsOn("routingDataSource") @Primary @Bean public DataSource dataSource(@Qualifier("routingDataSource") DataSource routingDataSource) { return new LazyConnectionDataSourceProxy(routingDataSource); } }
@Configuration public class JpaDataSourceConfigration {
@Bean @ConfigurationProperties("spring.jpa") public JpaProperties jpaProperties() { return new JpaProperties(); }
@Bean @ConfigurationProperties("spring.jpa.hibernate") public HibernateProperties hibernateProperties() { return new HibernateProperties(); }
@Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, JpaProperties jpaProperties, HibernateProperties hibernateProperties) {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean(); entityManagerFactory.setDataSource(dataSource); entityManagerFactory.setPersistenceProvider(new HibernatePersistenceProvider()); entityManagerFactory.setPersistenceUnitName("entityManagerUnit"); entityManagerFactory.setPackagesToScan("io.lific.product.infrastructure.jpa"); HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter(); jpaVendorAdapter.setGenerateDdl(jpaProperties.isGenerateDdl()); jpaVendorAdapter.setShowSql(jpaProperties.isShowSql()); if (jpaProperties.getDatabase() != null) { jpaVendorAdapter.setDatabase(jpaProperties.getDatabase()); } if (jpaProperties.getDatabasePlatform() != null) { jpaVendorAdapter.setDatabasePlatform(jpaProperties.getDatabasePlatform()); } var properties= jpaProperties.getProperties(); properties.put("hibernate.physical_naming_strategy", hibernateProperties.getNaming().getPhysicalStrategy()); properties.put("hibernate.implicit_naming_strategy", hibernateProperties.getNaming().getImplicitStrategy()); properties.put("hibernate.hbm2ddl.auto",hibernateProperties.getDdlAuto()); jpaProperties.setOpenInView(jpaProperties.getOpenInView()); entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter); entityManagerFactory.setJpaPropertyMap(properties); entityManagerFactory.afterPropertiesSet();
return entityManagerFactory; }
@Bean public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(entityManagerFactory); return transactionManager; }
@Bean public PersistenceExceptionTranslationPostProcessor exceptionTranslationPostProcessor() { return new PersistenceExceptionTranslationPostProcessor(); } }
|
위와같은 방법으로도 처리를 할수있다.
LazyConnectionDataSourceProxy
를 꼭 해줘야 TransactionSynchronizationManager.isCurrentTransactionReadOnly()
값을 정확하게 읽어 드릴수 있다.
참고자료