在jpa中保存对象之前,我如何知道id

mst*_*tzn 11 java oracle jpa jpa-2.0

我有一个新对象.我想在保存之前知道id.可能吗?或者还有另一种方法吗?我使用jpa作为orm和oracle作为数据库.

@Id
@Basic(optional = false)
@Column(name = "ID", nullable = false)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "woTypeSeq")
private Long id;
Run Code Online (Sandbox Code Playgroud)

我的实体中有一个代码字段.如果用户没有输入code字段的值,我想将代码设置为实体的id.如果我持久化实体,当然我可以获取id并将代码值设置为id,但这是额外的查询数据库.

我想做那样的事

if(entity.getCode()==null) {
   entity.setCode(entity.getId);
   jpaDao.saveOrUpdate(entity);
}
Run Code Online (Sandbox Code Playgroud)

mst*_*tzn 11

经过长时间的研究,我终于找到了解决方案.

实际上,如果在jpa中使用序列生成器,当然在将数据保存到数据库之前无法获取实体的id,因为下一个id将由数据库序列分配.

如果您使用自定义生成器,有一种获取ID的方法,您可以在保存之前获取id.这是简单的实现:

public class CustomGenerator extends IdentityGenerator implements Configurable {

    private IdentifierGenerator defaultGenerator;

    public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
        Long idValue = (Long)defaultGenerator.generate(session, object);
        //idValue will be assigned your entity id
        return idValue;
    }

    @Override
    public void configure(Type type, Properties params, Dialect d) throws MappingException {
        DefaultIdentifierGeneratorFactory dd = new DefaultIdentifierGeneratorFactory();
        dd.setDialect(d);
        defaultGenerator = dd.createIdentifierGenerator("sequence", type, params);
    }
}
Run Code Online (Sandbox Code Playgroud)

将CustomGenerator用于id字段:

@Id
@Basic(optional = false)
@Column(name = "ID", nullable = false)
@GenericGenerator(name = "seq_id", strategy = "com.yoncabt.abys.core.listener.CustomGenerator", parameters = { @Parameter(name = "sequence", value = "II_FIRM_DOC_PRM_SEQ") })
@GeneratedValue(generator = "seq_id")
private Long id;
Run Code Online (Sandbox Code Playgroud)


Shi*_*gon 10

使用@GeneratedValue类型id,您无法提前知道该值(在实际编写之前).但是,一旦你坚持你的Bean,id字段将填充在该bean实例中,你可以获得它而无需对它进行额外的查询.换一种说法:

MyEntiry myEnt = new MyEntity(); //the id field is null now
entityManager.persist(myEnt);//the id field is populated in myEnt now
Long id = myEnt.getId();
Run Code Online (Sandbox Code Playgroud)

此外,根据您EntityManager的配置方式,您可能还需要首先提交事务(手动),然后才能获取该ID.

根据评论更新

如果要在保存和/或更新实体之前拦截并对实体执行某些操作,可以使用JPA LifeCycle Listeners(如果您使用的是JPA版本2):使用侦听器和回调处理JPA生命周期事件.

基本上,你可以做一个validate()方法在bean,用其标注 @PrePersist@PreUpdate做验证它(如果代码是空集到的ID值)

每2条评论更新一次

是的,我老实说刚刚想到这个:如果id是自动生成的,它可能会在pre-persist事件之后填充,这样当你的pre-persist代码执行时你仍然不知道id是什么(您可能还会注意到,在示例中,您链接到ID不是自动生成的,而是手动设置).在这种情况下,您可以做的是向您的实体添加一个布尔字段(注释,@Transient因此它不会被持久化)调用isCodeEmpty(默认情况下,如果没有专门初始化,则为false).然后在您带@PrePersist注释的方法中检查代码字段的值是否为空,如果是,则将布尔值设置为true.然后你重构你的setId(...)方法,这样(除了设置id字段)它将检查这个布尔值,如果为true,则将代码字段的值设置为id字段的值:

public class YourEntity {

@Transient
private boolean isCodeEmpty;

public void setId(Whatever id) {
 this.id = id;
 if(isCodeEmpty) {
  this.code = id;
  //if necessary:
  //this.isCodeEmpty = false;
 }
}

@PrePersist
public void validate() {
 if(code == null || code.isEmpty()) {
  isCodeEmpty = true;
 }

}


}
Run Code Online (Sandbox Code Playgroud)