我正在使用实体类观察一个非常奇怪的行为,并使用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);
}
...
}
我在字段上使用观察点调试过程(在访问或修改字段时应该停止),并且在字段初始化时我看到一个修改,但是当我从查找调用中获得结果时,字段是空。
任何人都可以解释一下,为什么会这样?如果数据库中没有嵌入对象字段的数据(除了在查找调用之后手动设置它),我如何确保该字段不为空。
答案 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上。看到这个答案:
答案 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方法,它开始起作用