一、简述
有一个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 1
与
HikariDataSource
等后,第二天使用就没有报错了。
所以这个方法应该是可以解决问题的。