java - 具有对特定子类的继承限制的 JPA OneToMany 集合

标签 java hibernate jpa inheritance one-to-many

我正在玩 hibernate + JPA,我正在尝试完成查询以限制集合中的结果。目标是将查询限制在继承级别,因此不连接所有表。

我的代码:

    @Entity
    public static class Holder {

        @Id
        String holderId = UUID.randomUUID().toString();

        @OneToMany(cascade=CascadeType.ALL)
        @JoinColumn(name="subclassID")
        List<A> elements;
    }

    @Entity
    @Inheritance(strategy=InheritanceType.JOINED)
    public static class A {

        @Id
        String pkey = UUID.randomUUID().toString();

        String type = "a";

        String subclassID;
        @Override
        public String toString() {
            return "Class A, pkey: " + pkey + " Subclass " + subclassID;
        }
    }

    @Entity
    public static class B extends A {

        String test;

        String type = "b";

        @Override
        public String toString() {
            return "Class B, pkey: " + pkey + " Subclass " + subclassID + " Test: " + test;
        }
    }

    @Entity
    public static class C extends A {
        String ello;

        String type = "c";

        @Override
        public String toString() {
            return "Class C, pkey: " + pkey + " Subclass " + subclassID + " ello: " + ello;
        }
    }

现在,使用此代码:

        EntityManager em = createEntityManagerFactory.createEntityManager();

        em.getTransaction().begin();
        Holder h = new Holder();
        h.holderId = "hid";
        h.elements = new ArrayList<>();
        A e = new A();
        e.subclassID = h.holderId;
        h.elements.add(e);

        B b = new B();
        b.subclassID = h.holderId;
        b.test = "test";
        h.elements.add(b);

        C c = new C();
        c.subclassID = h.holderId;
        c.ello = "Ello";
        h.elements.add(c);

        em.persist(h);
        em.getTransaction().commit();

        em.getTransaction().begin();
        Holder find = em.find(Holder.class, "hid");
        em.getTransaction().commit();


        find.elements.forEach(x -> System.err.println(x));

它将打印所有子类并正确获取它们。所以我明白了:

Class A, pkey: 5074ec45-5917-47aa-9ce7-b904fbb78a54 Subclass hid
Class B, pkey: a8afc689-1314-4253-8d20-4751526c0d00 Subclass hid Test: test
Class C, pkey: f57137cc-32fd-473c-8310-0f83c7dad2ed Subclass hid ello: Ello

这个,因为我有一个连接策略,必须连接所有子类表,这在某些时候可能会很多。

现在大多数时候我知道我正在查询哪种子类,例如我只想要 A,或者 A 和 B,而不想要 C。

我正在努力弄清楚如何创建一个将考虑到这一点并且不加入不需要的子类的查询。如果有帮助,子类有一个“类型”属性,用于标识不同的子类(参见 A)。

如果您需要更多信息,请告诉我,

谢谢!

编辑:

我尝试按照建议的方法进行操作,结果如下:

  1. 按类型查询。

这是行不通的。无论我选择何种获取策略(惰性或急切),惰性获取仍会查询列表中的所有对象。代码:

Query xx = em.createQuery("SELECT h FROM TestApp$Holder h LEFT JOIN h.elements e where TYPE(e) = TestApp$B", Holder.class);

    System.out.println(" -- -- -- -- -");
    List<Holder> resultList = xx.getResultList();
    resultList.stream().forEach(x -> {
        x.elements.forEach(x2 -> System.err.println(x2));
    });

输出:

