LazyInitializationException 试图获取惰性初始化实例
当我尝试获取惰性初始化实体时,我在 IDE 中看到以下异常消息(我找不到它在代理实体中的存储位置,因此我无法提供此异常的整个堆栈跟踪):
I see the following exception message in my IDE when I try to get lazy initialized entity (I can't find where it is stored in the proxy entity so I can't provide the whole stack trace for this exception):
Method threw 'org.hibernate.LazyInitializationException' exception. Cannot evaluate com.epam.spring.core.domain.UserAccount_$$_jvste6b_4.toString()
这是我在尝试访问要使用的惰性初始化实体的字段后立即得到的堆栈跟踪:
Here is a stack trace I get right after I try to access a field of the lazy initialized entity I want to use:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:286)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
at com.epam.spring.core.domain.UserAccount_$$_jvstfc9_4.getMoney(UserAccount_$$_jvstfc9_4.java)
at com.epam.spring.core.web.rest.controller.BookingController.refill(BookingController.java:128)
我正在使用 Spring Data,配置了 JpaTransactionManager,数据库是 MySql,ORM 提供者是 Hibernate 4.注释 @EnableTransactionManagement 已打开,@Transactional 被放置在我能想象到的任何地方,但没有任何效果.
I'm using Spring Data, configured JpaTransactionManager, database is MySql, ORM provider is Hibernate 4. Annotation @EnableTransactionManagement is on, @Transactional was put everywhere I could imagine but nothing works.
这是一个关系:
@Entity
public class User extends DomainObject implements Serializable {
..
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "user_fk")
private UserAccount userAccount;
..
@Entity
public class UserAccount extends DomainObject {
..
@OneToOne(mappedBy = "userAccount")
private User user;
..
..一个配置:
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty(PROP_NAME_DATABASE_DRIVER));
dataSource.setUrl(env.getRequiredProperty(PROP_NAME_DATABASE_URL));
dataSource.setUsername(env.getRequiredProperty(PROP_NAME_DATABASE_USERNAME));
dataSource.setPassword(env.getRequiredProperty(PROP_NAME_DATABASE_PASSWORD));
return dataSource;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
entityManagerFactoryBean.setPackagesToScan(env.getRequiredProperty(PROP_ENTITYMANAGER_PACKAGES_TO_SCAN));
entityManagerFactoryBean.setJpaProperties(getHibernateProperties());
return entityManagerFactoryBean;
}
@Bean
public JpaTransactionManager transactionManager(@Autowired DataSource dataSource,
@Autowired EntityManagerFactory entityManagerFactory) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
jpaTransactionManager.setDataSource(dataSource);
return jpaTransactionManager;
}
.. 这就是我想要检索 UserAccount 的方式:
.. and this is how I want to retrieve UserAccount:
@RequestMapping(...)
@Transactional()
public void refill(@RequestParam Long userId, @RequestParam Long amount) {
User user = userService.getById(userId);
UserAccount userAccount = user.getUserAccount();
userAccount.setMoney(userAccount.getMoney() + amount);
}
Hibernate 版本是 4.3.8.Final,Spring Data 1.3.4.RELEASE 和 MySql 连接器 5.1.29.
Hibernate version is 4.3.8.Final, Spring Data 1.3.4.RELEASE and MySql connector 5.1.29.
请问我是否需要其他东西.提前谢谢!
Please, ask me if something else is needed. Thank you in advance!
推荐答案
首先要明白问题的根源不是事务.我们有一个事务和一个持久的上下文(会话).使用 @Transactional
注释 Spring 创建一个事务并打开持久化上下文.在方法被调用后,一个持久化的上下文就关闭了.
Firstly, you should understand that the root of the problem is not a transaction. We have a transaction and a persistent context (session). With @Transactional
annotation Spring creates a transaction and open persistent context. After method is invoked a persistent context becomes closed.
当您调用 user.getUserAccount()
时,您有一个包装 UserAccount
的代理类(如果您不加载 UserAccount
用户
).因此,当一个持久性上下文关闭时,在调用 UserAccount
的任何方法期间都会出现 LazyInitializationException
,例如 user.getUserAccount().toString()
代码>.
When you call a user.getUserAccount()
you have a proxy class that wraps UserAccount
(if you don't load UserAccount
with User
). So when a persistent context is closed, you have a LazyInitializationException
during call of any method of UserAccount
, for example user.getUserAccount().toString()
.
@Transactional
在您的情况下仅在 userService
级别上工作.要使 @Transactional
工作,将 @Transactional
注释放在方法上是不够的.您需要使用 Spring Context
中的方法获取类的对象.因此,要更新资金,您可以使用其他服务方法,例如 updateMoney(userId, amount)
.
@Transactional
working only on the userService
level, in your case. To get @Transactional
work, it is not enough to put the @Transactional
annotation on a method. You need to get an object of a class with the method from a Spring Context
. So to update money you can use another service method, for example updateMoney(userId, amount)
.
如果你想在控制器方法上使用 @Transactional
,你需要从 Spring Context
中获取一个控制器.Spring 应该明白,它应该用一个特殊的方法包装每个 @Transactional
方法来打开和关闭持久上下文.另一种方法是使用 Session Per Request Anti 模式.您需要添加一个特殊的 HTTP 过滤器.
If you want to use @Transactional
on the controller method you need to get a controller from the Spring Context
. And Spring should understand, that it should wrap every @Transactional
method with a special method to open and close a persistent context. Other way is to use Session Per Request Anti pattern. You will need to add a special HTTP filter.
https://vladmihalcea.com/the-open-session-in-view-anti-pattern/
相关文章