一、简述

有一个Java项目A,使用了mybatis-plus;
有一个Java项目B,使用了jpa(hibernate);
现在要把B项目整个合并到A项目中,遇到了这个错误:

Consider defining a bean named 'entityManagerFactory' in your configuration

也就是说,mybatis-plus与jpa(hibernate)在一个Java项目中同时使用时,报错,找不到:entityManagerFactory

二、踩坑过程

1.刚开始合并项目时,报错Consider defining a bean of type 'com.xxx.xxx.XXXRepository' in your configuration,没有找到自己写的Jpa的Repository.java类;后来发现,需要在启动类Application.java中,增加Jpa的包扫描语句:

@EnableJpaRepositories(basePackages="com.xxx")
@EntityScan(basePackages="com.xxx")

2.扫描到自己的Jpa的Repository后,就开始报错:

Consider defining a bean named 'entityManagerFactory' in your configuration

3.为了解决这个错误,百度发现几种解决方法:

●删除本地的maven的hibernate-core文件夹

感觉这个挺离谱的,估计是要让maven自动选择正确的hibernate-core版本的意思;总之,试了没有用。

●给spring-boot-starter-data-jpa包声明版本

这个也试了,参考B项目的jpa版本号,但是配置了也没用。

4.自己检查maven树,发现把所有冲突都解决完毕后,也没有用。

5.自己检查maven树,尝试各种exclude、include、指定版本号,都没有用。

三、正确解决方法

1.报错找不到entityManagerFactory,个人推测是,项目中同时使用mybatis-plus与jpa(hibernate),导致默认配置失效,需要手动配置一个新的数据源等

2.首先,自己创建一个entityManagerFactory,例如下方Java文件:


@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
  entityManagerFactoryRef = "myEntityManagerFactory",
  transactionManagerRef = "myTransactionManager",
  basePackages = {"com.xxx.repository"} //设置repository所在位置
)
public class DBConfig{
  //这4个value与application.properties中对应;冒号后是默认值
  @Value("${myuser:root}")
  private String myuser;
  @Value("${mypass:root}")
  private String mypass;
  @Value("${mydriver}")
  private String mydriver;
  @Value("${myurl}")
  private String myurl;
  
  @Bean
  public HikariDataSource hds(){
    HikariDataSource hds = new HikariDataSource();
    hds.setUsername(myuser);
    hds.setPassword(mypass);
    hds.setJdbcUrl(myurl);
    hds.setDriverClassName(mydriver);
    hds.setAutoCommit(true);
    
    return hds;
  }
  
  @Bean
  public Properties prop(){
    Properties prop = new Properties();
    
    //prop.put("hibernate.connection.driver_class",mydriver);
    //prop.put("hibernate.connection.url",myurl);
    //prop.put("hibernate.connection.username",myuser);
    //prop.put("hibernate.connection.password",mypass);

    prop.put("hibernate.show_sql","true");
    prop.put("hibernate.connection.userUnicode","true");
    prop.put("hibernate.connection.characterEncoding","UTF-8");
    prop.put("hibernate.format_sql","true");
    prop.put("hibernate.use_sql_comments","true");
    prop.put("hibernate.hbm2ddl.auto","update");
    prop.put("hibernate.dialect","org.hibernate.dialect.MySQL5Dialect");

    prop.put("hibernate.connection.autoReconnect","true");
    prop.put("hibernate.connection.autoReconnectForPools","true");
    prop.put("hibernate.connection.is-connection-validation-required","true");   
    prop.put("validationQuery","SELECT 1");   
    prop.put("testOnBorrow","true");   
    
    return prop;
  }

  @Primary  
  @Bean(name = "myEntityManagerFactory" )
  public LocalContainerEntityManagerFactoryBean myEntityManagerFactory(HikariDataSource hds, Properties prop){
      
    LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
    //这个扫描的是Entity(JavaBean)的位置,注意与上方的repository区别开
    bean.setPackagesToScan("com.xxx.entity");
    
    HibernateJpaVendorAdapter hjva = new HibernateJpaVendorAdapter();
    bean.setJpaVendorAdapter(hjva);
    
    bean.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
    
    bean.setDataSource(hds);
    
    bean.setJpaProperties(prop);
    
    return bean;
  }
  
  @Primary
  @Bean(name="myEntityManager")
  public EntityManager myEntityManager(EntityManagerFactory myEntityManagerFactory){
    return myEntityManagerFactory.createEntityManager();
  }
  
  @Primary
  @Bean(name="myTransactionManager")
  public PlatformTransactionManager myTransactionManager(EntityManagerFactory myEntityManagerFactory){
    JpaTransactionManager jtm= new JpaTransactionManager();
    jtm.setEntityManagerFactory(myEntityManagerFactory);
    
    return jtm;
    
  }
}

