Spring中No EntityManager with actual transaction available错误解决 技术背景 在Spring MVC Web应用程序中,当尝试调用persist
方法将实体模型保存到数据库时,可能会遇到No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call
错误。此错误通常表示当前线程没有可用的实际事务,无法可靠地处理持久化调用。
实现步骤 1. 检查配置文件 确保dispatcher-servlet.xml
等配置文件中正确配置了EntityManagerFactory
和TransactionManager
。示例配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <bean id ="entityManagerFactory" class ="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" > <property name ="persistenceXmlLocation" value ="classpath:./META-INF/persistence.xml" /> <property name ="dataSource" ref ="dataSource" /> <property name ="jpaVendorAdapter" > <bean class ="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" > <property name ="databasePlatform" value ="org.hibernate.dialect.H2Dialect" /> <property name ="showSql" value ="true" /> <property name ="generateDdl" value ="false" /> </bean > </property > <property name ="jpaProperties" > <props > <prop key ="hibernate.max_fetch_depth" > 3</prop > <prop key ="hibernate.jdbc.fetch_size" > 50</prop > <prop key ="hibernate.jdbc.batch_size" > 10</prop > </props > </property > </bean > <bean name ="transactionManager" class ="org.springframework.orm.jpa.JpaTransactionManager" > <property name ="entityManagerFactory" ref ="entityManagerFactory" /> </bean >
2. 添加事务注解 在需要进行持久化操作的方法上添加@Transactional
注解。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Controller public class RegisterController { @PersistenceContext EntityManager entityManager; @Autowired PasswordValidator passwordValidator; @InitBinder private void initBinder (WebDataBinder binder) { binder.setValidator(passwordValidator); } @RequestMapping(value = "/addUser", method = RequestMethod.GET) public String register (Person person) { return "register" ; } @RequestMapping(value = "/addUser", method = RequestMethod.POST) @Transactional public String register (@ModelAttribute("person") @Valid @Validated Person person, BindingResult result) { if (result.hasErrors()) { return "register" ; } else { entityManager.persist(person); return "index" ; } } }
3. 注意注解使用位置 方法级别 :在需要事务管理的方法上添加@Transactional
注解。类级别 :如果类中的多个方法都需要事务管理,可以在类上添加@Transactional
注解。4. 避免内部调用问题 如果在同一个类中,非事务方法调用事务方法,可能会导致事务注解失效。可以通过获取自身代理对象来调用事务方法,示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Component public class MarketObserver { @PersistenceContext(unitName = "maindb") private EntityManager em; @Autowired private GenericApplicationContext context; @Transactional(value = "txMain", propagation = Propagation.REQUIRES_NEW) public void executeQuery () { em.persist(...); } @Async public void startObserving () { context.getBean(MarketObserver.class).executeQuery(); } }
核心代码 以下是一个完整的示例,展示了如何在Spring中正确使用事务管理进行持久化操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd" > <context:component-scan base-package ="com.example" /> <tx:annotation-driven transaction-manager="transactionManager" /> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="password" /> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" > <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter" > <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" > <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" /> </bean> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" > <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> </beans>@Controller public class UserController { @PersistenceContext private EntityManager entityManager; @RequestMapping(value = "/saveUser", method = RequestMethod.POST) @Transactional public String saveUser (User user) { entityManager.persist(user); return "success" ; } }
最佳实践 事务粒度 :合理控制事务的粒度,避免事务过长或过短。过长的事务可能会导致锁持有时间过长,影响性能;过短的事务可能会导致频繁的事务开销。异常处理 :在事务方法中正确处理异常,确保事务能够正确回滚。事务传播行为 :根据业务需求选择合适的事务传播行为,如REQUIRED
、REQUIRES_NEW
等。常见问题 1. @Transactional
注解不生效 检查方法访问修饰符 :@Transactional
注解只对公共方法有效。检查代理机制 :确保Spring能够正确创建代理对象。如果使用基于接口的代理,确保类实现了接口;如果使用CGLIB代理,确保类不是final
类。检查配置文件 :确保tx:annotation-driven
标签已正确配置。2. 事务嵌套问题 在事务嵌套时,需要注意事务传播行为的设置,避免出现意外的事务行为。例如,在一个事务方法中调用另一个事务方法时,需要根据业务需求选择合适的传播行为。
3. 性能问题 事务会带来一定的性能开销,尤其是在高并发场景下。可以通过优化事务粒度、使用批量操作等方式来提高性能。