В Hibernate у нас есть два класса со следующими классами с отображением JPA:
package com.example.hibernate
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class Foo {
private long id;
private Bar bar;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@ManyToOne(fetch = FetchType.LAZY)
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
}
package com.example.hibernate
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
public class Bar {
private long id;
private String title;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
Теперь, когда мы загружаем из базы данных объект из класса Foo, используя сессию, получаем, например,:
Foo foo = (Foo) session.get (Foo.class, 1 / * или некоторый другой идентификатор, который существует в БД * /);
Член Bar в foo - это прокси-объект (в нашем случае javassist proxy, но он может быть cglib в зависимости от используемого вами поставщика байт-кода), который не инициализируется.
Если затем вы используете session.get для извлечения объекта Bar, который является членом только что загруженного класса Foo (мы находимся в том же сеансе), Hibernate не выполняет другой запрос к БД и извлекает объект из кэша сеанса (первого уровня) , Проблема в том, что это прокси для класса Bar, который не инициализирован, и попытка вызвать этот объект getId () вернет 0, а getTitle () вернет ноль.
Наше текущее решение довольно уродливо и проверяет, является ли объект, возвращаемый из get, прокси-сервером, вот код (формирует общую реализацию DAO):
@SuppressWarnings("unchecked")
@Override
@Transactional(readOnly = true)
public <T extends IEntity> T get(Class<T> clazz, Serializable primaryKey) throws DataAccessException {
T entity = (T) currentSession().get(clazz, primaryKey);
if (entity != null) {
if (LOG.isWarnEnabled()) {
LOG.warn("Object not found for class " + clazz.getName() + " with primary key " + primaryKey);
}
} else if (entity instanceof HibernateProxy){ // TODO: force initialization due to Hibernate bug
HibernateProxy proxy = (HibernateProxy)entity;
if (!Hibernate.isInitialized(proxy)) {
Hibernate.initialize(proxy);
}
entity = (T)proxy.getHibernateLazyInitializer().getImplementation();
}
return entity;
}
Есть ли лучший способ сделать это, не смог найти решение на форуме Hibernate и не нашел проблему в JIRA Hibernate.
Примечание: мы не можем просто использовать foo.getBar () (которая будет правильно инициализировать прокси), чтобы получить объект класса Bar, потому что операция session.get для извлечения объекта Bar не знает (или заботится об этом) что класс Bar также является ленивым членом объекта Foo, который был только что получен.