0

我虽然我想要的很简单,但我找不到任何解决我的问题的方法。我正在使用 playframework 1.2.3,它使用 Hibernate 作为 JPA。所以我认为playframework与问题无关。

我有一些课程(我省略了不相关的字段)

public class User {
  ...
}

public class Task {
  public DataContainer dataContainer;
}

public class DataContainer  {
  public Session session;
  public User user;
}

public class Session  {
  ...
}

所以我有从 Task 到 DataContainer 以及从 DataContainer 到 Session 的关联,并且 DataContainer 属于一个用户。DataContainers 可以始终具有相同的用户,但每个实例的会话必须不同。并且任务的 DataContainer 在每个实例中也必须不同。DataContainer 可以有或没有会话(它是可选的)。我只使用单向关联。应该足够了。

换句话说:每个任务必须有一个 DataContainer。每个 DataContainer 必须有一个/相同的 User 并且可以有一个 Session。

要创建数据库模式,我使用 JPA 注释:

@Entity
public class User extends Model {
  ...
}

@Entity
public class Task extends Model {
  @OneToOne(optional = false, cascade = CascadeType.ALL)
  public DataContainer dataContainer;
}

@Entity
public class DataContainer extends Model  {
  @OneToOne(optional = true, cascade = CascadeType.ALL)
  public Session session;

  @ManyToOne(optional = false, cascade = CascadeType.ALL)
  public User user;
}

@Entity
public class Session extends Model {
  ...
}

顺便说一句:模型是一个播放类,并提供主要 id 作为 long 类型。

当我为每个实体创建一些对象并“连接它们”时,我的意思是关联,它工作正常。但是当我尝试删除一个 Session 时,我得到了一个违反约束的异常,因为 DataContainer 仍然引用我要删除的 Session。

我希望 DataContainer 的 Session(字段)将分别设置为 null 外键(session_id)应该在数据库中取消设置。这没关系,因为它是可选的。

我不知道,我想我有很多问题。我使用了正确的注释@OneToOne吗?

我在互联网上发现了一些额外的注释和属性: @JoinColumn以及mappedBy反向关系的属性。但我没有,因为它不是双向的。或者是双向关联。本质上?

另一种尝试是使用在更新或删除时@OnDelete(action = OnDeleteAction.CASCADE)从 s 更改为的约束:NO ACTION

ADD CONSTRAINT fk4745c17e6a46a56 FOREIGN KEY (session_id)
      REFERENCES annotation_session (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE CASCADE;

但在这种情况下,当我删除会话时,DataContainer 和 User 将被删除。这对我来说是错误的。


编辑: 我正在使用 postgresql 9,jdbc 的东西包含在 play 中,我唯一的 db 配置是
db=postgres://app:app@localhost:5432/app

4

2 回答 2

3

您应该使用 OneToOne 还是 ManyToOne?使用最能描述情况的关联。如果多个 DataContainer 可能具有相同的会话,则它是 ManyToOne。如果只有一个 DataContainer 可能有一个给定的会话,那么它就是 OneToOne。

现在,如果您删除会话,并且 DataContainer 仍然引用它,那么您当然会遇到异常。这就是 FK 的用途:它确保您不能拥有引用不存在会话的 DataContainer。因此,要删除会话,您应该首先更新所有引用它的数据容器。

由于您的关联是单向的,因此您需要一个查询来执行此操作:

select dc from DataContainer dc where dc.session = :session

执行此查询,遍历结果,并将会话设置为空。然后,删除会话。

如果您的关联是双向的,您可以简单地执行以下操作:

for DataContainer dc : session.getDataContainers() {
    dc.setSession(null);
}

您还可以使用更新查询在单个查询中完成所有操作。但请注意,不会对会话中已加载的 DataContainer 进行这些更改:

update DataContainer dc set dc.session = null where dc.session = :session
于 2012-06-08T16:36:39.153 回答
0

不确定,但是使用的数据库是什么?它是由休眠自动加载的,还是您使用自己的 DDL 查询来创建它。如果是,请检查是否在数据库级别建立了约束,验证 DataContainer 表中的 sessionId 外键是否可为空

于 2012-06-08T16:13:03.887 回答