skarltjr / Memory_Write_Record

나의 모든 학습 기록
0 stars 0 forks source link

스프링부트 멀티데이터 소스(with querydsl) #56

Open skarltjr opened 2 years ago

skarltjr commented 2 years ago

상황 :

동아리에서 개발을 할 때 어플리케이션마다 다른 데이터베이스를 사용하는 기존의 코드가 존재
멀티 데이터소스를 어떻게 사용하는지 이해가 필요하다고 생각
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
    basePackages = [
        "com.yourssu.ground.soomsil.model",
        "com.yourssu.ground.errorbot.repository",
        "com.yourssu.ground.utils.model.repository"
    ],
    transactionManagerRef = SoomsilDataSourceConfig.TRANSACTION_MANAGER,
    entityManagerFactoryRef = "soomsilEntityManagerFactory"
)
class SoomsilDataSourceConfig(
    private val env: Environment
) {
    companion object {
        const val TRANSACTION_MANAGER = "soomsilTransactionManager"
    }

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.soomsil")
    fun soomsilDataSourceProperties(): DataSourceProperties {
        return DataSourceProperties()
    }

    @Bean
    @Primary
    fun soomsilDataSource(@Qualifier("soomsilDataSourceProperties") dataSourceProperties: DataSourceProperties): DataSource {
        return dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
    }

    @Bean
    @Primary
    fun soomsilEntityManagerFactory(
        @Qualifier("soomsilDataSource") dataSource: DataSource
    ): LocalContainerEntityManagerFactoryBean {
        val em = LocalContainerEntityManagerFactoryBean()
        val properties = HashMap<String, Any>()
        val vendor = HibernateJpaVendorAdapter()

        env.getProperty("spring.jpa.hibernate.ddl-auto")?.let { properties["hibernate.hbm2ddl.auto"] = it }
        env.getProperty("spring.jpa.properties.hibernate.dialect")?.let { properties["hibernate.dialect"] = it }
        properties["hibernate.implicit_naming_strategy"] = SpringImplicitNamingStrategy::class.java.name
        properties["hibernate.physical_naming_strategy"] = SpringPhysicalNamingStrategy::class.java.name

        em.dataSource = dataSource
        em.setPackagesToScan(
            "com.yourssu.ground.soomsil.model",
            "com.yourssu.ground.errorbot.model",
            "com.yourssu.ground.utils.model.entity"
        )
        em.jpaVendorAdapter = vendor
        em.setJpaPropertyMap(properties)

        return em
    }

    @Bean
    @Primary
    fun soomsilTransactionManager(@Qualifier("soomsilEntityManagerFactory") entityManagerFactory: EntityManagerFactory): PlatformTransactionManager {
        return JpaTransactionManager(entityManagerFactory)
    }
}

정리:

@ConfigurationProperties("spring.datasource.soomsil") yml에 정의된 soomsil 데이터소스 정보 활용하여 DataSourceProperties생성


- 5.`DataSource`
@Bean
@Primary
fun soomsilDataSource(@Qualifier("soomsilDataSourceProperties") dataSourceProperties: DataSourceProperties): DataSource {
    return dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
}

4번에 만들어진 DataSourceProperties를 통해 DataSource생성


- 6.`LocalContainerEntityManagerFactoryBean`

@Bean @Primary fun soomsilEntityManagerFactory( @Qualifier("soomsilDataSource") dataSource: DataSource ): LocalContainerEntityManagerFactoryBean { val em = LocalContainerEntityManagerFactoryBean() val properties = HashMap<String, Any>() val vendor = HibernateJpaVendorAdapter()

    env.getProperty("spring.jpa.hibernate.ddl-auto")?.let { properties["hibernate.hbm2ddl.auto"] = it }
    env.getProperty("spring.jpa.properties.hibernate.dialect")?.let { properties["hibernate.dialect"] = it }
    properties["hibernate.implicit_naming_strategy"] = SpringImplicitNamingStrategy::class.java.name
    properties["hibernate.physical_naming_strategy"] = SpringPhysicalNamingStrategy::class.java.name

    em.dataSource = dataSource
    em.setPackagesToScan(
        "com.yourssu.ground.soomsil.model",
        "com.yourssu.ground.errorbot.model",
        "com.yourssu.ground.utils.model.entity"
    )
    em.jpaVendorAdapter = vendor
    em.setJpaPropertyMap(properties)

    return em
}

멀티 데이터 소스를 활용할 땐 엔티티 매니저가 관리하는 객체 속성 중 빈으로 등록된 값을 사용하기 위해선 "자동"이 아닌 "수동"으로 별도의 설정이 필요하다고 한다.

앞서 4,5번에서 별도의 데이터소스를 위한 빈을 등록했고 이를 활용해야하는 상황

이를위해 entitymanager 생성 시 LocalContainerEntityManagerFactoryBean을 통해 이러한 bean들을 주입.

엔티티 매니저는 영속성 컨텍스트를 통해 엔티티들을 관리하고 이 엔티티매니저를 생성할 때 여러 설정을 위한 빈을 주입받아 생성할 수 있도록 LocalContainerEntityManagerFactoryBean활용


- 7.`PlatformTransactionManager`
@Bean
@Primary
fun soomsilTransactionManager(@Qualifier("soomsilEntityManagerFactory") entityManagerFactory: EntityManagerFactory): PlatformTransactionManager {
    return JpaTransactionManager(entityManagerFactory)
}

트랜잭션 매니저 생성 이때 트랜잭션을 관리할건데 어떠한 엔티티, 엔티티매니저에 대해서 관리할것인지를 파라미터로 전달

- ![스크린샷 2022-02-05 오전 1 09 01](https://user-images.githubusercontent.com/62214428/152572235-98dc5371-930d-470f-9295-c56170cd8951.png)
- 스프링 트랜잭션 추상화의 핵심 인터페이스는 PlatformTransactionManager
- 모든 스프링 트랜잭션 기능과 코드는 이 인터페이스를 활용
- ★`PlatformTransactionManager`은 트랜잭션이 어디서 시작하고 종료하는지, 커밋인지 롤백인지를 결졍.
- 내부에 들어가보면 
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
        throws TransactionException;

void commit(TransactionStatus status) throws TransactionException;

void rollback(TransactionStatus status) throws TransactionException;

이 존재

-----

사용 :
추가로 위 환경에서 querydsl을 사용해야했다

class QuerydslConfig{ @Bean fun jpaQueryFactory(@Qualifier("soomsilEntityManagerFactory")entityManager: EntityManager):JPAQueryFactory{ return JPAQueryFactory(entityManager) } }


- 다른 예시들을 찾아봤을 때 `  fun jpaQueryFactory(entityManager: EntityManager)`처럼 entityManager를 지정해줘야한다고 봤는데
- 예시들은 모두 하나의 dataSource를 사용했었다.
- 멀티 데이터 소스에서 활용하기위해선 어떤 entityManager를 활용해야할지 지정해줘야한다고 생각했다.
- 그래서 위 처럼 사용
skarltjr commented 2 years ago

내가 이걸 왜 기록하는가?