我使用Oracle XE 10g,Hibernate 3.5,JPA 2.0进行了设置。有一个带主键的简单表,它由插入时的数据库触发器生成。触发器从序列中获取值。触发/序列构造由Oracle XE完成。
实际问题是:如何在EntityManager.persist后获取我的实体中Id的当前值?
我试过了:
@Id
private long id;
- > id为0
;
@Id
@Generated(GenerationTime.ALWAYS)
@Column(insertable = false, updatable = false)
private long id;
- > id为0
;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private long id;
- >失败,因为Hibernate试图直接查询序列(不存在)。
ID在前两种方法中在数据库中生成,但我的对象中没有值。
答案 0 :(得分:7)
与@JB Nizet的评论相反,我实际上可以想到为什么我们让触发器分配ID的许多原因:执行存储过程,手动执行SQL和在Hibernate中运行本机查询,仅举几例。 / p>
我个人发现以下解决方案相当令人满意。它允许Hibernate找到最大ID,并在每次调用insert语句时使其递增。但是当语句命中数据库时,ID会被触发器生成的ID忽略并覆盖,因此没有uniqueness in a cluster problem:
@Id
@GeneratedValue(generator="increment")
@GenericGenerator(name="increment", strategy = "increment")
private Long id;
最大的缺点是@GenericGenerator是一个Hibernate注释,因此你失去了JPA的可移植性。程序员也不清楚这个ID实际上是否与一个序列相关联。
另一种方法是修改触发器,使其仅在ID为null时递增序列。请参阅“Hibernate issue with Oracle Trigger for generating id from a sequence”。在大多数情况下,这是最好的解决方案,因为它清楚地显示ID与序列相关联。我唯一的抱怨是它为user / hibernate提供了插入任何ID的选项,而不是在第一时间实际查询序列。
答案 1 :(得分:2)
除非你受到触发器的限制,否则这似乎是对序列的一种混淆,它似乎超出了正常的Hibernate生命周期。为什么不直接调用序列:
@SequenceGenerator(name="alias_for_my_sequence", sequenceName="seq_name_in_oracle")
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="alias_for_my_sequence")
@Id
private Long id;
然后你会得到这个值,因为Hibernate直接参与了这一代,事后并没有发生什么事情。
答案 2 :(得分:1)
您可以在Oracle会话中交换db生成的密钥。为此,插入触发器必须调用
dbms_session.set_identifier(new_id)
从您的代码中,您可以使用查询
检索新的键值String sql = "SELECT sys_context('USERENV', 'CLIENT_IDENTIFIER') FROM dual";
Query query = em.createNativeQuery(sql);
String new_id = (String) query.getSingleResult();
使用相同的EntityManager实例来保存新实体并检索生成的键值非常重要。