我使用的是Spring Boot,它使我成为实体经理。我决定测试从实体管理器获取会话工厂并将其用作示例。但是我遇到了下一个问题:javax.persistence.TransactionRequiredException: no transaction is in progress
javax.persistence.TransactionRequiredException: no transaction is in progress
属性
spring.datasource.url= jdbc:postgresql://localhost:5432/ring spring.datasource.username=postgres spring.datasource.password=root spring.jpa.show-sql = false spring.jpa.properties.hibernate.format_sql=false #Note: The last two properties on the code snippet above were added to suppress an annoying exception # that occurs when JPA (Hibernate) tries to verify PostgreSQL CLOB feature. spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults = false spring.jpa.properties.hibernate.current_session_context_class = org.springframework.orm.hibernate5.SpringSessionContext
服务等级
package kz.training.springrest.service; import kz.training.springrest.entity.User; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceUnit; @Service public class UserService { @PersistenceContext private EntityManager entityManager; @Transactional public void insertUser(User user) { SessionFactory sessionFactory = entityManager.unwrap(Session.class).getSessionFactory(); Session session = sessionFactory.getCurrentSession(); session.save(user); } }
跑步者
package kz.training.springrest.run; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.context.annotation.ComponentScan; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication @EntityScan("kz.training.springrest.entity") @EnableTransactionManagement @ComponentScan(basePackages="kz.training.springrest") public class SpringrestApplication { public static void main(String[] args) { SpringApplication.run(SpringrestApplication.class, args); } }
您有任何解决方法的想法吗?
我不太明白为什么您要使服务方法变得如此不必要地复杂。您应该只可以这样做
@Transactional public void insertUser(User user) { entityManager.persist( user ); }
如果有些地方需要访问本机Hibernate Session,则可以Session像下面这样直接打开并直接使用:
Session
@Transactional public void doSomethingFancyWithASession() { Session session = entityManager.unwrap( Session.class ); // use session as needed }
这里的概念是,Spring EntityManager通过使用@PersistenceContext注释为您提供了一个已经起作用的实例。该实例将在您的spring bean正在其中执行的当前线程中安全地使用。
EntityManager
@PersistenceContext
其次,通过使用@Transactional,这将导致Spring的事务管理自动确保EntityManager绑定到事务,无论是事务RESOURCE_LOCAL还是JTA事务都基于您的环境配置。
@Transactional
RESOURCE_LOCAL
JTA
由于调用,您遇到了问题#getCurrentSession()。
#getCurrentSession()
发生的事情是Spring EntityManager调用时创建了,然后在您的方法内部#getCurrentSession(),您要让Hibernate创建第二个会话,该会话未绑定到由@Transactional注释启动的事务。简而言之,它基本上类似于以下内容:
EntityManager entityManager = entityManagerFactory.createEntityManager(); entityManager.getTransaction().begin(); Session aNewSession = entityManager.unwrap( Session.class ) .getFactory() .getCurrentSession(); // at this point entityManager is scoped to a transaction // aNewSession is not scoped to any transaction // this also likely uses 2 connections to the database which is a waste
因此,按照我上面提到的范例进行操作,您就不再遇到问题了。你永远不应该需要调用#getCurrentSession()或者#openSession()在Spring环境中,如果你正确地允许Spring来注入你的EntityManager情况给你。
#openSession()