2008-09-15 6 views
1

В Hibernate мы имеем два класса со следующими классами с JPA отображения:Ленивая загрузка собственности и session.get проблема

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/* или какой-либо другой идентификатор, который существует в БД * /); Член панели foo - это прокси-объект (в нашем случае прокси-сервер javassist, но он может быть cglib одним в зависимости от используемого вами оператора байт-кода), который не инициализируется. Если вы затем используете session.get для извлечения объекта Bar, который является членом только что загруженного класса Foo (мы находимся в том же сеансе), Hibernate не выдает другой запрос БД и не извлекает объект из сеанса (первый уровень) кэш. Проблема в том, что это прокси для класса Bar, который не инициализирован и пытается вызвать этот объект. GetId() вернет 0, а getTitle() вернет значение null. Наше текущее решение является довольно некрасиво и проверяет, является ли объект, возвращаемый из ГЭТ является прокси вот код (образуют общую реализацию 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, который был только что извлечен.

ответ

0

Эта проблема действительно не наблюдается, хотя мы получаем прерывистые ошибки Lazy Load - так что, возможно, у нас есть одна и та же проблема, во всяком случае, это вариант использования другого сеанса для загрузки объекта Bar, который должен загрузить его с нуля, я бы ожидал ...

+0

Загрузка в другой сеанс не является вариантом: реальная проблема более сложна, но короткий ответ заключается в том, что мы не можем. – talg 2008-09-15 06:43:46

0

Я не могу воспроизвести поведение, которое вы видите. Вот мой код:

@Entity 
public class Foo { 
    private Long id; private String name; private Bar bar; 

    public Foo() { } 
    public Foo(String name) { this.name = name; } 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    public Long getId() { return id; } 
    public void setId(Long id) { this.id = id; } 

    @Basic 
    public String getName() { return name; } 
    public void setName(String name) { this.name = name; } 

    @ManyToOne(fetch = FetchType.LAZY) 
    public Bar getBar() { return bar; } 
    public void setBar(Bar bar) { this.bar = bar; } 
} 

@Entity 
public class Bar { 
    private Long id; private String name; 

    public Bar() { } 
    public Bar(String name) { this.name = name; } 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    public Long getId() { return id; } 
    public void setId(Long id) { this.id = id; } 

    @Basic 
    public String getName() { return name; } 
    public void setName(String name) { this.name = name; } 
} 

    public void testGets() { 
     SessionFactory sf = new AnnotationConfiguration() 
      .addPackage("hibtest") 
       .addAnnotatedClass(Foo.class) 
       .addAnnotatedClass(Bar.class) 
      .configure().buildSessionFactory(); 
     Session session = null; 
     Transaction txn = null; 

     // Create needed data 
     try { 
      session = sf.openSession(); 
      txn = session.beginTransaction(); 

      // Create a Bar 
      Bar bar = new Bar("Test Bar"); 
      session.save(bar); 

      // Create a Foo 
      Foo foo = new Foo("Test Foo"); 
      session.save(foo); 

      foo.setBar(bar); 

      txn.commit(); 
     } catch (HibernateException ex) { 
      if (txn != null) txn.rollback(); 
      throw ex; 
     } finally { 
      if (session != null) session.close(); 
     } 

     // Try the fetch 
     try { 
      session = sf.openSession(); 
      Foo foo = (Foo) session.get(Foo.class, 1L); 
      Bar bar = (Bar) session.get(Bar.class, 1L); 
      System.out.println(bar.getName()); 
     } finally { 
      if (session != null) session.close(); 
     } 
    } 

И все это прекрасно работает, как и следовало ожидать.

0

Вам действительно нужно делать ленивую загрузку?
Не могли бы вы установить FetchType в EAGER и всегда ли он загружать (правильно) с помощью соединения?

+0

Мне нужно делать ленивую загрузку, так как это свойство не всегда необходимо (просмотр списка объектов Foo, например) – talg 2008-09-15 14:13:22

1

У меня была аналогичная проблема:

  • я Session.save (nastyItem), чтобы сохранить объект в сессии. Тем не менее, я не заполнил покупателя недвижимости, который отображается как update = "false" insert = "false" (это случается очень часто, когда у вас есть скомпилированный первичный ключ, тогда вы сопоставляете многозначные слова как insert = «false» update = «false»)
  • Я запросил загрузку списка предметов, и элемент, который я только что сохранил, является частью набора результатов
  • Теперь что случилось? Hibernate видит, что элемент уже находится в кеше, а Hibernate не заменяет (возможно, не прерывать мою предыдущую ссылку nastyItem) его с недавно загруженным значением, но использует MY nastyItem, который я сам ввел в кеш сеанса. Хуже того, теперь ломается погрузка покупателя: он содержит null.

Чтобы избежать этих проблем с сеансом, я всегда делаю флеш и ясность после сохранения, слияния, обновления или удаления.Необходимость решить эти неприятные проблемы занимает слишком много времени :-(

0

Вы делаете что-то не так. Я не тестировал ваш код, но вам не нужно принудительно инициализировать прокси-серверы, аксессоры свойств делают это для Если вы используете Hibernate явно, не обращайте внимания на JPA, так как вы уже потеряли переносимость.

Hibernate должен автоматически обнаруживать каждый раз, когда требуется извлечь или записать в db. Если вы выдаете getProperty() из прокси, hibernate или любой другой провайдер jpa должны получить соответствующую строку из базы данных.

Единственная ситуация, в которой я не уверен, что спящий режим - умный достаточно, если вы выдаете save(), а затем выдаете get() с идентификатором сохраненного объекта, может быть проблема, если save() не очистил объект до db.

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

  • Нет связанных вопросов^_^