3.上方代码,也配置了@EnableJpaRepositories;因此,记得把启动类的Application.java中的这个注解去掉(总共配一个就行了)

4.下面看一下pom.xml,除了必要的jpa注解之外,本人的项目中,漏了一个hibernate-entitymanager的jar包配置,因此加上:

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-entitymanager</artifactId>
  <version>4.3.9.Final</version>
  <exclusions>
    <exclusion>
      <artifactId>hibernate-core</artifactId>
      <groupId>org.hibernate</groupId>
    </exclusion>
  </exclusions>
</dependency>
<dependency>
      <artifactId>hibernate-core</artifactId>
      <groupId>org.hibernate</groupId>
      <version>4.3.9.Final</version>
      <scope>compile</scope>
</dependency>

上方是为了保证jar包版本统一,才用了exclusion
(虽然看起来奇怪,但是本人项目不这样写就jar包冲突…)

5.最后,在配置文件中配置与上方xml对应的数据库连接信息即可,例如,在application-test.properties中进行如下配置:

myuser=root
mypass=root
mydriver=com.mysql.cj.jdbc.Driver
myurl=jdbc:mysql://10.123.123.123:3306/mydbname?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8

6.启动项目,就不会出现找不到entityManagerFactory的错误了。

四、总结

当Java项目启动出现找不到entityManagerFactory的错误信息时,正确解决方法是,自己创建一个entityManagerFactory放入spring容器,步骤如上。(亲测有效)

五、后记

后来,本人的这个项目又出现了一个奇葩问题,jpa(hibernate)执行select语句正常,可以返回数据;
但是执行save方法(insert),数据库并没有写入数据。

当时使用以下代码测试,关闭自动开启的事务:

@EnableJpaRepositories(basePackages="xxx.xxx.xxx", enableDefaultTransactions = false )

然后执行save方法时,就报错找不到EntityManager;
如果开启事务:

//这两句一样,因为默认是开启的
@EnableJpaRepositories(basePackages="xxx.xxx.xxx")
//@EnableJpaRepositories(basePackages="xxx.xxx.xxx", enableDefaultTransactions = true )

执行到save方法时,代码就不会报错,但是数据库就是没有数据存入。

整了两三天,总算是解决了,但是不太清楚原因;

个人推测,如果不开启事务,jpa就不允许执行insert等操作,只能select;如果开启事务,由于项目哪里有问题,执行save后没有提交,因此不报错,数据库也没有存入数据。

解决方法也不是很清楚,百度搜了一堆,改了一大堆代码,不知道哪里有用,哪里没用,只能总结下可能的解决方法

1.对比下上方的代码(上方是已解决这个错误的代码)

2.pom.xml中,缺失了jar包hibernate-entitymanager与hibernate-core,需要加上这两个。

3.需要增加@Primary注解,否则可能不行。

4.自定义的entityManager等JavaBean,最好起别名,例如myEntityManager,不要与默认的重名了(例如就起成entityManager,就是重名)

5.TransactionManager,事务管理的JavaBean,自定义时,jpa返回类型要为PlatformTransactionManager。(具体自定义方法看上方代码)

六、后记的后记,2022/2/21

1.昨天又发现一个错误:

DataAccessResourceFailureException: could not extract ResultSet

好像是程序刚启动没事,跑一阵子后就报这个错,sql相关的代码就无法执行、导致页面500。

开始以为是配置了autoReconnect=true后,导致了这个错误;
然后注释了autoReconnect=true,结果还是不行,还报错:

SqlExceptionHelper : The last packet successfully received from the server war xxx milliseconds age.
is longer than the server configured value of 'wait_timeout'.
You Should consider either expiring and/or testing connection validity before use in your application,
increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.

意思是还得加autoReconnect=true,因为不加时间长了也会报错。

原因:

如果连接池中的某个连接长时间未被使用,mysql就会中断这个连接;
后续如果程序用到了这个被中断的连接,就会导致程序报错。
(进而页面500,只能重启项目了。)

可能的解决方法:
1.配置正确autoReconnect=true相关代码(之前可能是本人配置有问题),如下:

prop.put("hibernate.connection.autoReconnect","true");
prop.put("hibernate.connection.autoReconnectForPools","true");
prop.put("hibernate.connection.is-connection-validation-required","true");   
prop.put("validationQuery","SELECT 1");   
prop.put("testOnBorrow","true");   

2.换一个数据源试试,例如HikariDataSource.

●之前总是项目到第二天就报这个错(因为晚上没人用,数据库连接就被关闭了,然后第二天一用就报错了);
现在配置了SELECT 1HikariDataSource等后,第二天使用就没有报错了。
所以这个方法应该是可以解决问题的。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