4

I noticed this weird behaviour in my model. (this is my model):

@Entity(name = "A")
public class A {

    @Id
    public String aId = UUID.randomUUID().toString();

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "aId")
    public Set<B> b = new HashSet<>();
}

@Entity(name = "B")
public class B {
    @Id
    public String bId = UUID.randomUUID().toString();
    public String aId;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "cId")
    @NotFound(action = NotFoundAction.IGNORE)
    public C obj;
}

@Entity(name = "C")
@Where(clause = "ran = 'hello'")
public class C {
    @Id
    public String cId = UUID.randomUUID().toString();

    public String ran = UUID.randomUUID().toString();

    public LocalDateTime start;
}

So: Class A has a collection of B, which references C.

C has an annotation "Where" that always evaluates to false.

Consider this test case:

    @Test
    public void test() throws IOException {

        Provider<EntityManager> provider = injector.getProvider(EntityManager.class);
        EntityManager em = provider.get();

        em.getTransaction().begin();

        A a = new A();
        B b = new B();
        b.aId = a.aId;
        a.b.add(b);
        C c = new C();
        b.obj = c;
        c.start = LocalDateTime.now().minusDays(10);
        em.persist(a);
        em.persist(b);
        em.persist(c);

        em.getTransaction().commit();

        em.clear();

        em.getTransaction().begin();

        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<B> createQuery = cb.createQuery(B.class);
        Root<B> from = createQuery.from(B.class);
        CriteriaQuery<B> select = createQuery.select(from);


        List<B> resultList = em.createQuery(createQuery).getResultList();
        em.getTransaction().commit();

        resultList.forEach( b2 -> {
            Assert.assertNull(b2.obj); // This is correct
        });

        em.clear();

        em.getTransaction().begin();
        CriteriaBuilder cb2 = em.getCriteriaBuilder();
        CriteriaQuery<A> createQuery2 = cb2.createQuery(A.class);
        Root<A> from2 = createQuery2.from(A.class);
        createQuery2.select(from2);

        List<A> resultList2 = em.createQuery(createQuery2).getResultList();

        resultList2.forEach( a2 -> {
            a2.b.forEach( b2 -> {
                Assert.assertNull(b2.obj); // this should be null but is not
            });
        });

    }

So I am doing:

  • Add all models: A, B and C with the propertie set.

Test 1:

Query for all Objects of type B. The where clause is correctly applied and C is not resolved.

Test 2:

Query all objects of Type A. B is resolved correctly, however the reference B->C is ignoring the where clause.

This surely can't be correct? Or am I missing something?

EDIT:

Some more testing revealed setting the fetching strategy to LAZY on the B<->C relationship fixes this (however I do not know why).

pandaadb
  • 6,306
  • 2
  • 22
  • 41
  • Did you try to move the `@Where(clause = "ran = 'hello'")` into the relationship between `B` and `C`: `public C obj;`? This is more intuitive for me and more flexible. And try to not use `EAGER` on mapping in any scenario, because you will have performance problems using this fetch strategy and this is really hard to remove later. – Dherik Jul 18 '16 at 22:56
  • @Dherik this doesn't work, because ran is a prop of C. B does not know if it is fetching C or not, you can not reference the property in C. (At least that did not work for me). This is why I put the annotation on the entity itself since the fetching did consider that annotation (however not in a multiple layer fetch as in A->B->C. Switching C to be fetched lazily (in the B ManyToOne relationsship) does make it consider the annotation. I am using eager because I ran into issues with the EM being closed. I have a very small model with at most 100 objects. Performance is not a big issue :) – pandaadb Jul 19 '16 at 09:35

0 Answers0