[英]Spring boot hibernate 5 integration error
我正在尝试使用 Java 配置将 Spring Boot 与 Hibernate 5 集成。 自从我使用 Hibernate 以来已经有一段时间了。 我收到下面提到的异常。 有人可以建议如何解决这个问题吗?
以下是我的 java 配置文件,后跟异常堆栈跟踪。
package com.example;
import com.example.dao.CustomerDao;
import com.example.model.Customer;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.util.Properties;
@SpringBootApplication
@EnableTransactionManagement
public class SpringHibernateDemoApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(SpringHibernateDemoApplication.class, args);
}
@Bean
public DataSource dataSource() throws ClassNotFoundException {
//SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
// dataSource.setDriverClass((Class<? extends Driver>)Class.forName("com.mysql.jdbc.Driver"));
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/customerDB");
dataSource.setUsername("root");
dataSource.setPassword("welcome");
return dataSource;
}
@Bean
public LocalSessionFactoryBean sessionFactory() throws ClassNotFoundException {
Properties properties = new Properties();
properties.put("hibernate.dialect","org.hibernate.dialect.MySQLDialect");
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.hbm2ddl.auto","create");
// properties.put("hibernate.connection.provider_class","org.hibernate.connection.DatasourceConnectionProvider");
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(dataSource());
localSessionFactoryBean.setPackagesToScan("com.example.model");
localSessionFactoryBean.setHibernateProperties(properties);
/*SessionFactory sessionFactory = new LocalSessionFactoryBuilder(dataSource()).
addAnnotatedClass(Customer.class).setProperties(properties).buildSessionFactory();*/
return localSessionFactoryBean;
}
@Bean(name = "hibernateTemplate")
public HibernateTemplate hibernateTemplate() throws ClassNotFoundException {
return new HibernateTemplate(sessionFactory().getObject());
}
@Bean
public CustomerDao customerDao() {
return new CustomerDao();
}
@Override
public void run(String... args) throws Exception {
Customer customer = new Customer();
customer.setFistName("amar");
customer.setLastName("dev");
customer.setEmail("amar@gmail.com");
customerDao().saveCustomer(customer);
}
}
例外。
java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:809) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:790) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:777) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1191) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1180) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
at com.example.SpringHibernateDemoApplication.main(SpringHibernateDemoApplication.java:23) [main/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_74]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_74]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_74]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_74]
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) [idea_rt.jar:na]
Caused by: java.lang.IllegalStateException: Already value [org.springframework.orm.jpa.EntityManagerHolder@10753c9] for key [org.hibernate.internal.SessionFactoryImpl@ba5d30] bound to thread [main]
at org.springframework.transaction.support.TransactionSynchronizationManager.bindResource(TransactionSynchronizationManager.java:190) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.orm.hibernate5.SpringSessionContext.currentSession(SpringSessionContext.java:127) ~[spring-orm-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:454) ~[hibernate-core-5.2.0.Final.jar:5.2.0.Final]
at org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:326) ~[spring-orm-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.orm.hibernate5.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:309) ~[spring-orm-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.orm.hibernate5.HibernateTemplate.save(HibernateTemplate.java:616) ~[spring-orm-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at com.example.dao.CustomerDao.saveCustomer(CustomerDao.java:11) ~[main/:na]
at com.example.dao.CustomerDao$$FastClassBySpringCGLIB$$fb200467.invoke(<generated>) ~[main/:na]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at com.example.dao.CustomerDao$$EnhancerBySpringCGLIB$$22cf791b.saveCustomer(<generated>) ~[main/:na]
at com.example.SpringHibernateDemoApplication.run(SpringHibernateDemoApplication.java:71) [main/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:806) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
问题是您没有在 Spring Boot 检测到休眠和 JPA 并自动配置它旁边定义HibernateTransactionManager
。 这两种技术都尝试启动事务,但都失败了。 取而代之的是使用框架,Spring Boot 已经为您自动配置了DataSource
和EntityManagerFactory
( SessionFactory
的 JPA 变体)。
HibernateTransactionManager
如果您真的想使用普通的 Hibernate,请添加一个事务管理器。
@Bean
public HibernateTransactionManager transactionManager(SessionFactory sf) {
return new HibernateTransactionManager(sf);
}
但是,我会敦促您使用该框架。 在src/main/resources
创建一个名为application.properties
的文件,并将以下属性放入其中。
spring.datasource.url=jdbc:mysql://localhost:3306/customerDB
spring.datasource.username=root
spring.datasource.password=welcome
现在您可以删除DataSource
并且您可以重写sessionFactory
方法以将DataSource
作为方法参数。
@Bean
public LocalSessionFactoryBean sessionFactory(DataSource ds) throws ClassNotFoundException {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(dataSource());
... // Other settings here
return localSessionFactoryBean;
}
但是,我建议删除所有内容并将以下内容添加到您的配置( application.properties
)中,而不是这样做。
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create
spring.jpa.database-platform=org.hibernate.dialect.MySQL57InnoDBDialect
现在,在您的SpringHibernateDemoApplication
您可以删除除CustomerDao
所有内容(除非用@Repository
注释,您也可以删除它)。 Spring Boot 已经检测并启用事务,因此您还可以删除@EnableTransactionManagement
。
@SpringBootApplication
public class SpringHibernateDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringHibernateDemoApplication.class, args);
}
@Bean
public CustomerDao customerDao() {
return new CustomerDao();
}
@Bean
public CommandLineRunner runner(CustomerDao dao) {
return new CommandLineRunner() {
@Override
public void run(String... args) throws Exception {
Customer customer = new Customer();
customer.setFistName("amar");
customer.setLastName("dev");
customer.setEmail("amar@gmail.com");
dao.saveCustomer(customer);
}
};
}
}
您唯一要做的就是使用EntityManager
替换HibernateTemplate
的使用(首先不推荐使用!)。 请参阅参考指南的JPA 部分。
@Repository
public class CustomerDao {
@PersistenceContext
private EntityManager em;
public void saveCustomer(Customer customer) {
em.persist(customer);
}
}
但是,如果您想真正减少代码,我建议添加spring-boot-starter-data-jpa
。 这将添加Spring Data JPA并允许您删除当前的CustomerDao
并简单地将其替换为一个接口。 (下面的代码假设Customer
有一个Long
类型的 id。)。
public interface CustomerDao extends JpaRepository<Customer, Long> {}
现在用CommandLineRunner
save
替换saveCustomer
。 (我添加了COmmandLineRunner
作为内部 bean,以便能够将CustomerDao
作为参数注入到方法中)。
@SpringBootApplication
public class SpringHibernateDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringHibernateDemoApplication.class, args);
}
@Bean
public CommandLineRunner runner(CustomerDao dao) {
return new CommandLineRunner() {
@Override
public void run(String... args) throws Exception {
Customer customer = new Customer();
customer.setFistName("amar");
customer.setLastName("dev");
customer.setEmail("amar@gmail.com");
dao.save(customer);
}
};
}
}
我强烈建议阅读 Spring Boot 参考指南并了解它是什么以及它的作用。 我还建议学习新的和公认的技术,如 JPA,以及 Spring Data JPA 等 Spring 项目如何帮助你解决这些问题。
您遇到的根本问题基本上是将LocalSessionFactoryBean
与已经定义了EntityManagerFactory
实例的 spring-data 一起使用; 人们可能没有理由直接使用它,但HibernateTemplate
可能是一个很好的理由; 如果你真的需要使用HibernateTemplate
不是像下面那样创建它,以避免创建额外的EntityManagerFactory
实例:
@Bean
public HibernateTemplate hibernateTemplate(EntityManagerFactory entityManagerFactory) {
return new HibernateTemplate(entityManagerFactory.unwrap(SessionFactory.class));
}
PS:此解决方案不完整,因为我不知道如何将正确创建的HibernateTemplate
(适用于读取但不适用于写入)与现有事务管理器进一步集成
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.