2015-12-20 63 views
2

我有以下實體:LazyInitializationException中使用Spring數據JpaRepositories

用戶:

@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從春數據JPA簡單JpaRepositories。它們的配置情況如下:

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

} 

我的測試拋出一個LazyInitializationException中上線15的消息是:

未能懶洋洋地初始化角色的集合: news.entities.users.User。 newsSources,不能 初始化代理 - 沒有會話

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

如果我詮釋我的測試作爲@Transaction除非拋出異常,但我不確定這是否是解決此問題的正確方法。

+0

做您嘗試使用查詢連接抓取? – Pulkit

+0

我不寫任何查詢spring數據做這個工作。 –

+0

那好,但你可以在倉庫界面中使用@Query註釋編寫你自己的查詢。 – Pulkit

回答

4

多對多註釋默認情況下它是提取類型是懶惰

FetchType fetch() default LAZY; 

在你的情況下,在用戶類newsSources將延遲訪。

爲了簡單起見,假設您不直接使用事務性註釋。對於任何數據庫操作,都需要事務。在使用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: failed to lazily initialize a collection of roles, could not initialize proxy - no Session

+0

你的回答有很多幫助。如果我定義了接口:public interface UserRepository extends JpaRepository { User findByUserName(String userName); ....}並使用userRepository.findByUserName(「aa」)。getUserName()將在測試中確定,您能解釋原因嗎? – yuxh

+0

用戶名字段不需要被懶惰地加載,因爲它在同一張表中。而作爲一對多或多對多的人可以懶洋洋地加載,因爲它涉及其他表和連接。這是你要求的嗎? –

+0

請檢查我的問題:https://stackoverflow.com/questions/49080175/spring-data-jpa-getone-throw-lazyinitializationexception-and-findby-not – yuxh

相關問題