2015-05-06 2 views
5

У меня возникли проблемы с тем, чтобы ассоциация @ManyToOne была загружена lazilly. Я использую fetch = LAZY, но он не работает, когда соединение не производится столбцом первичного ключа.@ManyToOne (fetch = FetchType.LAZY) не работает с столбцом с непервичным ключом

Я знаю, что этот вопрос уже был asked, но я думаю, что на него не ответил должным образом, поэтому я предоставляю подробную информацию, чтобы прояснить проблему.

Это моя модель:

DummyB -> DummyA 

Эти таблицы:

create table dummyA (
    id number(18,0), --pk 
    name varchar2(20) -- unique field 
); 

create table dummyB (
    id number(18,0), 
    dummya_id number(18,0), 
    dummya_name varchar2(20) 
); 

И эти лица:

@Entity 
public class DummyA implements Serializable { 

    private Long id; 
    private String name; 

    @Id 
    public Long getId() { 
     return id; 
    } 

    public void setId(Long id) { 
     this.id = id; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

} 

@Entity 
public class DummyB implements Serializable { 

    private Long id; 
    private DummyA dummyA; 

    @Id 
    public Long getId() { 
     return id; 
    } 

    public void setId(Long id) { 
     this.id = id; 
    } 

    /* Case 1: mapping DummyB -> DummyA by DummyA NON primary key (field name) */ 
    // @ManyToOne(fetch = FetchType.LAZY) 
    // @JoinColumn(name = "dummya_id") 
    // public DummyA getDummyA() { 
    // return dummyA; 
    // } 

    /* Case 2: mapping DummyB -> DummyA by DummyA primary key */ 
    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "dummya_name", referencedColumnName = "name") 
    @LazyToOne(LazyToOneOption.PROXY) 
    public DummyA getDummyA() { 
     return dummyA; 
    } 

    public void setDummyA(DummyA dummyA) { 
     this.dummyA = dummyA; 
    } 

} 

Примечание Метод getDummyA в сущности DummyB является дубликат попробуйте два случая присоединиться к объектам.

Случай 1: отображение DummyB -> DummyA по DummyA первичный ключ

@ManyToOne (скачивает = FetchType.LAZY) @JoinColumn (имя = "dummya_id")

Это прекрасно работает, просто один запрос выполняется для извлечения объектов DummyB.

Случай 2: отображение DummyB -> DummyA по DummyA NON первичного ключа (имя поля)

@ManyToOne (скачивает = FetchType.LAZY) @JoinColumn (имя = "dummya_name", referencedColumnName = имя " ")

Такой же фиктивный выбор выполняется, но сразу после этого выполняется выбор dummyA filtering по имени =? для получения связанного объекта A.

Я использую очень простой JUnit для выполнения фильтрации:

public class DummyTest { 

    @Autowired 
    HibernateTransactionManager transactionManager; 

    @Test 
    @Transactional 
    public void testFindDummyB() throws DAOException { 
     Long idDummyB = 2L; 

     Session session = getCurrentHibernateSession(); 

     List lst = session.createCriteria(DummyB.class) 
       .add(Restrictions.eq("id", idDummyB)).list(); 

     assertTrue(lst.size() > 0); 
    } 

    private Session getCurrentHibernateSession() { 
     return this.transactionManager.getSessionFactory().getCurrentSession(); 
    } 

} 

Мои библиотеки:

  • org.hibernate: зимовать одножильных: баночка: 4.2.17.Final: компилировать
  • org.hibernate.common: hibernate-commons-annotations: jar: 4.0.2.Final: compile
  • org.hibernate.javax.persistence: hibernate-jpa-2.0-api: jar: 1.0.1.Final: компилировать
  • org.hibernate: гибернации-валидатор: баночка: 4.3.2.Final: при условии

Другие вещи, которые я уже пробовал: метод

  • Добавление @LazyToOne hiberante к getDummyA() Безразлично не имеет никакого эффекта.

    @LazyToOne (LazyToOneOption.PROXY) @ManyToOne (выборка = FetchType.LAZY, опционально = истина) @JoinColumn (имя = "dummya_name", referencedColumnName = "название") @LazyToOne (LazyToOneOption.PROXY)

  • Создание внешнего ключа из таблицы DummyB в dummyA (и уникальное ограничение в поле dummya.name) не имеет никакого эффекта.

  • Добавление @Column (unique = true) в метод DummyA getName() не удалось.
  • Установите опцию = true или false, как предложено here.
  • Попытка принудительной ленивой загрузки с использованием setFetchMode в критериях не работает, выбор DummyA продолжает выполняться.

    Список lst = session.createCriteria (DummyB.class) .add (Restrictions.eq ("id", idDummyB)). setFetchMode ("dummyA", FetchMode.SELECT) .list();

