해결된 질문
작성
·
649
·
수정됨
0
안녕하세요!
강의 잘 봤습니다!
강의 보고 다중DB를 설정하는과정에서 에러가나서 질문드려요
여기서 하면 안되는거같은데 ... 도움부탁드릴려고 글 남깁니다!
targetDb에서 select는 정상적으로 들어오는데 localDB save 할때 할당 문제인거 같은데 ...
에로코드
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder@4ff98dfc] for key [HikariDataSource (HikariPool-1)] bound to thread [scheduling-1]
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:467) ~[spring-orm-5.3.6.jar:5.3.6]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.startTransaction(AbstractPlatformTransactionManager.java:400) ~[spring-tx-5.3.6.jar:5.3.6]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373) ~[spring-tx-5.3.6.jar:5.3.6]
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595) ~[spring-tx-5.3.6.jar:5.3.6]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:382) ~[spring-tx-5.3.6.jar:5.3.6]
------
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) [spring-context-5.3.6.jar:5.3.6]
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) [spring-context-5.3.6.jar:5.3.6]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_181]
at java.util.concurrent.FutureTask.runAndReset$$$capture(FutureTask.java:308) [na:1.8.0_181]
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java) [na:1.8.0_181]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_181]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) [na:1.8.0_181]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_181]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_181]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_181]
Caused by: java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder@4ff98dfc] for key [HikariDataSource (HikariPool-1)] bound to thread [scheduling-1]
at org.springframework.transaction.support.TransactionSynchronizationManager.bindResource(TransactionSynchronizationManager.java:193) ~[spring-tx-5.3.6.jar:5.3.6]
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:442) ~[spring-orm-5.3.6.jar:5.3.6]
... 93 common frames omitted
2024-02-26 12:16:27.150 INFO 23616 --- [ scheduling-1] o.s.batch.core.step.AbstractStep : Step: [step] executed in 737ms
2024-02-26 12:16:27.160 INFO 23616 --- [ scheduling-1] c.m.mj_agent.listener.JobLoggerListener : taragetJob Job is Done. (Status: FAILED)
2024-02-26 12:16:27.160 INFO 23616 --- [ scheduling-1] c.m.mj_agent.listener.JobLoggerListener : Job is Failed
2024-02-26 12:16:27.168 INFO 23616 --- [ scheduling-1] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=taragetJob]] completed with the following parameters: [{time=1708917386263}] and the following status: [FAILED] in 797ms
localDbConfig
package com.munjaon.mj_agent.config;
import org.springframework.beans.factory.annotation.Autowired;
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.Primary;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
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 javax.sql.DataSource;
import java.util.HashMap;
/**
*
*/
@Configuration
@PropertySource({ "classpath:application.yml" })
@EnableJpaRepositories(
// basePackages = {"com.repia.collectweb.common", "com.repia.collectweb.schedule"},
// basePackages = {"com.munjaon.mj_agent.batch.local.repo"},
basePackages = {"com.munjaon.mj_agent.batch.local"},
entityManagerFactoryRef = "primaryEntityManager",
transactionManagerRef = "primaryTransactionManager"
)
public class LocalDbConfig {
@Autowired
private Environment env;
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.local-datasource.datasource.hikari")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean primaryEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(primaryDataSource());
em.setPackagesToScan(new String[] { "com.munjaon.mj_agent.batch.local.repo.entity" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.show_sql", env.getProperty("spring.jpa.show_sql"));
properties.put("hibernate.format_sql", env.getProperty("spring.jpa.format_sql"));
properties.put("hibernate.use_sql_comments", env.getProperty("spring.jpa.use_sql_comments"));
properties.put("hibernate.hbm2ddl.auto", env.getProperty("spring.jpa.hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect", env.getProperty("spring.jpa.database-platform"));
// properties.put("hibernate.dialect", env.getProperty("spring.jpa.database"));
em.setJpaPropertyMap(properties);
return em;
}
@Bean
@Primary
public PlatformTransactionManager primaryTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(primaryEntityManager().getObject());
return transactionManager;
}
}
TargetDbConfig
package com.munjaon.mj_agent.config;
import org.springframework.beans.factory.annotation.Autowired;
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.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
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;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Objects;
/**
*
*/
@Configuration
@PropertySource({ "classpath:application.yml" })
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = {"com.munjaon.mj_agent.batch.target.repo"}
, entityManagerFactoryRef = "targetDbEntityManager"
, transactionManagerRef = "targetDbTransactionManager"
)
public class TargetDbConfig {
@Autowired
private Environment env;
@Bean
// @Primary
public LocalContainerEntityManagerFactoryBean targetDbEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(targetDbDataSource());
em.setPackagesToScan(new String[] { "com.munjaon.mj_agent.batch.target.repo.entity" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.show_sql", env.getProperty("spring.jpa.show_sql"));
properties.put("hibernate.format_sql", env.getProperty("spring.jpa.format_sql"));
properties.put("hibernate.use_sql_comments", env.getProperty("spring.jpa.use_sql_comments"));
// properties.put("hibernate.hbm2ddl.auto", false);
// properties.put("hibernate.dialect", false);
properties.put("hibernate.hbm2ddl.auto", env.getProperty("spring.jpa.hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect", env.getProperty("spring.jpa.database-platform"));
em.setJpaPropertyMap(properties);
return em;
}
@Bean
// @Primary
@ConfigurationProperties(prefix = "spring.target-datasource.datasource.hikari")
public DataSource targetDbDataSource() {
return DataSourceBuilder.create().build();
}
// @Primary
// @Bean
@Bean(name = "targetDbTransactionManager")
public PlatformTransactionManager targetDbTransactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(Objects.requireNonNull(targetDbEntityManager().getObject()));
}
}
감사합니다!
답변 1
0
안녕하세요, 인프런 AI 인턴입니다.
hehihoho3님, 다중 데이터베이스를 설정하시는 과정에서 발생한 에러에 대해 질문 주셨군요.
에러 메시지를 보면, Transaction 관리에서 문제가 발생했음을 알 수 있습니다. CannotCreateTransactionException
과 java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder@4ff98dfc] for key [HikariDataSource (HikariPool-1)] bound to thread [scheduling-1]
이라는 부분에서 힌트를 얻을 수 있습니다.
다중 데이터베이스 구성 시 @Primary
어노테이션을 사용하여 주 데이터 소스를 지정하는 것을 잘 하셨습니다. 다만, 트랜잭션 관리자에 대해서도 분리가 필요합니다. 여러 데이터베이스에 대한 각각의 EntityManagerFactory
와 TransactionManager
설정이 필요하죠. 여기서 중요한 점은 각각의 설정에서 고유한 이름을 가지고 있어야 한다는 것입니다.
또한, LocalDbConfig
와 TargetDbConfig
둘 다 @EnableTransactionManagement
와 @EnableJpaRepositories
를 명시하였는데, JPA 리포지토리와 트랜잭션 매니저의 연결이 제대로 이루어지고 있는지 확인이 필요합니다.
제공해주신 정보를 바탕으로 Spring Boot 관련 문서에서 관련 설정을 할 수 있는 예제 코드를 찾았습니다. 다중 EntityManagerFactory
설정을 위한 예제 코드를 참고해 보시면 문제 해결에 도움이 될 것 같습니다.
Java 설정 예제:
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(
basePackageClasses = Order.class,
entityManagerFactoryRef = "firstEntityManagerFactory",
transactionManagerRef = "firstTransactionManager"
)
public class OrderConfiguration {
// ...
}
Kotlin 설정 예제:
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(
basePackageClasses = [Order::class],
entityManagerFactoryRef = "firstEntityManagerFactory",
transactionManagerRef = "firstTransactionManager"
)
class OrderConfiguration {
// ...
}
상기 설정에서 basePackageClasses
, entityManagerFactoryRef
, transactionManagerRef
등을 자신의 설정에 맞게 변경해 주어야 합니다.
코드에서 Order.class
와 OrderConfiguration
은 각각 예제에서 사용된 도메인 클래스와 설정 클래스입니다. 실제 연결하려는 도메인 클래스와 설정 클래스 이름으로 바꿔주셔야 합니다.
각각의 EntityManagerFactory
에 대한 설정을 EntityManagerFactoryBuilder
를 이용해 줄 수 있으며, JpaVendorAdapter
를 올바르게 설정하고, 필요한 JPA 속성을 properties
에 채워넣어야 합니다.
이 정보와 위의 코드 예제를 토대로 본인의 설정을 점검하고, 문제가 되는 부분을 수정하시면 될 것 같습니다. 설정을 분리하고 각 설정에 대해 적절한 EntityManagerFactory
와 TransactionManager
를 할당하는 방식으로 접근해 보세요.
자세한 설정 방법은 다음 문서에서 확인하실 수 있습니다:
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto.data-access.use-multiple-entity-managers
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.java-config
적절한 설정을 통해 문제를 해결하시길 바랍니다. 추가 질문이 있으시면 도움을 드릴 준비가 되어 있습니다. 감사합니다!
안녕하세요 😊
강의를 시청해주셔서 감사합니다 :) 지금처럼 하나씩 하나씩 세팅도 해보고, 개발을 하시면 더욱 큰 실력을 쌓을 수 있으리라 생각이 되겠습니다.
코드를 보았을 때 primaryDataSource() 와 targetDbDataSource() 호출 되는 부분이 있습니다. 빈으로 등록을 해놓으셔도 함수를 직접 호출하지 않고, 스프링으로 부터 의존성 주입을 받아서 사용을 해주셔야 될 것으로 판단이 되겠습니다.
감사합니다.