奇怪的JPA行为,初始化字段为空

时间:2010-01-16 08:05:05

标签: java hibernate jpa

我正在使用实体类观察一个非常奇怪的行为,并使用JPA(hibernate entitymanager 3.3.1.ga)加载此类的对象。 Class有一个(嵌入)字段,在声明中初始化。该字段的setter实现了一个空检查(即在设置null值时会抛出异常)。

...
@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获取此对象时,如果db中没有嵌入对象中指定的字段的数据,则该字段为null。

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

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

我在字段上使用观察点调试过程(在访问或修改字段时应该停止),并且在字段初始化时我看到一个修改,但是当我从查找调用中获得结果时,字段是空。

任何人都可以解释一下,为什么会这样?如果数据库中没有嵌入对象字段的数据(除了在查找调用之后手动设置它),我如何确保该字段不为空。

5 个答案:

答案 0 :(得分:17)

JPA规范没有明确说明如何处理一组表示可嵌入对象的列,这些列都是空的。它可以发出空引用或带有所有空字段的对象实例的信号。在这种情况下,Hibernate会选择一个空引用,但其他JPA实现可能会选择较晚的引用。

永远不会调用你的setter的原因是因为Hibernate通过反射访问你的字段,绕过了你实现的setter。这样做是因为您使用基于字段的访问而不是基于属性的访问。

Chad的答案会提供您正在寻找的功能,但有一点需要注意(见下文)。

  

“......实体的持久状态   由持久性访问   提供者运行时[1]通过   JavaBeans样式属性访问器或   通过实例变量。单身   访问类型(字段或属性访问)   适用于实体层次结构。什么时候   使用注释,放置   任何一个上的映射注释   持久字段或持久字段   实体类的属性   将访问类型指定为   基于现场或基于财产的访问   分别......“[ejb3持久性   规格]

因此,通过将注释向下移动到setter,您告诉JPA您希望使用基于属性的访问而不是基于字段的访问。但是,您应该知道,基于字段的访问 - 与您当前实现的一样 - 优先于基于属性的访问。有一些原因可以阻止基于属性的访问,但是有一个原因是你被迫为所有持久性实体字段添加getter和setter,但是你可能不希望那些相同的字段容易被外部客户端变异。换句话说,使用JPA基于属性的访问会强迫您削弱实体的封装。

答案 1 :(得分:3)

答案是(感谢rcampell),如果嵌入对象的所有数据都为空(在db中),嵌入对象也将为null,尽管它在声明中初始化时。唯一的解决方案似乎是手动设置对象。

@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;
}

对我来说仍感到陌生......

答案 2 :(得分:1)

嗯,您的对象可能会在幕后构建两次。 JPA实现通常会直接设置这些字段。

我认为如果你想让它们被使用,你需要将注释放在Getters和setter上。看到这个答案:

Empty constructors and setters on JPA Entites

答案 3 :(得分:1)

现在是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;
    }
    ...
}

答案 4 :(得分:0)

我遇到了同样的问题,我只是使用@Getter和@setter lombok批注添加了getter和setter方法,它开始起作用