Я не могу найти в документации Hibernate в точку, где она относится к этому поведению, поэтому мне интересно, если есть что-то не так в моих аннотаций или я наткнулся на ошибку гибернации.

Может ли кто-нибудь сказать?

ОБНОВЛЕНО по заказу мкр-Dev: Чтобы установить его более ясным:

Является ли это ожидаемое поведение или это ошибка? если это ожидаемое поведение, где оно документировано?

спасибо.

ответ

0

Вы не можете использовать это имя в качестве столбца объединения, потому что для этого нет уникального ограничения. Возможно, это приводит к отображению ManyToMany вместо ManyToOne. Я понятия не имею, согласился ли спящий режим, но в конечном итоге это закончится чем-то неожиданным. Кроме того, я не вижу для этого случая использования. Я советую вам всегда использовать длинный идентификатор в качестве первичного ключа и автоматически отображать это поле. Такая специальная обработка необходима только в том случае, если у вас есть неортодоксальная утилита или должна быть совместима с устаревшим db.

+0

Дело подвергается выше, является упрощение моего проблема, в моем фактическом случае столбец для соединения является уникальным столбцом. О том, если это не имеет смысла, это сложно объяснить, и это не вопрос моего вопроса, я спрашиваю: «Это ожидаемое поведение, если да, то где оно документировано?» – kothvandir

+0

Пожалуйста, исправьте свой вопрос, потому что одно выше, чем неправильно. –

+0

Это именно то, о чем я прошу в своем последнем абзаце, но, как вы просили, я поставил его более ясным. – kothvandir

3

Увидеть то же поведение с Hibernate 5.0.4. @ManyToOne (с обратным OneToMany) и Lazy выборка работает отлично, если столбец соединения является первичным ключом. Если это не так, ленивые разрывы загрузки и Hibernate охотно извлекает все ManyToOne каждый раз при создании объекта. Это может быть катастрофически медленным, если вы делаете Criteria.list(), скажем, 1000 записей. То, что начиналось как единый запрос для 1000 записей, может вставлять в 5000 запросов, чтобы с нетерпением получать множество @ManyToOne с использованием индивидуальных отборов.

Абсолютно ничего, что я смог проверить/изменить, объяснил это каким-либо образом, и я могу надежно воспроизвести его.

Решение, которое я должен был реализовать в мое приложение, которое использует не-PK для объединений был просто мусор @ManyToOne/@OneToMany пары аннотаций и записи коллекции выполняет загрузку вручную (кэширование результатов с транзиторной переменной). Это намного больше, но производительность значительно выше, учитывая, что некоторые из моих объектов имеют 5 или 6 @ManyToOne объектов, и все они были с нетерпением вызваны отдельными выборами Hibernate.

К сожалению, я не могу реорганизовать свою схему, чтобы удовлетворить эту причуду в спящем режиме. Я выполняю проект с участием Heroku Connect и объединения между таблицами при объединении данных из Salesforce.com, используя столбец «sfid» в таблице, который не является первичным ключом.Первичный ключ - это отдельное значение, уникальное для записи в базе данных Heroku Postgres и не может использоваться для объединения, поскольку никакие другие таблицы в базе данных не относятся к этому первичному ключу.

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

1

В случае, если кто-то еще есть проблемы с этим Мы получили его на работу следующим образом:

@ManyToOne(fetch = FetchType.LAZY) 
@JoinColumn(name = "dummya_name", referencedColumnName = "name", insertable = false, updatable = false), 
@LazyToOne(LazyToOneOption.NO_PROXY) 
public DummyA getDummyA() { 
    if (fieldHandler != null) { 
     return (DummyA) fieldHandler.readObject(this, "dummyA", dummyA); 
    } 
    return dummyA; 
} 

public void setDummyA(DummyA dummyA) { 
    if (fieldHandler != null) { 
     this.dummyA = (DummyA) fieldHandler.writeObject(this, "dummyA", this.dummyA, dummyA); 
     return; 
    } 
    this.dummyA= dummyA; 
} 

@Override 
public void setFieldHandler(FieldHandler fieldHandler) { 
    this.fieldHandler = fieldHandler; 
} 

@Override 
public FieldHandler getFieldHandler() { 
    return fieldHandler; 
} 

Это было хорошо объяснившую в Hibernate lazy loading for reverse one to one workaround - how does this work?

0

@peach решение работает для меня. Просто несколько вещей, не было упомянуто:

@Entity 
public class DummyB implements Serializable { 

должен быть

@Entity 
public class DummyB implements Serializable, FieldHandled { 

и если вы используете @JsonIgnoreProperties вы должны добавить fieldHandler

@JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "fieldHandler"}) 
Смежные вопросы