Ленивая загрузка объектов (Lazy loading) Java + Spring + JPA + Hibernate

407
28 февраля 2017, 15:46

Нужно сделать ленивую загрузку для объектов, не вызывая org.hibernate.LazyInitializationException.

конфигурация JPA :

    @Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
    LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
    entityManagerFactory.setDataSource(dataSource);
    entityManagerFactory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
    entityManagerFactory.setJpaDialect(new HibernateJpaDialect());
    entityManagerFactory.setPackagesToScan("education.web.platform");
    entityManagerFactory.setJpaPropertyMap(hibernateJpaProperties());
    return entityManagerFactory;
}
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
    JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
    jpaTransactionManager.setEntityManagerFactory(emf);
    return jpaTransactionManager;
}
private Map<String, ?> hibernateJpaProperties() {
    HashMap<String, String> properties = new HashMap<>();
    properties.put("hibernate.hbm2ddl.auto", environment.getRequiredProperty("hibernate.hbm2ddl.auto"));
    properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));

    return properties;
}
@Bean
public DataSource dataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(environment.getRequiredProperty("hibernate.connection.driver_class"));
    dataSource.setUrl(environment.getRequiredProperty("hibernate.connection.url"));
    dataSource.setUsername(environment.getRequiredProperty("hibernate.connection.username"));
    dataSource.setPassword(environment.getRequiredProperty("hibernate.connection.password"));
    return dataSource;
}

Пример возникновения проблемы:

Загружаемый класс:

@Entity
@Table(name = "theory_tasks")
@PrimaryKeyJoinColumn(name = "task_id")
public class TheoryTask extends Task {
...
@OneToMany(targetEntity = ProbablyAnswer.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "task")
private List<ProbablyAnswer> probablyAnswers = new ArrayList<>();
...

Класс объектов для ленивой загрузки:

@Entity
@Table(name = "probably_answers")
public class ProbablyAnswer implements Serializable{
...
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "task_id")
private Task task;
...

Если метод получающий список объектов в DAO помечен @Transactional то при обращении к списку ProbablyAnswers в TheoryTask они не подгружаются и при передаче его на страницу выбрасывается org.hibernate.LazyInitializationException.

Если пометить @Transactional метод в сервисе или контроллере то ленивая загрузка игнорируется и рекурсивно загружается весь граф объектов.

Метод которым я получаю сущность:

 public List<T> getAll(){
    String genericClassName = persistentClass.toGenericString();
    genericClassName = genericClassName.substring(genericClassName.lastIndexOf('.')+1);
    String hql = "FROM "+genericClassName;
    return  entityManager.createQuery(hql,persistentClass).getResultList();
}

В дальнейшем сущности выводятся на html страницу.

Answer 1

Перед тем как использовать полученный список, проинициализируйте его утилитой Hibernate.initialize(yourList)

public List<T> getAll(){
    String genericClassName = persistentClass.toGenericString();
    genericClassName = genericClassName.substring(genericClassName.lastIndexOf('.')+1);
    String hql = "FROM "+genericClassName;
    List<T> list = entityManager.createQuery(hql,persistentClass).getResultList();
    Hibernate.initialize(list);
    return list;
}
Answer 2

Все сущности с дочерними можно получить одним запросом к БД, для этого переопределите в имплементации Ваш метод и укажите какие дочерние объекты нужно прогрузить, например так:

 public List<TheoryTask> getAll(){   
    String hql = "FROM TheoryTask t LEFT JOIN FETCH t.probablyAnswers";
    return  entityManager.createQuery(hql,TheoryTask.class).getResultList();
}

В БД будет идти один запрос и будет применятся только для этого метода.

READ ALSO
Парсинг строки Java

Парсинг строки Java

Пример строки:

497
почему панель не выводится на экран

почему панель не выводится на экран

field должен быть добавлен в объект класса JWindow или JFrame, которому необходимо задать границы и включить видимостьСама по себе JPanel отрисовываться...

350
Java: блок внутри функции, его польза

Java: блок внутри функции, его польза

Доброго времени сутокСобственно, вопрос в том, какой практический смысл конструкции, когда выделяется блок внутри метода

214
Как дать Label-у имя, используя код?

Как дать Label-у имя, используя код?

Можно ли в java переименовать Label, прописав это в коде самостоятельно?

252