14:17:44.466 [main] DEBUG org.hibernate.SQL - select testapp_ho0_.holderId as holderId1_0_ from holder testapp_ho0_ left outer join TestApp$A elements1_ on testapp_ho0_.holderId=elements1_.subclassID left outer join TestApp$B elements1_1_ on elements1_.pkey=elements1_1_.pkey left outer join TestApp$C elements1_2_ on elements1_.pkey=elements1_2_.pkey where case when elements1_1_.pkey is not null then 1 when elements1_2_.pkey is not null then 2 when elements1_.pkey is not null then 0 end=1
Hibernate: select testapp_ho0_.holderId as holderId1_0_ from holder testapp_ho0_ left outer join TestApp$A elements1_ on testapp_ho0_.holderId=elements1_.subclassID left outer join TestApp$B elements1_1_ on elements1_.pkey=elements1_1_.pkey left outer join TestApp$C elements1_2_ on elements1_.pkey=elements1_2_.pkey where case when elements1_1_.pkey is not null then 1 when elements1_2_.pkey is not null then 2 when elements1_.pkey is not null then 0 end=1
14:17:44.468 [main] DEBUG org.hibernate.loader.Loader - Result set row: 0
14:17:44.469 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[io.test.TestApp$Holder#hid]
14:17:44.471 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Resolving associations for [io.test.TestApp$Holder#hid]
14:17:44.472 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Done materializing entity [io.test.TestApp$Holder#hid]
14:17:44.473 [main] DEBUG o.h.l.c.p.AbstractLoadPlanBasedCollectionInitializer - Loading collection: [io.test.TestApp$Holder.elements#hid]
14:17:44.473 [main] DEBUG org.hibernate.SQL - select elements0_.subclassID as subclass2_1_0_, elements0_.pkey as pkey1_1_0_, elements0_.pkey as pkey1_1_1_, elements0_.subclassID as subclass2_1_1_, elements0_.type as type3_1_1_, elements0_1_.test as test1_2_1_, elements0_1_.type as type2_2_1_, elements0_2_.ello as ello1_3_1_, elements0_2_.type as type2_3_1_, case when elements0_1_.pkey is not null then 1 when elements0_2_.pkey is not null then 2 when elements0_.pkey is not null then 0 end as clazz_1_ from TestApp$A elements0_ left outer join TestApp$B elements0_1_ on elements0_.pkey=elements0_1_.pkey left outer join TestApp$C elements0_2_ on elements0_.pkey=elements0_2_.pkey where elements0_.subclassID=?
Hibernate: select elements0_.subclassID as subclass2_1_0_, elements0_.pkey as pkey1_1_0_, elements0_.pkey as pkey1_1_1_, elements0_.subclassID as subclass2_1_1_, elements0_.type as type3_1_1_, elements0_1_.test as test1_2_1_, elements0_1_.type as type2_2_1_, elements0_2_.ello as ello1_3_1_, elements0_2_.type as type2_3_1_, case when elements0_1_.pkey is not null then 1 when elements0_2_.pkey is not null then 2 when elements0_.pkey is not null then 0 end as clazz_1_ from TestApp$A elements0_ left outer join TestApp$B elements0_1_ on elements0_.pkey=elements0_1_.pkey left outer join TestApp$C elements0_2_ on elements0_.pkey=elements0_2_.pkey where elements0_.subclassID=?
14:17:44.475 [main] DEBUG o.h.l.p.e.p.i.ResultSetProcessorImpl - Preparing collection intializer : [io.test.TestApp$Holder.elements#hid]
14:17:44.477 [main] DEBUG o.h.l.p.e.p.i.ResultSetProcessorImpl - Starting ResultSet row #0
14:17:44.478 [main] DEBUG o.h.l.p.e.p.i.CollectionReferenceInitializerImpl - Found row of collection: [io.test.TestApp$Holder.elements#hid]
14:17:44.479 [main] DEBUG o.h.l.p.e.p.i.ResultSetProcessorImpl - Starting ResultSet row #1
14:17:44.479 [main] DEBUG o.h.l.p.e.p.i.CollectionReferenceInitializerImpl - Found row of collection: [io.test.TestApp$Holder.elements#hid]
14:17:44.480 [main] DEBUG o.h.l.p.e.p.i.ResultSetProcessorImpl - Starting ResultSet row #2
14:17:44.480 [main] DEBUG o.h.l.p.e.p.i.CollectionReferenceInitializerImpl - Found row of collection: [io.test.TestApp$Holder.elements#hid]
14:17:44.480 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Resolving associations for [io.test.TestApp$A#c9b6a82a-eed7-48f5-824e-c591537a6be6]
14:17:44.480 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Done materializing entity [io.test.TestApp$A#c9b6a82a-eed7-48f5-824e-c591537a6be6]
14:17:44.480 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Resolving associations for [io.test.TestApp$B#c73dd9ae-9e4e-483e-96cc-4ab802f5dc59]
14:17:44.480 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Done materializing entity [io.test.TestApp$B#c73dd9ae-9e4e-483e-96cc-4ab802f5dc59]
14:17:44.480 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Resolving associations for [io.test.TestApp$C#729f2528-18f5-44da-9dca-2b399b66e183]
14:17:44.480 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Done materializing entity [io.test.TestApp$C#729f2528-18f5-44da-9dca-2b399b66e183]
14:17:44.480 [main] DEBUG o.h.e.l.i.CollectionLoadContext - 1 collections were found in result set for role: io.test.TestApp$Holder.elements
14:17:44.481 [main] DEBUG o.h.e.l.i.CollectionLoadContext - Collection fully initialized: [io.test.TestApp$Holder.elements#hid]
14:17:44.481 [main] DEBUG o.h.e.l.i.CollectionLoadContext - 1 collections initialized for role: io.test.TestApp$Holder.elements
14:17:44.481 [main] DEBUG o.h.r.j.i.ResourceRegistryStandardImpl - HHH000387: ResultSet's statement was not registered
14:17:44.481 [main] DEBUG o.h.l.c.p.AbstractLoadPlanBasedCollectionInitializer - Done loading collection
Class A, pkey: c9b6a82a-eed7-48f5-824e-c591537a6be6 Subclass hid
Class B, pkey: c73dd9ae-9e4e-483e-96cc-4ab802f5dc59 Subclass hid Test: test
Class C, pkey: 729f2528-18f5-44da-9dca-2b399b66e183 Subclass hid ello: Ello

2. Fetch in query 

起作用的是让查询在服务工作时自动获取,但是它仍然连接所有表,尽管该类型清楚地消除了不必要连接的任何结果。 这看起来像这样:

Query xx = em.createQuery("SELECT h FROM TestApp$Holder h LEFT JOIN fetch h.elements e where TYPE(e) = TestApp$B", Holder.class);

        System.out.println(" -- -- -- -- -");
        List<Holder> resultList = xx.getResultList();
        resultList.stream().forEach(x -> {
            x.elements.forEach(x2 -> System.err.println(x2));
        });

输出:

14:20:25.063 [main] DEBUG org.hibernate.SQL - select testapp_ho0_.holderId as holderId1_0_0_, elements1_.pkey as pkey1_1_1_, elements1_.subclassID as subclass2_1_1_, elements1_.type as type3_1_1_, elements1_1_.test as test1_2_1_, elements1_1_.type as type2_2_1_, elements1_2_.ello as ello1_3_1_, elements1_2_.type as type2_3_1_, case when elements1_1_.pkey is not null then 1 when elements1_2_.pkey is not null then 2 when elements1_.pkey is not null then 0 end as clazz_1_, elements1_.subclassID as subclass2_1_0__, elements1_.pkey as pkey1_1_0__ from holder testapp_ho0_ left outer join TestApp$A elements1_ on testapp_ho0_.holderId=elements1_.subclassID left outer join TestApp$B elements1_1_ on elements1_.pkey=elements1_1_.pkey left outer join TestApp$C elements1_2_ on elements1_.pkey=elements1_2_.pkey where case when elements1_1_.pkey is not null then 1 when elements1_2_.pkey is not null then 2 when elements1_.pkey is not null then 0 end=1
Hibernate: select testapp_ho0_.holderId as holderId1_0_0_, elements1_.pkey as pkey1_1_1_, elements1_.subclassID as subclass2_1_1_, elements1_.type as type3_1_1_, elements1_1_.test as test1_2_1_, elements1_1_.type as type2_2_1_, elements1_2_.ello as ello1_3_1_, elements1_2_.type as type2_3_1_, case when elements1_1_.pkey is not null then 1 when elements1_2_.pkey is not null then 2 when elements1_.pkey is not null then 0 end as clazz_1_, elements1_.subclassID as subclass2_1_0__, elements1_.pkey as pkey1_1_0__ from holder testapp_ho0_ left outer join TestApp$A elements1_ on testapp_ho0_.holderId=elements1_.subclassID left outer join TestApp$B elements1_1_ on elements1_.pkey=elements1_1_.pkey left outer join TestApp$C elements1_2_ on elements1_.pkey=elements1_2_.pkey where case when elements1_1_.pkey is not null then 1 when elements1_2_.pkey is not null then 2 when elements1_.pkey is not null then 0 end=1
14:20:25.066 [main] DEBUG org.hibernate.loader.Loader - Result set row: 0
14:20:25.067 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[io.test.TestApp$Holder#hid], EntityKey[io.test.TestApp$A#3c5b7de6-1658-49ab-b90b-eacde31276c3]
14:20:25.071 [main] DEBUG org.hibernate.loader.Loader - Found row of collection: [io.test.TestApp$Holder.elements#hid]
14:20:25.077 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Resolving associations for [io.test.TestApp$Holder#hid]
14:20:25.077 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Done materializing entity [io.test.TestApp$Holder#hid]
14:20:25.077 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Resolving associations for [io.test.TestApp$B#3c5b7de6-1658-49ab-b90b-eacde31276c3]
14:20:25.077 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Done materializing entity [io.test.TestApp$B#3c5b7de6-1658-49ab-b90b-eacde31276c3]
14:20:25.077 [main] DEBUG o.h.e.l.i.CollectionLoadContext - 1 collections were found in result set for role: io.test.TestApp$Holder.elements
14:20:25.077 [main] DEBUG o.h.e.l.i.CollectionLoadContext - Collection fully initialized: [io.test.TestApp$Holder.elements#hid]
14:20:25.078 [main] DEBUG o.h.e.l.i.CollectionLoadContext - 1 collections initialized for role: io.test.TestApp$Holder.elements
Class B, pkey: 3c5b7de6-1658-49ab-b90b-eacde31276c3 Subclass hid Test: test

最佳答案

  1. 如果您直接选择实体层次结构,您可以使用以下内容来限制要返回的类型:

     SELECT a FROM A a WHERE TYPE(a) = A OR TYPE(a) = B
    
  2. 我猜这就是您正在寻找的情况。在这种情况下,以下可能会起作用(但我自己还没有测试过):

    SELECT h FROM Holder h LEFT JOIN h.elements e WHERE TYPE(e) IN (A, B)
    

我在这里使用了LEFt JOIN,因为如果数据库中没有关联的元素,您将不会得到Holder的任何东西,否则。

注意:在 TYPE(e) = B 中,B 是实体的名称,而不是字符串。

关于java - 具有对特定子类的继承限制的 JPA OneToMany 集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37973603/

相关文章:

java - 在过滤器中设置授权 header

java - EntityNotFoundException - Spring mvc 无法查询具有抽象类属性的对象,但可以查询属性对象。并且仅限于某些遗产

java - Hibernate 不将实体持久保存到 MySql - Wildfly 10

java - 如何让 Eclipse JPA(大理)使用另一个项目的 persistence.xml?

java - 打开任务管理器并使用 Java 代码处理其多个选项卡

java - 从 enunciate 中排除一些 jaxb 生成的类

java - Enum 单例有惩罚吗?

java - 在 hibernate 中获取不重复的行

java - Spring data - 启用乐观锁定

java - 在 Spring Data REST 中配置多对多关系,以便删除一个对象不会删除另一个对象