JPA:如何添加递归非空关系的第一个实体

发布于 2024-10-28 03:56:25 字数 1048 浏览 1 评论 0原文

如何添加递归非空关系的第一个实体?

当尝试对用户实体使用我的默认审核防护(EntityListener)时会出现问题。 Hibernate 无法插入第一个用户。这个例子是情况的简化:

@Entity
class User {
    @Id @GeneratedValue
    private Long id;
    @Basic
    private String name;
    @ManyToOne(optional=false)
    @JoinColumn(nullable=false,updatable=false)
    private User createdBy;

    // getters & setters
    // equals & hashcode are based on name
}

我尝试过类似的操作:

User user = new User();
user.setName( "Some one" );
user.setCreatedBy( user );

Hibernate 失败,根异常是:

 com.microsoft.sqlserver.jdbc.SQLServerException: Cannot insert the value NULL into 
column 'createdby_id', table 'mydb.user'; column does not allow nulls. INSERT fails.

我知道几种解决方法:手动插入第一个实体(SQL 插入)或设置 nullable=true。首先很烦人,其次是完整性失败点(需要自定义审计侦听器和数据库触发器)。有更好的选择吗?或者,换句话说:是否有仅 JPA 的解决方案?

我的平台是:SQL Server 2008 和 Hibernate 3.6 作为 JPA2 提供程序。

编辑:起初,我使用的是persist()。我尝试了 merge() ,它可以做出更智能的猜测,但没有区别。我也尝试了 CascadeType.MERGE 。

How to add the first entity of recursive non-null relation?

The trouble occurs when trying to use my default audit guard (EntityListener) for user entities. Hibernate isnt able to insert first user. The example is simplification of situation:

@Entity
class User {
    @Id @GeneratedValue
    private Long id;
    @Basic
    private String name;
    @ManyToOne(optional=false)
    @JoinColumn(nullable=false,updatable=false)
    private User createdBy;

    // getters & setters
    // equals & hashcode are based on name
}

And I have tried something like:

User user = new User();
user.setName( "Some one" );
user.setCreatedBy( user );

Hibernate fails and root exception is:

 com.microsoft.sqlserver.jdbc.SQLServerException: Cannot insert the value NULL into 
column 'createdby_id', table 'mydb.user'; column does not allow nulls. INSERT fails.

I know several workarounds: insert first entity manually (SQL insert) or set nullable=true. First is just annoying and second is fail point of integrity (custom audit listener & db trigger required). Is there better options? Or, in the other words: is there JPA-only solution?

My platform is: SQL Server 2008 and Hibernate 3.6 as JPA2 provider.

Edit: At first, I was using persist(). I tried merge() which could make more intelligent guess but no difference. And I tried CascadeType.MERGE too.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

静若繁花 2024-11-04 03:56:25

非空约束要求您在引用字段中插入 user.id,但如果您已将 id 字段设置为自动生成,则直到插入后才知道该值。要解决此问题,您可以使用序列来生成 @Id,

create sequence SEQ_USER

并通过获取该序列的下一个值并将两个字段设置为该值来插入第一个用户。对于来自 JPA 的本机 SQL 查询,这看起来像:

User u = new User();
u.setName("First User");
Query q = em.createNativeQuery("VALUES nextval for SEQ_USER");
Long nextId = Long.valueOf((Integer) q.getSingleResult());
q = em.createNativeQuery("insert into user (id, name, created_by) values (?, ?, ?)");
q.setParameter(1, nextId).setParameter(2, u.getName()).setParameter(3, nextId);
q.executeUpdate();
// don't forget to bring u into persistence context
u = em.find(User.class, nextId);

这有点笨拙,但只需调用一次。所有其他用户都将以正常方式保留(使用 SEQ_USER 作为 @Id 的生成器序列)

或者,您可以编写自己的 SequenceGenerator 来执行此操作:

public class UserIdGenerator implements IdentifierGenerator {

  @Override
  public Serializable generate(SessionImplementor session, Object object) throws HibernateException {

    User o = (User) object;
    Connection connection = session.connection();
    try {
      PreparedStatement ps = connection.prepareStatement("VALUES nextval for SEQ_USER");
      ResultSet rs = ps.executeQuery();
      if (rs.next()) {
        return rs.getLong(1);
      }
    } catch (SQLException e) {
      log.error("Unable to generate Id: " + e.getMessage(), e);
    }
    return null;
  }
}

在 User 中,您可以将该生成器设置为:

@Id
@GenericGenerator(name = "idSource", strategy = "com.example.db.UserIdGenerator", parameters = { @Parameter(name = "sequence", value = "SEQ_USER") })
@GeneratedValue(generator = "idSource")
private Long id;

然后

User u = new User();
u.setName("First User");
u.setCreatedBy(u);
em.persist(u);

按预期工作。

The not null constraint mandates that you insert the user.id in the reference field, but if you have set the id field to be autogenerated, that value is not known until after the insert. To solve this, you could use a sequence to generate the @Id

create sequence SEQ_USER

and insert the first user by fetching the next value of that sequence, and setting both fields to that value. With a native SQL query from JPA this looks like:

User u = new User();
u.setName("First User");
Query q = em.createNativeQuery("VALUES nextval for SEQ_USER");
Long nextId = Long.valueOf((Integer) q.getSingleResult());
q = em.createNativeQuery("insert into user (id, name, created_by) values (?, ?, ?)");
q.setParameter(1, nextId).setParameter(2, u.getName()).setParameter(3, nextId);
q.executeUpdate();
// don't forget to bring u into persistence context
u = em.find(User.class, nextId);

which is a little kludgy but has to be called only once. All other users would be persisted the normal way (using SEQ_USER as Generator sequence for the @Id)

Alternatively, you could write your own SequenceGenerator do just that:

public class UserIdGenerator implements IdentifierGenerator {

  @Override
  public Serializable generate(SessionImplementor session, Object object) throws HibernateException {

    User o = (User) object;
    Connection connection = session.connection();
    try {
      PreparedStatement ps = connection.prepareStatement("VALUES nextval for SEQ_USER");
      ResultSet rs = ps.executeQuery();
      if (rs.next()) {
        return rs.getLong(1);
      }
    } catch (SQLException e) {
      log.error("Unable to generate Id: " + e.getMessage(), e);
    }
    return null;
  }
}

In User, you qould set that generator as:

@Id
@GenericGenerator(name = "idSource", strategy = "com.example.db.UserIdGenerator", parameters = { @Parameter(name = "sequence", value = "SEQ_USER") })
@GeneratedValue(generator = "idSource")
private Long id;

and then

User u = new User();
u.setName("First User");
u.setCreatedBy(u);
em.persist(u);

works as expected.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文