LazyInitializationException与Spring Data JpaRepositories [英] LazyInitializationException with Spring Data JpaRepositories

查看:90
本文介绍了LazyInitializationException与Spring Data JpaRepositories的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下实体:

用户:

  @实体
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 
公共类NewsSource {

@Id
@Column (空值= false)
私人网址;

私人LocalDateTime updateTime;

@OneToMany(cascade = CascadeType.ALL)
私人列表< News> newses;

@ManyToMany(cascade = CascadeType.ALL)
private List< User>用户; UsersRepository和NewsSourcesRepository是来自Spring Data JPA的简单JpaRepositories。他们的配置如下:

  @Configuration 
@EnableTransactionManagement
@PropertySource(classpath:database_config。属性)
@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));

返回新的HikariDataSource(dataSourceConfig);

$ b @Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,Environment env){
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan(pl.mielecmichal.news.entities);

属性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);

返回entityManagerFactoryBean;


$ Be
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
返回transactionManager;
}

}

我的测试在线抛出LazyInitializationException 15.消息是:


未能长期初始化角色集合:
news.entities.users.User.newsSources ,无法
初始化代理 - 无会话




  @Test 
public void cascadeRelationsShouldBeRetrieved()throws MalformedURLException {
NewsSource source = new NewsSource();
source.setUrl(新网址(SOME_URL));
newsSourcesRepository.save(source);
newsSourcesRepository.flush();

User user = new User();
user.setEmail(EMAIL);
列出< 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(),(SOME_URL));

NewsSource savedSource = newsSourcesRepository.findOne(newsSource.getUrl());
assertThat(新用户应该保存在M2M关系中,savedSource.getUsers(),Matchers.contains(user));





如果我注释我的测试为@Transactional异常不会被抛出, m不知道这是解决这个问题的正确方法。

解决方案

ManyToMany注解默认情况下它的抓取类型很懒惰

  FetchType fetch()默认LAZY; 

在您的情况下,User class中的newsSources将被延迟取出。



为简单起见,我们假设您不直接使用Transactional Annotation。对于任何数据库操作,都需要事务。在使用spring data jpa存储库时,Transactional注释应用于所有jpa存储库方法。这些方法调用的事务在调用方法时开始,并在方法执行完成时结束。最后一条语句成立,除非有同一个数据库的外部事务。



考虑以下几行,

  User savedUser = usersRepository.findOne(EMAIL); 
NewsSource newsSource = savedUser.getNewsSources()。get(0);

事务开始并结束于usersRepository.findOne(EMAIL)本身。现在,用户已保存的用户对象具有将被延迟加载的新闻源。因此,当您调用savedUser.getNewsSources()时,它会尝试使用持久性会话延迟加载。由于事务上下文已关闭,因此没有活动的关联会话。



现在,如果将Transactional注释添加到使用Test注释注释的方法中,事务从此处开始, savedUser.getNewsSources()被调用,这个相同的事务将被使用。现在,当您执行savedUser.getNewsSources()时,会有一个关联的会话,因此会正常工作。



将事务注释放在测试方法上没有任何问题。由于映射是懒惰的,所以你必须放置事务注释。在这里,当您直接调用测试方法中的jpa库方法并对懒惰的引用对象执行操作时,您肯定必须在注释的测试方法上使用事务注释。



类似的问题:
LazyInitializationException:未能懒惰地初始化一个集合角色,无法初始化代理 - 没有会话


I have following entities:

User:

@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
}

News Source:

@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 and NewsSourcesRepository are simple JpaRepositories from Spring Data JPA. Their configuration is as follow:

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

}

My test throws an LazyInitializationException on line 15. The message is:

failed to lazily initialize a collection of role: news.entities.users.User.newsSources, could not initialize proxy - no Session

    @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));
    }

If I annotate my test as @Transactional exception is not thrown, but I'm not sure is this a proper way to solve this.

解决方案

ManyToMany annotation by default it's fetch type is lazy

FetchType fetch() default LAZY;

In your case, newsSources in User class will be lazily fetched.

Let's assume that you are directly not using Transactional Annotation for the sake of simplicity. For any database operation a transaction is required. As you are using spring data jpa repositories, Transactional annotation is applied to all jpa repository methods. The transactions for these method calls begin when the method is called and ends when the method execution is finished. The last statement holds unless there is an outer transaction of the same database.

Consider following lines,

User savedUser = usersRepository.findOne(EMAIL);
NewsSource newsSource = savedUser.getNewsSources().get(0);

Transaction starts and ends in the usersRepository.findOne(EMAIL) itself. Now "User savedUser" object has newsSources which will be loaded lazily. So when you call savedUser.getNewsSources(), it tries to load lazily using the persistence session. As the transaction context was already closed there is no active associated session.

Now if you add Transactional annotation to the method annotated with Test annotation, transaction begins here itself and now when savedUser.getNewsSources() is called, this same transaction will be used. And now when you do savedUser.getNewsSources(), there is an associated session with it and therefore will work properly.

There is nothing wrong in putting transactional annotation on test method. As the mapping is lazy, somewhere you have to put transactional annotation. Here as you are directly calling jpa repository method within test method and performing operation on lazy reference object, you definitely have to use transactional annotation on the annotated test method.

Similar question: LazyInitializationException: failed to lazily initialize a collection of roles, could not initialize proxy - no Session

这篇关于LazyInitializationException与Spring Data JpaRepositories的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