2015-12-20 2 views
2

У меня есть следующие объекты:LazyInitializationException с JpaRepositories Spring Data

Пользователь:

@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 простые JpaRepositories от Spring Data JPA. Их конфигурация выглядит следующим образом:

@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

Я не пишу никаких запросов. Весенние данные выполняют эту работу. –

+0

Это нормально, но вы можете написать свой собственный запрос, используя анонимность @Query в интерфейсе репозитория. – Pulkit

ответ

4

ManyToMany аннотаций по умолчанию это тип выборки ленив

FetchType fetch() default LAZY; 

В вашем случае, newsSources в классе пользователя будет лениво неправдоподобным.

Предположим, что вы просто не используете Transactional Annotation для простоты. Для любой операции с базой данных требуется транзакция. Поскольку вы используете репозитории java-данных весны, аннотация Transactional применяется ко всем методам репозитория jpa. Операции для этих вызовов метода начинаются, когда метод вызывается и заканчивается, когда выполнение метода завершено. Последний оператор выполняется, если нет внешней транзакции одной базы данных.

Рассмотрим следующие строки,

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

сделка начинается и заканчивается в usersRepository.findOne (EMAIL) сам. Теперь объект «User savedUser» имеет newsSources, который будет загружен лениво. Поэтому, когда вы вызываете savedUser.getNewsSources(), он пытается лениво загружать сеанс персистентности. Поскольку контекст транзакции уже закрыт, активный сеанс связи отсутствует.

Теперь, если вы добавите аннотацию Transactional к методу, аннотируемому аннотацией Test, транзакция начинается здесь сама, и теперь, когда вызывается safeUser.getNewsSources(), эта же транзакция будет использоваться. И теперь, когда вы сохранилиUser.getNewsSources(), с ним связан связанный сеанс и, следовательно, будет работать правильно.

Нет ничего плохого в создании аннотации транзакций по методу тестирования. Поскольку отображение является ленивым, где-то вы должны положить транзакционную аннотацию. Здесь, когда вы напрямую вызываете метод репозитория jpa в методе тестирования и выполняете операцию на ленивом ссылочном объекте, вам определенно нужно использовать транзакционную аннотацию по аннотированному методу тестирования.

Похожий вопрос: LazyInitializationException: failed to lazily initialize a collection of roles, could not initialize proxy - no Session

+0

Ответ вам очень помогает. Если я определяю интерфейс: открытый интерфейс UserRepository расширяет 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

Смежные вопросы