SpringData JPA를 사용하는 환경에서 multi-database (feat. master/slave구분, querydsl) 구성하기
이번에 면허 재검증 프로세스를 구현하면서, multi database 환경을 구성해야 하는 작업을 수행했다.
작업하면서 구성한 내용을 개인화해서 정리해본다!
구현 환경 및 dependency 설정
작업을 구현해야 하는 환경은 Kotlin, SpringBoot, JPA, Mysql 환경이었다.
두가지의 데이터베이스를 다룰때 모두 JPA를 사용한다.
디폴트 데이터베이스와 추가로 연결할 데이터베이스를 지정한다.
여기서는 디폴트로 사용할 데이터베이스는 DefaultDatabase, 추가로 사용할 데이터베이스는 ADatabase라고 지정한다.
추가한 dependency는 다음과 같다.
plugins {
kotlin("jvm") version "1.9.25"
kotlin("plugin.spring") version "1.9.25"
id("org.springframework.boot") version "3.4.1"
id("io.spring.dependency-management") version "1.1.7"
kotlin("kapt") version "1.9.21"
kotlin("plugin.jpa") version "1.9.21"
}
group = "com.yunhalee.database"
version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta")
kapt("com.querydsl:querydsl-apt:5.0.0:jakarta")
}
kotlin {
compilerOptions {
freeCompilerArgs.addAll("-Xjsr305=strict")
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
noArg {
annotation("jakarta.persistence.Entity")
}
allOpen {
annotation("jakarta.persistence.Entity")
}
multi 데이터베이스 설정하기 + JPA
먼저 트랜잭션 readOnly 여부에 따라서 라우팅해주는 DynamicRoutigDatasource를 구현한다.
예전에 넥스트 스텝에 참여하면서 DB 부하분산을 학습하면서 써둔 글이 있다.
↓
https://dodop-blog.tistory.com/326
조회 성능 개선하기 ( ③ DB 최적화, Replication )
인덱스 설계에 이어서 이번엔 DB 최적화 부분과 JPA와 함께 MySQL Replicatioin을 구현하는 방법에 대해서 배웠다. DB 최적화 대상 Connector (Client) MySQL 서버에 접근하기 위해 Application에서 설치하여 사용
dodop-blog.tistory.com
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
import org.springframework.transaction.support.TransactionSynchronizationManager.isCurrentTransactionReadOnly
import javax.sql.DataSource
class DynamicRoutingDataSource(
master: DataSource,
readOnly: DataSource,
) : AbstractRoutingDataSource() {
init {
super.setTargetDataSources(
mapOf(
DataBaseRole.MASTER to master,
DataBaseRole.READ_ONLY to readOnly,
),
)
super.setDefaultTargetDataSource(master)
}
override fun determineCurrentLookupKey(): Any =
when {
isCurrentTransactionReadOnly() -> DataBaseRole.READ_ONLY
else -> DataBaseRole.MASTER
}
enum class DataBaseRole {
MASTER,
READ_ONLY,
}
}
application.yaml에 다음과 같이 jpa설정과 더불어 데이터베이스 설정을 넣어준다. (각각의 데이터베이스에 임의로 readOnly 테이블을 만들었다.)
spring:
jpa:
properties:
default_batch_fetch_size: 500
org.hibernate.flushMode: COMMIT
hibernate:
format_sql: false
dialect: org.hibernate.dialect.MariaDBDialect
query:
in_clause_parameter_padding: true
plan_cache_max_size: 128
plan_parameter_metadata_max_size: 16
hibernate:
ddl-auto: validate
show-sql: false
datasource:
hikari:
connection-timeout: 5000
max-lifetime: 58000
leak-detection-threshold: 30000
datasources:
default:
master-datasource:
hikari:
jdbc-url: jdbc:mysql://localhost:33306/default
username: root
password: root
driver-class-name: org.mariadb.jdbc.Driver
readonly-datasource:
hikari:
jdbc-url: jdbc:mysql://localhost:33306/default_read_only
username: root
password: root
driver-class-name: org.mariadb.jdbc.Driver
a:
master-datasource:
hikari:
jdbc-url: jdbc:mysql://localhost:33307/a
username: root
password: root
driver-class-name: org.mariadb.jdbc.Driver
readonly-datasource:
hikari:
jdbc-url: jdbc:mysql://localhost:33307/a_read_only
username: root
password: root
driver-class-name: org.mariadb.jdbc.Driver
이를 활용하는 DefaultDatabaseConfig를 구성한다.
masterDatasource와 readonlyDatasource를 만들고 아까 만들어둔 DynamicRoutingDatasource를 이용해서 트랜잭션에 따라 분산하는 routingDatasource를 사용하는 defaultDatasource를 빈으로 등록한다.
이때 해당 데이터소스를 기본으로 사용할 것이기 때문에 @Primary 로 설정해주었다. 만약, 여러 데이터 소스를 빈으로 등록하였는데 우선적용할 데이터 소스가 명시되어 있지 않으면, 예외가 발생한다.
이후에 Jpa설정에서 사용할 defaultDatasource를 사용하는 JdbcTemplate도 빈으로 등록해준다.
- 💡 참고
- 이때, ConditionalOnBean 설정을 해주지 않으면 AutoConfiguration 환경 조건이 충족되지 않아 예외가 발생할 수 있다.
- https://lomuto.tistory.com/21
EntityManagerFactoryBuilder could not be found 에러 원인과 해결
*************************** APPLICATION FAILED TO START *************************** Description: Parameter 0 of method entityManagerFactory in ... required a bean of type 'org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder' that could not be foun
lomuto.tistory.com
import com.zaxxer.hikari.HikariDataSource
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy
import org.springframework.transaction.annotation.EnableTransactionManagement
import javax.sql.DataSource
@Configuration
@EnableTransactionManagement
class DefaultDatabaseConfig {
@ConfigurationProperties(prefix = "spring.datasources.default.master-datasource.hikari")
@Bean("masterDataSource")
fun masterDataSource(): DataSource {
return DataSourceBuilder.create().type(HikariDataSource::class.java).build()
}
@ConfigurationProperties(prefix = "spring.datasources.default.readonly-datasource.hikari")
@Bean("readonlyDataSource")
fun readonlyDataSource(): DataSource {
return DataSourceBuilder.create().type(HikariDataSource::class.java).build()
}
@Bean("routingDataSource")
@ConditionalOnBean(name = ["masterDataSource", "readonlyDataSource"])
fun routingDataSource(
@Qualifier("masterDataSource") masterDataSource: DataSource,
@Qualifier("readonlyDataSource") readonlyDataSource: DataSource,
): DataSource {
return DynamicRoutingDataSource(master = masterDataSource, readOnly = readonlyDataSource)
}
@Primary
@Bean
@ConditionalOnBean(name = ["routingDataSource"])
fun defaultDataSource(routingDataSource: DataSource): DataSource = LazyConnectionDataSourceProxy(routingDataSource)
@Bean(name = ["defaultJdbcTemplate"])
@ConditionalOnBean(name = ["defaultDataSource"])
fun defaultJdbcTemplate(@Qualifier("defaultDataSource") dataSource: DataSource): JdbcTemplate = JdbcTemplate(dataSource)
}
동일한 방식으로 ADatabaseConfig도 만들어준다. 여기서 aDatasource는 primary로 등록되지 않는다.
import com.zaxxer.hikari.HikariDataSource
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy
import org.springframework.transaction.annotation.EnableTransactionManagement
import javax.sql.DataSource
@Configuration
@EnableTransactionManagement
class ADatabaseConfig {
@ConfigurationProperties(prefix = "spring.datasources.a.master-datasource.hikari")
@Bean("aMasterDataSource")
fun masterDataSource(): DataSource {
return DataSourceBuilder.create().type(HikariDataSource::class.java).build()
}
@ConfigurationProperties(prefix = "spring.datasources.a.readonly-datasource.hikari")
@Bean("aReadonlyDataSource")
fun readonlyDataSource(): DataSource {
return DataSourceBuilder.create().type(HikariDataSource::class.java).build()
}
@Bean("aRoutingDataSource")
@ConditionalOnBean(name = ["aMasterDataSource", "aReadonlyDataSource"])
fun routingDataSource(
@Qualifier("aMasterDataSource") masterDataSource: DataSource,
@Qualifier("aReadonlyDataSource") readonlyDataSource: DataSource,
): DataSource {
return DynamicRoutingDataSource(master = masterDataSource, readOnly = readonlyDataSource)
}
@Bean("aDataSource")
@ConditionalOnBean(name = ["aRoutingDataSource"])
fun accountsDataSource(@Qualifier("aRoutingDataSource") aRoutingDataSource: DataSource): DataSource = LazyConnectionDataSourceProxy(aRoutingDataSource)
@Bean(name = ["aJdbcTemplate"])
@ConditionalOnBean(name = ["aDataSource"])
fun accountsJdbcTemplate(@Qualifier("aDataSource") dataSource: DataSource): JdbcTemplate = JdbcTemplate(dataSource)
}
이제 각 데이터베이스 별로 사용할 엔티티 및 repository를 정의한다.
여기서는 패키지별로 구분할 수 있게 등록해주었다.
TeamEntity의 경우 a데이터베이스를 사용하고, UserEntity의 경우 default 데이터베이스를 사용한다.
package com.yunhalee.database.multidatabase.user.entity
import com.yunhalee.database.multidatabase.util.jpa.BaseEntity
import jakarta.persistence.Column
import jakarta.persistence.Convert
import jakarta.persistence.Entity
@Entity
class UserEntity(
@Column
var email: String,
@Column
var name: String,
@Column
var phone: String,
@Convert(converter = UserStatusConverter::class)
@Column
var status: UserStatus
) : BaseEntity()
package com.yunhalee.database.multidatabase.user.entity
import jakarta.persistence.AttributeConverter
import jakarta.persistence.Converter
enum class UserStatus {
ACTIVE,
INACTIVE
}
@Converter(autoApply = true)
class UserStatusConverter : AttributeConverter<UserStatus, String> {
override fun convertToDatabaseColumn(attribute: UserStatus): String {
return attribute.name
}
override fun convertToEntityAttribute(dbData: String): UserStatus {
return UserStatus.values().find { it.name == dbData } ?: UserStatus.INACTIVE
}
}
package com.yunhalee.database.multidatabase.user.repository
import com.yunhalee.database.multidatabase.user.entity.UserEntity
import org.springframework.data.jpa.repository.JpaRepository
interface UserRepository: JpaRepository<UserEntity, Long>{
}
package com.yunhalee.database.multidatabase.team.entity
import com.yunhalee.database.multidatabase.util.jpa.BaseEntity
import jakarta.persistence.Entity
@Entity
class TeamEntity(
var email: String,
var name: String,
var phone: String,
) : BaseEntity()
package com.yunhalee.database.multidatabase.team.repository
import com.yunhalee.database.multidatabase.team.entity.TeamEntity
import org.springframework.data.jpa.repository.JpaRepository
interface TeamRepository : JpaRepository<TeamEntity, Long> {
}
공통으로 사용할 수 있는 JPA 설정은 다음과 같이 넣어주었다.
import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaAuditing
@Configuration
@EnableJpaAuditing
class JpaConfig {
}
이제 데이터베이스별로 Jpa 설정파일을 만들어준다.
사용할 레파지토리 패키지와 엔티티 패키지 위치를 설정해준다.
이때, default database는 특정 패키지를 제외한 모든 패키지를 스캔할 수 있게 aDatasource를 사용하는 패키지에만 exclude filter를 사용했다.
entityManagerFactoryBuilder를 사용해서 설정할때도 동일한 방법이 있을지 찾아보았는데, string 값으로 지정하고 있기때문에 모든 패키지를 스캔해서 필터링하고 넣는 것는 방법 이외에는 방법을 찾지 못했다. 🥲
그래서, 모든 패키지를 스캔하는 방법 보다는 지정해주는 것이 좋을 것 같아 패키지를 지정해주었다.
이때도 마찬가지로 defaultTransactionManager에 @Primary로 우선순위를 부여했다.
import jakarta.persistence.EntityManagerFactory
import org.hibernate.cfg.AvailableSettings
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
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.orm.jpa.EntityManagerFactoryBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.FilterType
import org.springframework.context.annotation.Primary
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.orm.hibernate5.SpringBeanContainer
import org.springframework.orm.jpa.JpaTransactionManager
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
import org.springframework.transaction.PlatformTransactionManager
import javax.sql.DataSource
@Configuration
@EnableJpaRepositories(
basePackages = ["com.yunhalee.database.multidatabase"],
excludeFilters = [
ComponentScan.Filter(
type = FilterType.REGEX,
pattern = ["com.yunhalee.database.multidatabase.team.*"],
),
],
entityManagerFactoryRef = "defaultEntityManagerFactory",
transactionManagerRef = "defaultTransactionManager",
)
class DefaultDatabaseJpaConfig {
@Primary
@Bean("defaultEntityManagerFactory")
fun defaultEntityManagerFactory(
@Qualifier("defaultDataSource") dataSource: DataSource,
jpaProperties: JpaProperties,
hibernateProperties: HibernateProperties,
builder: EntityManagerFactoryBuilder,
beanFactory: ConfigurableListableBeanFactory,
): LocalContainerEntityManagerFactoryBean {
val build = builder
.dataSource(dataSource)
.packages(
"com.yunhalee.database.multidatabase.user"
)
.persistenceUnit("default")
// 이때 jpaProperties의 경우 ddl auto 설정과 같은 hibernate 설정은 가지고 있지 않기 때문에 해당 설정의 사용을 원한다면, hibernate 설정으로 감싸서 설정해야한다.
.properties(hibernateProperties.determineHibernateProperties(jpaProperties.properties, HibernateSettings()))
.build()
// AttributeConverter와 같은 등록된 빈을 가져오기 위해 SpringBeanContainer를 설정
// 설정하지 않으면 AttributeConverter를 찾지 못해 NoSuchMethodException가 발생한다.
build.jpaPropertyMap[AvailableSettings.BEAN_CONTAINER] = SpringBeanContainer(beanFactory)
return build
}
@Primary
@Bean("defaultTransactionManager")
fun defaultTransactionManager(
@Qualifier("defaultEntityManagerFactory") entityManagerFactory: EntityManagerFactory,
): PlatformTransactionManager {
return JpaTransactionManager(entityManagerFactory)
}
}
위에 주석처리된 설명과 같이 나의 경우 enum 상태 속성을 관리할때 AttributeConverter를 사용하고 있었는데, 이때 LocalContainerEntityManagerFactoryBean을 설정할때, 빈을 스캔하도록 지정해주지 않으면 기존에는 자동으로 주입되던 AttributeConverter를 찾지 못해 NoSuchMethodException가 발생한다.
언제까지 자동으로 잡아 주길 원해.
LocalContainerEntityManagerFactoryBean | 안녕하세요. 카카오헤어샵 백엔드 개발팀 카이입니다. 이번 포스팅에서는 빈으로 등록된 값 사용한 AttributeConverter를 개발하면서 겪은 문제를 공유하고 어떻게 풀
brunch.co.kr
또한 application.yaml에 ddl-auto 설정을 해주더라도 다음과 같이 jpa설정만 적용하면 정상동작 하지 않는다는 것을 알게 되었는데, 이는 JpaProperties에 해당 설정값이 없기 때문이다!
[Spring Boot] Multiple DataBases 를 사용하는 환경에서 table이 자동으로 생성되지 않음(ddl-auto)
상황: - Spring boot 3.2.x 사용 - MySQL DB 두개 사용. 다중 데이터베이스 환경 - application.yml 에 작성한 내용: hibernate: ddl-auto: create (update, create-drop 모두 안됨) - 각 DBConfig 마다 적은 내용의 일부 @Bean public
the0.tistory.com
- 💡 참고
Spring JPA spring.jpa.hibernate.ddl-auto has no impact
I must have a config problem but when I change the value of spring.jpa.hibernate.ddl-auto, it always has the same behavior, ie create tables if they do not exist, add a column if I add a field in my
stackoverflow.com
fun accountsEntityManagerFactory(
dataSource: DataSource,
jpaProperties: JpaProperties,
builder: EntityManagerFactoryBuilder,
beanFactory: ConfigurableListableBeanFactory
): LocalContainerEntityManagerFactoryBean {
val build = builder
.dataSource(dataSource)
.packages("")
.persistenceUnit("default")
// 문제 부분
.properties(jpaProperties.properties)
.build()
build.jpaPropertyMap[AvailableSettings.BEAN_CONTAINER] = SpringBeanContainer(beanFactory)
return build
}
JpaProperties를 살펴보면 다음과 같다. hibernate 설정을 가지고 있지 않다.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.orm.jpa.vendor.Database;
/**
* External configuration properties for a JPA EntityManagerFactory created by Spring.
*
* @author Dave Syer
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Eddú Meléndez
* @author Madhura Bhave
* @since 1.1.0
*/
@ConfigurationProperties(prefix = "spring.jpa")
public class JpaProperties {
/**
* Additional native properties to set on the JPA provider.
*/
private Map<String, String> properties = new HashMap<>();
/**
* Mapping resources (equivalent to "mapping-file" entries in persistence.xml).
*/
private final List<String> mappingResources = new ArrayList<>();
/**
* Name of the target database to operate on, auto-detected by default. Can be
* alternatively set using the "Database" enum.
*/
private String databasePlatform;
/**
* Target database to operate on, auto-detected by default. Can be alternatively set
* using the "databasePlatform" property.
*/
private Database database;
/**
* Whether to initialize the schema on startup.
*/
private boolean generateDdl = false;
/**
* Whether to enable logging of SQL statements.
*/
private boolean showSql = false;
/**
* Register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the
* thread for the entire processing of the request.
*/
private Boolean openInView;
public Map<String, String> getProperties() {
return this.properties;
}
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
public List<String> getMappingResources() {
return this.mappingResources;
}
public String getDatabasePlatform() {
return this.databasePlatform;
}
public void setDatabasePlatform(String databasePlatform) {
this.databasePlatform = databasePlatform;
}
public Database getDatabase() {
return this.database;
}
public void setDatabase(Database database) {
this.database = database;
}
public boolean isGenerateDdl() {
return this.generateDdl;
}
public void setGenerateDdl(boolean generateDdl) {
this.generateDdl = generateDdl;
}
public boolean isShowSql() {
return this.showSql;
}
public void setShowSql(boolean showSql) {
this.showSql = showSql;
}
public Boolean getOpenInView() {
return this.openInView;
}
public void setOpenInView(Boolean openInView) {
this.openInView = openInView;
}
}
그렇다면, 어떻게 적용할 수 있을까?
다음과 같이 Hibernate설정을 발견하고 이를 활용하도록 했다. ✨✨✨✨✨
@ConfigurationProperties("spring.jpa.hibernate")
public class HibernateProperties {
private static final String DISABLED_SCANNER_CLASS = "org.hibernate.boot.archive.scan.internal.DisabledScanner";
private final Naming naming = new Naming();
/**
* DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto" property.
* Defaults to "create-drop" when using an embedded database and no schema manager was
* detected. Otherwise, defaults to "none".
*/
private String ddlAuto;
// 발견한 부분
public Map<String, Object> determineHibernateProperties(Map<String, String> jpaProperties,
HibernateSettings settings) {
Assert.notNull(jpaProperties, "JpaProperties must not be null");
Assert.notNull(settings, "Settings must not be null");
return getAdditionalProperties(jpaProperties, settings);
}
}
다음으로 aDatabaseJpaConfig 파일은 다음과 같다.
import jakarta.persistence.EntityManagerFactory
import org.hibernate.cfg.AvailableSettings
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
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.orm.jpa.EntityManagerFactoryBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.FilterType
import org.springframework.context.annotation.Primary
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.orm.hibernate5.SpringBeanContainer
import org.springframework.orm.jpa.JpaTransactionManager
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
import org.springframework.transaction.PlatformTransactionManager
import javax.sql.DataSource
@Configuration
@EnableJpaRepositories(
basePackages = ["com.yunhalee.database.multidatabase.team.*"],
entityManagerFactoryRef = "defaultEntityManagerFactory",
transactionManagerRef = "defaultTransactionManager",
)
class ADatabaseJpaConfig {
@Primary
@Bean("aEntityManagerFactory")
fun defaultEntityManagerFactory(
@Qualifier("aDataSource") dataSource: DataSource,
jpaProperties: JpaProperties,
hibernateProperties: HibernateProperties,
builder: EntityManagerFactoryBuilder,
beanFactory: ConfigurableListableBeanFactory,
): LocalContainerEntityManagerFactoryBean {
val build = builder
.dataSource(dataSource)
.packages(
"com.yunhalee.database.multidatabase.team"
)
.persistenceUnit("a")
.properties(hibernateProperties.determineHibernateProperties(jpaProperties.properties, HibernateSettings()))
.build()
build.jpaPropertyMap[AvailableSettings.BEAN_CONTAINER] = SpringBeanContainer(beanFactory)
return build
}
@Bean("aTransactionManager")
fun defaultTransactionManager(
@Qualifier("aEntityManagerFactory") entityManagerFactory: EntityManagerFactory,
): PlatformTransactionManager {
return JpaTransactionManager(entityManagerFactory)
}
}
entityManager별로 QueryDsl을 설정하기
이번엔 설정한 엔티티 매니저별로 queryDsl을 사용할 수 있도록 설정해보자.
PersistenceContext의 unitName에 들어갈 설정은 앞서 JpaConfig에서 EntityManagerFactory 빈 설정에 넣어주었던 persistenceUnit 명을 넣어주면 된다.
import com.querydsl.jpa.impl.JPAQueryFactory
import jakarta.persistence.EntityManager
import jakarta.persistence.PersistenceContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary
@Configuration
class QueryDslConfig {
@PersistenceContext(unitName = "default")
private val defaultEntityManager: EntityManager? = null
@PersistenceContext(unitName = "a")
private val aEntityManager: EntityManager? = null
@Primary
@Bean
fun defaultQueryFactory(): JPAQueryFactory {
return JPAQueryFactory(defaultEntityManager)
}
@Bean
fun aQueryFactory(): JPAQueryFactory {
return JPAQueryFactory(aEntityManager)
}
}
사용할떄는 queryFactory대신 지정된 aQueryFactory나 defaultQueryFactory를 이용해서 사용하면 된다!
끝!!✨✨✨✨✨
+ 다음으로 해보고 싶은 것 -> JTA를 이용해서 분산트랜잭션 적용해보기 ✨
✨ 참고한 사이트)
- https://velog.io/@suhongkim98/Spring-Data-JPA-multi-datasource-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0
Spring Data JPA multi-datasource 설정하기 #1
하나의 스프링 애플리케이션에서 여러 데이터베이스에 붙어 JPA로 작업하기
velog.io
- https://brunch.co.kr/@purpledev/33
언제까지 자동으로 잡아 주길 원해.
LocalContainerEntityManagerFactoryBean | 안녕하세요. 카카오헤어샵 백엔드 개발팀 카이입니다. 이번 포스팅에서는 빈으로 등록된 값 사용한 AttributeConverter를 개발하면서 겪은 문제를 공유하고 어떻게 풀
brunch.co.kr
- https://lomuto.tistory.com/21
EntityManagerFactoryBuilder could not be found 에러 원인과 해결
*************************** APPLICATION FAILED TO START *************************** Description: Parameter 0 of method entityManagerFactory in ... required a bean of type 'org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder' that could not be foun
lomuto.tistory.com
[Spring Boot] Multiple DataBases 를 사용하는 환경에서 table이 자동으로 생성되지 않음(ddl-auto)
상황: - Spring boot 3.2.x 사용 - MySQL DB 두개 사용. 다중 데이터베이스 환경 - application.yml 에 작성한 내용: hibernate: ddl-auto: create (update, create-drop 모두 안됨) - 각 DBConfig 마다 적은 내용의 일부 @Bean public
the0.tistory.com
- https://mudchobo.github.io/posts/spring-boot-jpa-multiple-database
Spring Boot JPA - multiple database 설정. - mudchobo devlog
리플리케이션 되어 있는 디비에서 서비스쪽에서는 전부 마스터, 통계와 같이 슬로우쿼리가 걸리는 애들은 슬레이브로 쿼리를 날리기 위해서 여기까지 왔다. 역시 이 방법이 제일 맘에 드는 것
mudchobo.github.io
- https://jong-bae.tistory.com/58
[SpringBoot JPA] 다중 DB 설정하기 (multi Datasource + 이기종 DB)
간혹 사이트들을 연계해야 할때, 그 중에서도 api 없는 사이트의 데이터를 사용해야할 때, 하나의 웹에서 여러 DB를 연결 시켜 사용해야 할 때가 있다. 간단하게 DB Link 로 해결하려 했으나 메인으
jong-bae.tistory.com
[JPA/Querydsl] Multiple Databases(다중 DB) 설정하기
하나의 Spring 코드에서 여러 개의 DB를 사용해보자! 🌠
velog.io
spring.jpa.hibernate.naming.physical-strategy 전략 변경
JPA를 적용하고 나서 Entity(=VO) 생성 시 변수 명을 그대로 매핑 안하고 카멜을(UserName = user_name) 언더바 형식으로 자동 매핑해준다. 가끔 이 부분이 불편해서 변수 명과 DB칼럼명을 그대로 매핑 하게
dev-elop.tistory.com
- https://jong-bae.tistory.com/62
[QueryDSL] 다중 Datasource 설정하기 (feat.@Qualifier 사용)
org.hibernate.hql.internal.ast.QuerySyntaxException: SampleEntity is not mapped 앞서 다중 DB를 사용하기 위해 설정하고 JPA + QueryDSL + Mybatis 를 이용할 수 있게 구성했는데 그 중 querydsl 을 사용할 때 Primary DB 는 크게
jong-bae.tistory.com
- https://jong-bae.tistory.com/61 ✨✨✨✨✨
[SpringBoot] 다중 Datasource, JPA+QueryDsl+Mybatis 사용 설정.
지난번에 이기종 DB 를 연결하기 위해 Datasource 를 분리하는 작업을 했었는데거기에 덧붙여 JPA와 Mybatis를 사용할 수 있게하고 QueryDSL 까지 사용하려고 한다. [SpringBoot JPA] 다중 DB 설정하기 (mult
jong-bae.tistory.com
- https://velog.io/@kdohyeon/Spring-EnableJpaAuditing
[JPA] @EnableJpaAuditing
@EnableJpaAuditing 어노테이션에 대해 알아봅니다.
velog.io
💡 추후 JTA에서 참고할만한 글
Common Application Properties :: Spring Boot
docs.spring.io
- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jta-atomikos/2.7.18
Spring Data JPA multi-datasource 글로벌 트랜잭션 사용하기(JtaTransactionManager) #2
글로벌 트랜잭션 사용하기
velog.io
- https://devssul.tistory.com/33
Spring & JTA(분산 트랜잭션)
Spring Boot에 JTA 사용하기 안녕하세요. 기존 게시물 중 Spring Multi Datasource와 Multi Transaction 게시 글들이 있는데 말이 Multi Transaction이었지 결국에는 별개의 Transaction이었기 때문에 서로 다른 Transaction
devssul.tistory.com
- https://d2.naver.com/helloworld/5812258