[英]Spring boot hibernate 5 integration error
I am trying to integrate spring Boot with Hibernate 5 using Java configs.我正在尝试使用 Java 配置将 Spring Boot 与 Hibernate 5 集成。 It has been some time since I have used Hibernate.自从我使用 Hibernate 以来已经有一段时间了。 I am getting the below mentioned exception.我收到下面提到的异常。 Can someone please suggest how to resolve this ?有人可以建议如何解决这个问题吗?
Following is my java config file followed by the exception stack trace.以下是我的 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);
}
}
The exception.例外。
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]
The problem is you aren't defining a HibernateTransactionManager
next to that Spring Boot detects hibernate and JPA and automatically configures it.问题是您没有在 Spring Boot 检测到休眠和 JPA 并自动配置它旁边定义HibernateTransactionManager
。 Both technologies try to start a transaction and that fails.这两种技术都尝试启动事务,但都失败了。 Instead work with the framework, Spring Boot already auto configures a DataSource
and EntityManagerFactory
(the JPA variant of a SessionFactory
) for you.取而代之的是使用框架,Spring Boot 已经为您自动配置了DataSource
和EntityManagerFactory
( SessionFactory
的 JPA 变体)。
HibernateTransactionManager
缺少HibernateTransactionManager
If you really want to use plain Hibernate add a transaction manager.如果您真的想使用普通的 Hibernate,请添加一个事务管理器。
@Bean
public HibernateTransactionManager transactionManager(SessionFactory sf) {
return new HibernateTransactionManager(sf);
}
I would however urge you to use the framework.但是,我会敦促您使用该框架。 Create a file called application.properties
in src/main/resources
and put the following properties in it.在src/main/resources
创建一个名为application.properties
的文件,并将以下属性放入其中。
spring.datasource.url=jdbc:mysql://localhost:3306/customerDB
spring.datasource.username=root
spring.datasource.password=welcome
Now you can remove the DataSource
and you can rewrite your sessionFactory
method to take a DataSource
as method argument.现在您可以删除DataSource
并且您可以重写sessionFactory
方法以将DataSource
作为方法参数。
@Bean
public LocalSessionFactoryBean sessionFactory(DataSource ds) throws ClassNotFoundException {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(dataSource());
... // Other settings here
return localSessionFactoryBean;
}
However instead of doing this I suggest removing everything and add the following to your configuration (the application.properties
).但是,我建议删除所有内容并将以下内容添加到您的配置( application.properties
)中,而不是这样做。
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create
spring.jpa.database-platform=org.hibernate.dialect.MySQL57InnoDBDialect
Now in your SpringHibernateDemoApplication
you can remove everything but your CustomerDao
(unless that is annotated with @Repository
you could remove that as well).现在,在您的SpringHibernateDemoApplication
您可以删除除CustomerDao
所有内容(除非用@Repository
注释,您也可以删除它)。 Spring Boot already detects and enables transactions so you can also remove the @EnableTransactionManagement
. 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);
}
};
}
}
The only thing for you to do is to replace the use of HibernateTemplate
(which isn't recommended in the first place!) with the use of an EntityManager
.您唯一要做的就是使用EntityManager
替换HibernateTemplate
的使用(首先不推荐使用!)。 See the JPA Section of the reference guide.请参阅参考指南的JPA 部分。
@Repository
public class CustomerDao {
@PersistenceContext
private EntityManager em;
public void saveCustomer(Customer customer) {
em.persist(customer);
}
}
However if you want to really reduce your code I suggest adding the spring-boot-starter-data-jpa
.但是,如果您想真正减少代码,我建议添加spring-boot-starter-data-jpa
。 This will add Spring Data JPA and allows you to remove your current CustomerDao
and simply replace it with an interface.这将添加Spring Data JPA并允许您删除当前的CustomerDao
并简单地将其替换为一个接口。 (Code below assumes Customer
has an id of type Long
.). (下面的代码假设Customer
有一个Long
类型的 id。)。
public interface CustomerDao extends JpaRepository<Customer, Long> {}
Now replace the saveCustomer
with save
in the CommandLineRunner
.现在用CommandLineRunner
save
替换saveCustomer
。 (I added the COmmandLineRunner
as an inner bean to be able to inject the CustomerDao
into the method as an argument). (我添加了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);
}
};
}
}
I strongly suggest a read of the Spring Boot reference guide and learn what it is and what it does.我强烈建议阅读 Spring Boot 参考指南并了解它是什么以及它的作用。 I would also suggest learning new and accepted technologies like JPA and how Spring projects like Spring Data JPA can help you with those.我还建议学习新的和公认的技术,如 JPA,以及 Spring Data JPA 等 Spring 项目如何帮助你解决这些问题。
The root issue you have is basically the usage of LocalSessionFactoryBean
with spring-data which already defines a EntityManagerFactory
instance;您遇到的根本问题基本上是将LocalSessionFactoryBean
与已经定义了EntityManagerFactory
实例的 spring-data 一起使用; one would probably have no reason to use it directly but HibernateTemplate
might appear as a good reason;人们可能没有理由直接使用它,但HibernateTemplate
可能是一个很好的理由; if you really need to use HibernateTemplate
than create it like below such that to avoid the creation of an additional EntityManagerFactory
instance:如果你真的需要使用HibernateTemplate
不是像下面那样创建它,以避免创建额外的EntityManagerFactory
实例:
@Bean
public HibernateTemplate hibernateTemplate(EntityManagerFactory entityManagerFactory) {
return new HibernateTemplate(entityManagerFactory.unwrap(SessionFactory.class));
}
PS: this solution is incomplete because I don't know how to further integrate the correctly created HibernateTemplate
(which works with reads but not writes) with the existing transaction manager PS:此解决方案不完整,因为我不知道如何将正确创建的HibernateTemplate
(适用于读取但不适用于写入)与现有事务管理器进一步集成
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.