【问题标题】:Returning the entities with no parents?返回没有父母的实体?
【发布时间】:2015-05-28 08:57:08
【问题描述】:

我有以下在图表中使用的类,因此可以有父母和孩子:

@Entity(name = "PedagogicalNeed")
@Table(name = "PedagogicalNeed")
public class PedagogicalNeedEntity extends AbstractEntity implements PedagogicalNeed {

    private String name;

    private String description;

    @ManyToMany(targetEntity = PedagogicalNeedEntity.class, fetch = FetchType.LAZY)
    private Set<PedagogicalNeed> parents = new HashSet<PedagogicalNeed>(0);

    @ManyToMany(targetEntity = PedagogicalNeedEntity.class, fetch = FetchType.LAZY)
    private Set<PedagogicalNeed> children = new HashSet<PedagogicalNeed>(0);

    ...
}

超类AbstractEntity用于定义@Id字段:

@MappedSuperclass
public abstract class AbstractEntity implements Serializable {
    ...
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    ...
}

我有以下请求,它应该返回“父母”集合为空的实体。

@Query("SELECT pn FROM PedagogicalNeed pn WHERE pn.parents IS EMPTY")
Iterable<PedagogicalNeedEntity> findRoots();

我的问题是,当这个请求被执行时,我得到了以下“NOT NULL 约束违规”:

Caused by: org.hsqldb.HsqlException: integrity constraint violation: NOT NULL check constraint; SYS_CT_10102 table: PEDAGOGICALNEED_PEDAGOGICALNEED column: PARENTS_ID

问题:如何返回没有父母的实体?

UPDATE:生成的架构如下:

CREATE MEMORY TABLE PUBLIC.PEDAGOGICALNEED(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL PRIMARY KEY,DESCRIPTION VARCHAR(255),NAME VARCHAR(255))
ALTER TABLE PUBLIC.PEDAGOGICALNEED ALTER COLUMN ID RESTART WITH 1
CREATE MEMORY TABLE PUBLIC.PEDAGOGICALNEED_PEDAGOGICALNEED(
    PEDAGOGICALNEED_ID BIGINT NOT NULL,
    PARENTS_ID BIGINT NOT NULL,
    CHILDREN_ID BIGINT NOT NULL,
    PRIMARY KEY(PEDAGOGICALNEED_ID,CHILDREN_ID),
    CONSTRAINT FK_GSX50JH17YMGUMDQ7ADD0Y0Y5 FOREIGN KEY(PARENTS_ID) REFERENCES PUBLIC.PEDAGOGICALNEED(ID),
    CONSTRAINT FK_L6QY3L7AMTRVX1O62WVS6C9X8 FOREIGN KEY(PEDAGOGICALNEED_ID) REFERENCES PUBLIC.PEDAGOGICALNEED(ID),
    CONSTRAINT FK_EHV0QET4GVHBX733YJV6Q9KQD FOREIGN KEY(CHILDREN_ID) REFERENCES PUBLIC.PEDAGOGICALNEED(ID))

我不明白这个映射,为什么只有一个表“PEDAGOGICALNEED_PEDAGOGICALNEED”三个字段? PedagogicalNeedEntity 与其父实体之间的链接与同一实体与其子实体之间的链接无关。所以应该有两张表而不是一张,每张表只有两列:

  • 第一个表应该有 (PEDAGOGICALNEED_ID, CHILDREN_ID) 作为键
  • 第二个表应该有 (PEDAGOGICALNEED_ID, PARENTS_ID) 作为键

