当我尝试获取懒惰的初始化实体时,我在IDE中看到以下异常消息(我无法找到它在代理实体中的存储位置,因此无法为该异常提供整个堆栈跟踪):
Method threw 'org.hibernate.LazyInitializationException' exception. Cannot evaluate com.epam.spring.core.domain.UserAccount_$$_jvste6b_4.toString()
这是我尝试访问要使用的惰性初始化实体的字段后得到的堆栈跟踪:
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提供程序是Hibernate4。注释@EnableTransactionManagement处于启用状态,@ Transactional随处可见,但无济于事。
这是一个关系:
@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的方式:
@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。
请问我是否还需要其他东西。先感谢您!
首先,您应该了解问题的根源不是交易。我们有一个事务和一个持久的上下文(会话)。使用@Transactional注释,Spring创建一个事务并打开持久上下文。调用方法后,持久上下文将关闭。
@Transactional
当你调用一个user.getUserAccount()你有一个代理类,包装UserAccount(如果你不加载UserAccount用User)。因此,当关闭持久性上下文时,您可以LazyInitializationException调用的任何方法UserAccount,例如toString()。
user.getUserAccount()
UserAccount
User
LazyInitializationException
toString()
@Transactional``userService在您的情况下,只能在级别上工作。要开始@Transactional工作,仅将@Transactional注释放在方法上是不够的。您需要使用的方法获取类的对象Spring Context。因此,要更新货币,您可以使用其他服务方法,例如updateMoney(userId, amount)。
@Transactional``userService
Spring Context
updateMoney(userId, amount)
如果要@Transactional在controller方法上使用,则需要从中获取控制器Spring Context。Spring应该理解,它应该@Transactional用特殊方法包装每个方法,以打开和关闭持久性上下文。其他方法是使用“每个请求的会话数”反模式。您将需要添加特殊的HTTP过滤器。
https://vladmihalcea.com/the-open-session-in-view-anti- pattern/