Что такое ленивая стратегия и как она работает? - PullRequest
2 голосов
/ 29 июля 2011

У меня проблема. Я изучаю JPA. Я использую встроенный OpenEJB контейнер в модульных тестах, но работает только @OneToMany(fetch=EAGER). В противном случае коллекция всегда пуста. Я не нашел, как работает ленивая стратегия, как контейнер заполняет данные и при каких обстоятельствах запускает действие загрузки контейнера?

Я прочитал, что действие срабатывает, когда вызывается геттер. Но когда у меня есть код:

@OneToMany(fetch = LAZY, mappedBy="someField")
private Set<AnotherEntities> entities = new Set<AnotherEntities>();
...
public Set<AnotherEntities> getEntities() {
    return entities;
}

Я всегда получаю ноль. Я считаю, что стратегию LAZY нельзя протестировать с помощью встроенного контейнера. Проблема может быть также в двунаправленном отношении.

Есть ли у кого-нибудь еще подобные опыты с тестированием JPA?

Вложения

Реальный контрольный пример с настройкой:

@RunWith(UnitilsJUnit4TestClassRunner.class)
@DataSet("dataSource.xml")
public class UnitilsCheck extends UnitilsJUnit4 {
    private Persister prs;

    public UnitilsCheck() {
        Throwable err = null;
        try {
            Class.forName("org.hsqldb.jdbcDriver").newInstance();
            Properties props = new Properties();
            props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
            props.put("ds", "new://Resource?type=DataSource");
            props.put("ds.JdbcDriver", "org.hsqldb.jdbcDriver");
            props.put("ds.JdbcUrl", "jdbc:hsqldb:mem:PhoneBookDB");
            props.put("ds.UserName", "sa");
            props.put("ds.Password", "");
            props.put("ds.JtaManaged", "true");
            Context context = new InitialContext(props);
            prs = (Persister) context.lookup("PersisterImplRemote");
        }
        catch (Throwable e) {
            e.printStackTrace();
            err = e;
        }
        TestCase.assertNull(err);
    }

    @Test
    public void obtainNickNamesLazily() {
        TestCase.assertNotNull(prs);
        PersistableObject po = prs.findByPrimaryKey("Ferenc");
        TestCase.assertNotNull(po);
        Collection<NickNames> nicks = po.getNickNames();
        TestCase.assertNotNull(nicks);
        TestCase.assertEquals("[Nick name: Kutyafája, belongs to Ferenc]", nicks.toString());
    }
}

Бин Presister - это бин, опосредующий доступ к бинам сущностей. Важный код класса следует:

@PersistenceUnit(unitName="PhonePU")
protected EntityManagerFactory emf;

public PhoneBook findByPrimaryKey(String name) {
    EntityManager em = emf.createEntityManager();

    PhoneBook phonebook = (PhoneBook)em.find(PhoneBook.class, name);
    em.close();

    return phonebook;
}

Entity PhoneBook - это одна строка телефонной книги (также лицо). Один человек может иметь ноль или более псевдонимов. Со стратегией EAGER это работает. С LAZY коллекция всегда нулевая. Может быть проблема в отрыве предметов. (См. OpenEJB - JPA Concepts , часть Кэши и отсоединение.) Но в руководстве написано, что коллекция может быть иногда (больше как много раз) пустой, но не нулевой.

Ответы [ 2 ]

2 голосов
/ 08 августа 2011

Проблема в жизненном цикле сущности. (Geronimo использует OpenJPA, поэтому не вижу учебника OpenJPA, часть Управление жизненным циклом объекта .) Приложение использует транзакции, управляемые контейнером. Каждый вызов метода в компоненте Persiser выполняется в собственном переходе. А контекст постоянства зависит от транзакции. Сущность отключается от контекста в конце транзакции, то есть в конце метода. Я попытался получить сущность и во второй строке тем же методом, чтобы получить коллекцию псевдонимов, и это сработало. Таким образом, проблема была выявлена: я не могу дополнительно получить какие-либо данные сущности из хранилища данных без повторного присоединения сущности к некоторому контексту постоянства. Сущность повторно присоединяется методом EntityManager.merge().

Код нуждается в большем количестве исправлений. Поскольку объект не может получить ссылку EntityManager и повторно присоединить себя, метод, возвращающий псевдонимы, должен быть перемещен в класс Persister. (Комментарий Эврика отмечает критическую линию, вновь присоединяющую сущность.)

public Collection<NickNames> getNickNamesFor(PhoneBook pb) {
    //emf is an EntityManagerFactory reference
    EntityManager em = emf.createEntityManager();
    PhoneBook pb = em.merge(pb); //Heureka!
    Collection<NickNames> nicks = pb.getNickNames();
    em.close();
    return nicks;
}

Коллекция затем получается следующим образом:

//I have a PhoneBook instance pb
//pb.getNickNames() returns null only
//I have a Persister instance pe
nicks = pe.getNickNames(pb);

Вот и все.

Вы можете взглянуть на мой второй вопрос по этой теме, который я задавал на этом форуме. Это вопрос OpenJPA - ленивая загрузка не работает .

0 голосов
/ 29 июля 2011

Как бы я написал код

@Entity
public class MyEntity {

  @OneToMany(fetch = LAZY, mappedBy="someField")
  private Set<AnotherEntities> entities;

  // Constructor for JPA
  // Fields aren't initalized here so that each em.load
  // won't create unnecessary objects
  private MyEntity() {}

  // Factory method for the rest 
  // Have field initialization with default values here
  public static MyEntity create() {
    MyEntity e = new MyEntity();
    e.entities = new Set<AnotherEntities>();
    return e;
  }

  public Set<AnotherEntities> getEntities() {
    return entities;
  }

}

Идея № 2:

Я просто подумал, что порядок операций при выборке EAGER и LAZY может отличаться, т.е. выборка EAGER может

  1. Поле объявления entities
  2. Значение выборки для entities (я бы предположил null)
  3. Установить значение entities на new Set<T>()

пока ленивый может

  1. Объявление поля `сущности
  2. установить значение от entities до new Set<T>()
  3. Получите значение для entities (я бы предположил null) '

Нужно найти цитату и для этого.

Идея № 1: (Не правильный ответ)

Что если бы вы аннотировали геттер вместо поля? Это должно дать JPA команду использовать геттеры и сеттеры вместо доступа к полям.

В API персистентности Java сущность может иметь на основе полей или доступ на основе собственности доступ. В полевом доступе , поставщик сохраняемости получает доступ к состоянию объекта напрямую через его экземпляр переменные . В доступе на основе свойств поставщик сохраняемости использует Методы доступа get / set в стиле JavaBeans для доступа к объектам постоянные свойства.

С API персистентности Java - более простая модель программирования для персистентности сущности

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...