【问题讨论】:

    标签: java sql jpa


    【解决方案1】:

    PedagogicalNeedEntitys 之间的多对多的数据库表 PEDAGOGICALNEED_PEDAGOGICALNEED 有一个永远不会填充的列 PEDAGOGICALNEED_ID

    在 PedagogicalNeedEntity 中为这样的 ID 列提供列定义(伪代码):

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
    

    您可以自己提供 id(在持久化实体之前)或使用生成的 id 值。

    重要提示:为避免冗余,您不应在两种关系上都使用@JoinTable。在一种关系上使用@JoinTable,在另一种关系上使用mappedBy

    一个简单的例子,展示了如何创建这样一个“节点网络”(用 Hibernate 4.3.8 和 Derby 10.9.1.0 测试):

    @Entity
    public class Node {
    
        @Id
        public int id;
    
        public String nodeName;
    
        @ManyToMany
        @JoinTable(name="relationships")
        public List<Node> parents;
    
        @ManyToMany(mappedBy="parents")
        public List<Node> children;
    
        public static void main(String[] args) {
            EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("persistenceUnit");
            EntityManager entityManager = entityManagerFactory.createEntityManager();
            EntityTransaction transaction = entityManager.getTransaction();
            transaction.begin();
            entityManager.persist(new Node());
            transaction.commit();
            entityManager.close();
        }
    
    }
    

    这将创建两个表:

    • 表节点
      • 列 ID(整数)
      • 列 NODENAME(varchar(255) 可为空)
    • 表关系
      • 列 PARENTS_ID (int)
      • 列 CHILDREN_ID (int)

    【讨论】:

    • @Id 字段在AbstractEntity(超类)中定义。我会更新我的问题。
    • @geceo 你能尝试使用strategy=GenerationType.IDENTITY(只是猜测)
    • 我很高兴你警告我我的错误,你完全正确地认为我通过使用两次 @JoinTable 引入了冗余。我现在对映射的工作方式有了更好的理解。非常感谢!
    【解决方案2】:

    这不是由查询引起的异常。它必须是在查询之前触发的持久化操作的异常。

    持久性提供程序必须在执行查询之前刷新所有 dml 语句。

    数据库上的 PEDAGOGICALNEED_ID 列不能为空,但您没有为该列提供值。

    查询看起来不错。

    【讨论】:

      【解决方案3】:

      更新:这不起作用,请参阅最佳答案以了解如何获取单个连接表。


      问题解决了!我只需要指定“连接表”的名称:

      @ManyToMany(targetEntity = PedagogicalNeedEntity.class, fetch = FetchType.LAZY)
      @JoinTable(name="PedagogicalNeed_parents")
      private Set<PedagogicalNeed> parents = new HashSet<PedagogicalNeed>(0);
      
      @ManyToMany(targetEntity = PedagogicalNeedEntity.class, fetch = FetchType.LAZY)
      @JoinTable(name="PedagogicalNeed_children")
      private Set<PedagogicalNeed> children = new HashSet<PedagogicalNeed>(0);
      

      感谢您的帮助!

      【讨论】:

      • 我很困惑,我以为你想要一个映射表并在那里存储父关系。然后可以根据该数据计算子关系。否则,您的数据库中有很多冗余。考虑在你的孩子中使用mappedBy - 关系而不是JoinTable
      • 是的,我认为你是对的,我已经引入了冗余。关于您的建议,我尝试了子关系中的“mappedBy”,但它生成了一个包含三列(PedagogicalNeed_ID、Parent_ID、Child_ID)的中间表,我想不出该表中有任何有意义的 3-uple。因此,使用@JoinTable 似乎是仅获得 2 列的唯一方法。如果您知道如何避免冗余,我很感兴趣。
      • 我更新了我的答案以包含一个经过测试的示例,该示例具有双向多对多关系但只有一个中间表。
      • 谢谢,我会在我的项目再次编译后立即对此进行测试(目前已完全损坏)。
      • 我很高兴您警告我我的错误,您完全正确,我通过使用两次 @JoinTable 引入了冗余。我现在对映射的工作方式有了更好的理解。非常感谢!
      猜你喜欢
      • 2019-01-17
      • 1970-01-01
      • 2021-06-06
      • 1970-01-01
      • 1970-01-01
      • 2017-07-04
      • 2014-07-06
      • 1970-01-01
      • 2021-07-16
      相关资源
      最近更新 更多