Странное поведение JPA, инициализированное поле равно нулю - PullRequest
16 голосов
/ 16 января 2010

Я наблюдаю очень странное поведение с классом сущностей и загружаю объект этого класса с помощью JPA (hibernate entitymanager 3.3.1.ga). Класс имеет (встроенное) поле, которое инициализируется в объявлении. Установщик поля реализует нулевую проверку (т. Е. Выдает исключение при установке нулевого значения).

...
@Entity
public class Participant extends BaseEntity implements Comparable<Participant> {
   ...
    @Embedded
 private AmsData amsData = new AmsData();

 public void setAmsData(AmsData amsData) {
  Checks.verifyArgNotNull(amsData, "amsdata");
  this.amsData = amsData;
 }
    ...
}

Когда я получаю этот объект с JPA, поле становится пустым, если в БД нет данных для полей, указанных во встроенном объекте.

...
public class ParticipantJpaDao implements ParticipantDao {
 @PersistenceContext
 private EntityManager em;

 @Override
 public Participant getParticipant(Long id) {
  return em.find(Participant.class, id);
 }
    ...
}

Я отладил процесс с помощью контрольной точки на поле (должен остановиться при доступе или изменении поля), и я вижу одну модификацию, когда поле инициализируется, но когда я получаю результат от вызова find, поле нуль.

Кто-нибудь может объяснить, почему это так? Как я могу убедиться, что это поле не равно нулю, даже если в базе данных нет данных для полей встроенного объекта (кроме установки его вручную после вызова find).

Ответы [ 4 ]

17 голосов
/ 16 января 2010

В спецификации JPA явно не сказано, как обрабатывать набор столбцов, представляющих встраиваемый объект, которые все пусты. Он может сигнализировать нулевую ссылку или экземпляр объекта со всеми пустыми полями. В этом случае Hibernate выбирает нулевую ссылку, хотя другие реализации JPA могут выбрать более позднюю.

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

Ответ Чада обеспечит требуемую функциональность, но есть оговорка (см. Ниже).

"... Постоянное состояние объекта доступ по настойчивости время выполнения провайдера [1] либо через Средства доступа к свойствам в стиле JavaBeans или через переменные экземпляра. Один тип доступа (доступ к полю или свойству) применяется к иерархии объектов. когда аннотации используются, размещение аннотации сопоставления либо на постоянные поля или постоянные свойства класса сущности указывает тип доступа как доступ на основе поля или свойства соответственно ... "[ejb3 упорство спецификация]

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

3 голосов
/ 16 января 2010

Ответ (благодаря rcampell), если все данные внедренного объекта равны нулю (в БД), внедренный объект также будет нулевым, хотя когда он инициализируется в объявлении.Единственное решение, кажется, заключается в установке объекта вручную.

@Override
public Participant getParticipant(Long id) {
    Participant participant = em.find(Participant.class, id);
    if(participant != null && participant.getAmsData() == null)
    {
        participant.setAmsData(new AmsData());
    }
    return participant;
}

Мне все еще кажется странным ...

1 голос
/ 14 июня 2018

Сейчас 2018, и у меня была такая же проблема в похожей ситуации.

Используя ваш код в качестве примера, я решил проблему следующим образом:

@Entity
public class Participant extends BaseEntity implements Comparable<Participant> {
    ...
    @Embedded
    private AmsData amsData = new AmsData();

    public void getAmsData(AmsData amsData) {
        Checks.verifyArgNotNull(amsData, "amsdata");
        this.amsData = amsData;
    }

    public AmsData getAmsData(){
        if(amsData == null){
            amsData = new AmsData();
        }
        return amsData;
    }
    ...
}
1 голос
/ 16 января 2010

Ну, вполне возможно, что ваш объект может быть построен за кулисами дважды. Реализации JPA обычно устанавливают эти поля напрямую.

Я думаю, вам нужно поместить аннотации на самих получателей и установщиков, если вы хотите, чтобы они использовались. Смотрите этот ответ:

Пустые конструкторы и установщики в JPA Entites

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