我有以下实体:
用户:
@Entity
public class User {
@Id
@Column(nullable = false)
private String email = "";
@Column(nullable = false)
private String nickname = "";
@Column(nullable = false)
private String password = "";
@ManyToMany(cascade = CascadeType.ALL)
private List<NewsSource> newsSources;
// getters and setters
}
新闻来源:
@Entity
public class NewsSource {
@Id
@Column(nullable = false)
private URL url;
private LocalDateTime updateTime;
@OneToMany(cascade = CascadeType.ALL)
private List<News> newses;
@ManyToMany(cascade = CascadeType.ALL)
private List<User> users;
}
UsersRepository和NewsSourcesRepository是来自Spring Data JPA的简单JpaRepository。它们的配置如下:
@Configuration
@EnableTransactionManagement
@PropertySource("classpath:database_config.properties")
@EnableJpaRepositories(basePackages = {"news.repositories" })
public class RepositoriesConfiguration {
@Bean(destroyMethod = "close")
DataSource dataSource(Environment env) {
HikariConfig dataSourceConfig = new HikariConfig();
dataSourceConfig.setDriverClassName(env.getRequiredProperty("db.driver"));
dataSourceConfig.setJdbcUrl(env.getRequiredProperty("db.url"));
dataSourceConfig.setUsername(env.getRequiredProperty("db.username"));
dataSourceConfig.setPassword(env.getRequiredProperty("db.password"));
return new HikariDataSource(dataSourceConfig);
}
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, Environment env) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan("pl.mielecmichal.news.entities");
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));
jpaProperties.put("hibernate.hbm2ddl.auto", env.getRequiredProperty("hibernate.hbm2ddl.auto"));
jpaProperties.put("hibernate.ejb.naming_strategy", env.getRequiredProperty("hibernate.ejb.naming_strategy"));
jpaProperties.put("hibernate.show_sql", env.getRequiredProperty("hibernate.show_sql"));
jpaProperties.put("hibernate.format_sql", env.getRequiredProperty("hibernate.format_sql"));
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
@Bean
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
我的测试在第15行上引发了LazyInitializationException。消息为:
@Test
public void cascadeRelationsShouldBeRetrieved() throws MalformedURLException {
NewsSource source = new NewsSource();
source.setUrl(new URL(SOME_URL));
newsSourcesRepository.save(source);
newsSourcesRepository.flush();
User user = new User();
user.setEmail(EMAIL);
List<NewsSource> sources = new ArrayList<>();
sources.add(source);
user.setNewsSources(sources);
usersRepository.save(user);
usersRepository.flush();
User savedUser = usersRepository.findOne(EMAIL);
NewsSource newsSource = savedUser.getNewsSources().get(0);
assertThat("News source should be saved", newsSource.getUrl(), is(SOME_URL));
NewsSource savedSource = newsSourcesRepository.findOne(newsSource.getUrl());
assertThat("New user should be saved in M2M relation", savedSource.getUsers(), Matchers.contains(user));
}
如果我注释我的测试是因为未引发@Transactional异常,但是我不确定这是否是解决此问题的正确方法。
最佳答案
默认情况下,ManyToMany批注的提取类型是惰性的
FetchType fetch() default LAZY;
在您的情况下,将延迟获取User类中的newsSources。
为了简单起见,假设您没有直接使用事务注释。对于任何数据库操作,都需要进行事务处理。当您使用spring数据jpa存储库时,事务注释将应用于所有jpa存储库方法。这些方法调用的事务在调用方法时开始,在方法执行完成时结束。除非存在相同数据库的外部事务,否则最后一条语句将成立。
考虑以下几行,
User savedUser = usersRepository.findOne(EMAIL);
NewsSource newsSource = savedUser.getNewsSources().get(0);
事务在usersRepository.findOne(EMAIL)本身中开始和结束。现在,“User savedUser”对象具有将延迟加载的newsSources。因此,当您调用savedUser.getNewsSources()时,它将尝试使用持久性 session 延迟加载。由于事务上下文已经关闭,因此没有 Activity 的关联 session 。
现在,如果将Transactional注释添加到带有Test注释的方法中,则事务本身将在此处开始,现在,当调用saveUser.getNewsSources()时,将使用相同的事务。现在,当您执行saveedUser.getNewsSources()时,将有一个关联的 session ,因此将正常工作。
将事务注释放在测试方法上没有错。由于映射是懒惰的,因此必须在某处放置事务注释。在这里,由于您是直接在测试方法中调用jpa存储库方法并在惰性引用对象上执行操作,因此您肯定必须在带注释的测试方法上使用事务性注释。
类似的问题:
LazyInitializationException: failed to lazily initialize a collection of roles, could not initialize proxy - no Session
关于java - Spring Data JpaRepositories的LazyInitializationException,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34383721/