Spring中No EntityManager with actual transaction available错误解决

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等配置文件中正确配置了EntityManagerFactoryTransactionManager。示例配置如下:

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";
}
}

最佳实践

  • 事务粒度:合理控制事务的粒度,避免事务过长或过短。过长的事务可能会导致锁持有时间过长,影响性能;过短的事务可能会导致频繁的事务开销。
  • 异常处理:在事务方法中正确处理异常,确保事务能够正确回滚。
  • 事务传播行为:根据业务需求选择合适的事务传播行为,如REQUIREDREQUIRES_NEW等。

常见问题

1. @Transactional注解不生效

  • 检查方法访问修饰符@Transactional注解只对公共方法有效。
  • 检查代理机制:确保Spring能够正确创建代理对象。如果使用基于接口的代理,确保类实现了接口;如果使用CGLIB代理,确保类不是final类。
  • 检查配置文件:确保tx:annotation-driven标签已正确配置。

2. 事务嵌套问题

在事务嵌套时,需要注意事务传播行为的设置,避免出现意外的事务行为。例如,在一个事务方法中调用另一个事务方法时,需要根据业务需求选择合适的传播行为。

3. 性能问题

事务会带来一定的性能开销,尤其是在高并发场景下。可以通过优化事务粒度、使用批量操作等方式来提高性能。


Spring中No EntityManager with actual transaction available错误解决
https://119291.xyz/posts/2025-04-28.spring-entitymanager-transaction-error-solve/
作者
ww
发布于
2025年4月28日
许可协议