最近在公司在一个已经很臃肿的项目上添加新功能,要求使用的是新数据库,并且使用Spring Data JPA。这让我很懵逼,因为此项目臃肿的程度已经无法想象了,里面有N+个数据源,并且ORM使用到了JDBC、mybatis、Spring Data JPA。如果当时约定使用相同的技术就不会出现问题了(前人种坑,后人填坑....)。
@Configuration
@MapperScan(value = "com.xichuan.flow.mapper.flow", sqlSessionFactoryRef = "sqlSessionFactory")
public class FlowDataSourceConfig {
@Primary
@Bean(name="dataSource")
@ConfigurationProperties(prefix="spring.flow_datasource")
public DataSource flowDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(flowDataSource());
return sessionFactory.getObject();
}
}
@SuppressWarnings(value = "unused")
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = {
"com.xichuan.imap.repository"
},
entityManagerFactoryRef = "imapEntityManagerFactory",
transactionManagerRef = "imaptransactionManager")
public class ImapDataSourceConfig {
@Autowired
private Environment env;
public ImapDataSourceConfig() {
super();
}
@Bean
public LocalContainerEntityManagerFactoryBean imapEntityManagerFactory() {
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(imapDataSource());
em.setPackagesToScan(
new String[]{
"com.xichuan.imap.entity"
}
);
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
@ConfigurationProperties(prefix = "spring.imap_datasource")
public DataSource imapDataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(Preconditions.checkNotNull(env.getProperty("spring.imap_datasource.driverClassName")));
dataSource.setUrl(Preconditions.checkNotNull(env.getProperty("spring.imap_datasource.url")));
dataSource.setUsername(Preconditions.checkNotNull(env.getProperty("spring.imap_datasource.username")));
dataSource.setPassword(Preconditions.checkNotNull(env.getProperty("spring.imap_datasource.password")));
final Properties propertiesJpa = new Properties();
dataSource.setConnectionProperties(propertiesJpa);
return dataSource;
}
@Bean
public PlatformTransactionManager imapTransactionManager(EntityManagerFactory emf) {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
@Service
public class ImapServiceImpl{
@Autowired
private EntityManager mEntityManager;
@PostConstruct
public void initFactory() {
logger.info("开始实例化JPAQueryFactory");
mQueryFactory = new JPAQueryFactory(mEntityManager);
}
...
...
}
乍一看并没有什么问题,对,我感觉没有问题,后来发现问题贼多..................
@Configuration
@EnableJpaRepositories
(
entityManagerFactoryRef="classroom_EntityManagerFactoryBean",
transactionManagerRef = "classroom_transactionManager",
basePackages = {
"com.xichuan.classroom.dao"
}
)
@EnableTransactionManagement
public class ClassroomDataSourceConfig {
@Autowired
private JpaProperties jpaProperties;
@Bean(name = "classroom_databaseSource")
//@Qualifier("classroom_databaseSource")
@ConfigurationProperties(prefix="spring.cr_datasource")
public DataSource classroomDataSource() {
return DataSourceBuilder.create().build();
}
/**
*实体管理
* @param builder
* @return
*/
@Bean(name = "classroom_EntityManagerFactoryBean")
public LocalContainerEntityManagerFactoryBean classroomEntityManagerFactoryBean(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(classroomDataSource())
.properties(jpaProperties.getHibernateProperties(classroomDataSource()))
.packages("com.xichuan.classroom.domain") //设置实体类所在位置
.persistenceUnit("bPersistenceUnit")
.build();
}
/**
* EntityManagerFactory类似于Hibernate的SessionFactory,mybatis的SqlSessionFactory
* 总之,在执行操作之前,我们总要获取一个EntityManager,这就类似于Hibernate的Session,
* mybatis的sqlSession.
* @param builder
* @return
*/
@Bean(name = "classroom_entityManagerFactory")
public EntityManagerFactory classroomEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return this.classroomEntityManagerFactoryBean(builder).getObject();
}
/**
* 配置imap的jpa事务
* @param builder
* @return
*/
@Bean(name = "classroom_transactionManager")
public PlatformTransactionManager writeTransactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(classroomEntityManagerFactory(builder));
}
}
-------------------------------------------运行过程,且解决之路---------------------------------------------------------------------
嗯....,感觉完全正确,运行吧..
2018-12-13 11:01:44.395 ERROR LoggingFailureAnalysisReporter.report
***************************
APPLICATION FAILED TO START
***************************
Description:
Field mEntityManager in com.viroyal.cloud.imap.service.impl.EduAdminServiceImpl required a single bean, but 3 were found:
- org.springframework.orm.jpa.SharedEntityManagerCreator#0: defined by method 'createSharedEntityManager' in null
- org.springframework.orm.jpa.SharedEntityManagerCreator#1: defined by method 'createSharedEntityManager' in null
- org.springframework.orm.jpa.SharedEntityManagerCreator#2: defined by method 'createSharedEntityManager' in null
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
[WARNING]
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:507)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'eduAdminController': Unsatisfied dependency expressed through field 'mEduAdminService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'eduAdminServiceImpl': Unsatisfied dependency expressed through field 'mEntityManager'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManager' available: expected single matching bean but found 3: org.springframework.orm.jpa.SharedEntityManagerCreator#0,org.springframework.orm.jpa.SharedEntityManagerCreator#1,org.springframework.orm.jpa.SharedEntityManagerCreator#2
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:866)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:762)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:372)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1187)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1176)
at com.viroyal.cloud.SmartSchoolApplication.main(SmartSchoolApplication.java:36)
... 6 more
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'eduAdminServiceImpl': Unsatisfied dependency expressed through field 'mEntityManager'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManager' available: expected single matching bean but found 3: org.springframework.orm.jpa.SharedEntityManagerCreator#0,org.springframework.orm.jpa.SharedEntityManagerCreator#1,org.springframework.orm.jpa.SharedEntityManagerCreator#2
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 25 more
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManager' available: expected single matching bean but found 3: org.springframework.orm.jpa.SharedEntityManagerCreator#0,org.springframework.orm.jpa.SharedEntityManagerCreator#1,org.springframework.orm.jpa.SharedEntityManagerCreator#2
at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:173)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 38 more
刚看到错误,一脸懵逼,这是啥...........。明显,这是因为多数据源导致的错误。隐隐约约感觉到,数据源与EntityManeger原因导致的错误。日志中有个提示像是在暗示什么:
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
我发现mybatis数据源设置@Primary并没又特别大的作用,将器@Primary移植到imapDataSource数据源上。
修改内容在配置文件中,我会详细标记的。
@Configuration
@MapperScan(value = "com.xichuan.flow.mapper.flow", sqlSessionFactoryRef = "sqlSessionFactory")
public class FlowDataSourceConfig {
//@Primary /**-------------将此注解取消到------*/
@Bean(name="dataSource")
@ConfigurationProperties(prefix="spring.flow_datasource")
public DataSource flowDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(flowDataSource());
return sessionFactory.getObject();
}
}
@SuppressWarnings(value = "unused")
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = {
"com.xichuan.imap.repository"
},
entityManagerFactoryRef = "imap_EntityManagerFactory", /**--相应的修改---*/
transactionManagerRef = "imap_transactionManager") /**--相应的修改---*/
public class ImapDataSourceConfig {
@Autowired
private Environment env;
public ImapDataSourceConfig() {
super();
}
@Bean(name = "imap_EntityManagerFactory") /**----------重命名Bean-------------------*/
@Primary /**-----------添加@Primary-------------------*/
public LocalContainerEntityManagerFactoryBean imapEntityManagerFactory() {
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(imapDataSource());
em.setPersistenceUnitName("aPersistenceUnit"); /**----------设置了UniteName-------------*/
em.setPackagesToScan(
new String[]{
"com.xichuan.imap.entity"
}
);
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
@Bean(name = "imap_datasource") /**----------重命名Bean-------------------*/
@ConfigurationProperties(prefix = "spring.imap_datasource")
@Primary /**-----------添加@Primary-------------------*/
public DataSource imapDataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(Preconditions.checkNotNull(env.getProperty("spring.imap_datasource.driverClassName")));
dataSource.setUrl(Preconditions.checkNotNull(env.getProperty("spring.imap_datasource.url")));
dataSource.setUsername(Preconditions.checkNotNull(env.getProperty("spring.imap_datasource.username")));
dataSource.setPassword(Preconditions.checkNotNull(env.getProperty("spring.imap_datasource.password")));
final Properties propertiesJpa = new Properties();
dataSource.setConnectionProperties(propertiesJpa);
return dataSource;
}
@Bean(name = "imap_transactionManager") /**----------重命名Bean-------------------*/
@Primary /**-----------添加@Primary-------------------*/
public PlatformTransactionManager imapTransactionManager(@Qualifier("imap_EntityManagerFactory")EntityManagerFactory emf) { /**----------使用@Qualifier--------------*/
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
@Service
public class ImapServiceImpl{
//@Autowired
@PersistenceContext(unitName = "aPersistenceUnit")
private EntityManager mEntityManager;
@PostConstruct
public void initFactory() {
logger.info("开始实例化JPAQueryFactory");
mQueryFactory = new JPAQueryFactory(mEntityManager);
}
...
...
}
好了,此时运行就没有错误了.....,要是当时都用mybatis-plus多好啊,就不会有这样那样的问题。最近写的项目都约定好使用mybatis-plus。心累,心累...
第三个参考链接的一句话关于@PersistenceContext。
@PersistenceContext
allows you to specify which persistence unit you want to use. Your project might have multiple data sources connected to different DBs and @PersistenceContext
allows you to say which one you want to operate on
参考链接:
java - No qualifying bean of type 'javax.persistence.EntityManager' available: expected single matching bean but found 2 - Stack Overflow
@Qualifier与@Resource与@Autowired的区别_z_k_h的博客-CSDN博客
spring - @Autowired vs @PersistenceContext for EntityManager bean - Stack Overflow
github上有我更多的笔记:Raray-chuan (兮川) · GitHub,欢迎stars与following,如果有问题可以在issue中向我咨询
关注我的公众号,获取更多关于后端、大数据的知识