2

In a standard JEE application with JPA, I have a master entity A that contains One to Many collection of B. The A Entity has the following form:

@Table(name = "TABLE_A")
@Entity
public class A implements Serializable {

    @Id
    @SequenceGenerator(name = "A_ID_GENERATOR", sequenceName = "SEQ_A", allocationSize = 1, initialValue = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "A_ID_GENERATOR")
    @Column(unique = true, nullable = false, precision = 16)
    private Long id;

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY  , mappedBy = "a")
     private List<B> bCollection;
     public List<B> getB() {
        return this.bCollection;
     }

     public void setB(List<B> bCollection) {
        this.bCollection = bCollection;
     }

     public Long getId() {
         return id;
     }

     public void setId(Long id) {
         this.id = id;
     }

}

The Entity B has the following form :

@Table(name = "TABLE_B")
@Entity
public class B implements Serializable {

    @Id
    @SequenceGenerator(name = "B_ID_GENERATOR", sequenceName = "SEQ_B", allocationSize = 1, initialValue = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "B_ID_GENERATOR")
    @Column(unique = true, nullable = false, precision = 16)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "A_FK")
    private A a;

    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public A getA() {
        return this.a;
    }

    public void setA(A a) {
        this.a = a;
    }

}

In an EJB method with @TransactionAttribute(TransactionAttributeType.REQUIRED) I retrieve an A from DB and call getB() to fetch the data of B under current A. Then I manually detach the current A with :

em.detach(a);

Before the return of the EJB method, if I test the B instances under current A with em.contains(b) they are still managed even if I use CascadeType.ALL.

The transactional method in EJB seems like the following :

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void doSomething(String aBusinessKey) {

    A a = fetchAByItsBusinessKey(aBusinessKey);
    List<B> bs = a.getB();

    em.detach(a);

    //Test if Bs are managed
    boolean isManaged = em.contains(bs.get(0));

}

Could anyone explain why CascadeType.DETACH is blocked by FetchType.LAZY? When I change Fetch Type to EAGER the detaching is propagated to detail collection of B.

As engine I use Eclipse Link.

--EDIT The problem is that CascadeType.DETACH is not propagated in detail collections. All entities are managed and fetched before detaching.

Ilias Stavrakis
  • 753
  • 10
  • 19
  • ok, i am probably wrong: you mean that this part _and call getB()_ means _All Entities are fetched and managed before detaching_? – pirho Nov 09 '17 at 12:43
  • Yes, In the same transaction I fetch a master entity A, I call getB() to fetch it's detail entities of type B and then I call em.detach(a) and I expect the entity manager to detach also B entities because of CascadeType.ALL – Ilias Stavrakis Nov 09 '17 at 13:04
  • Can you put the `B` you are using also? Is there and if, then what kind of annotations for `A`? I would like to test this. – pirho Nov 09 '17 at 13:09
  • Check your first code snippet: it has annotation `@OneToOne`. Then it has `private B b;` but also has `List getB()` which returns `b`? And you state _A that contains One to many collection of B_? So there is something you need to edit in your code to get it more understandablei think. Like putting those entities just like you test them. – pirho Nov 09 '17 at 14:16
  • @pirho I editted the code to provide both A and B entities and correct the typos. – Ilias Stavrakis Nov 10 '17 at 07:27

1 Answers1

1

Detach only cascades over fetched properties and relationships, and you are fetching the list of Bs after detaching A. getB only returns the provider's collection implementation - a proxy over the actual collection, and does not fetch the results. Only accessing this collection, such as calling size on it, will trigger the fetch.

On other providers, this would cause an exception, but EclipseLink allows fetching lazy relationships as long as the context is still available. If the entity wasn't serialized and the EMF is still open, to keep object identity, EclipseLink will use the EntityManager the entity was read from to fetch your collection, causing the results to be managed in that EntityManager.

Chris
  • 20,138
  • 2
  • 29
  • 43
  • Exactly. I tested OPs code with @OneToOne with getting just one `B` and everything worked because it fetches the entity. But for lists it is just like that. I do not if this is appropriate (i'll delete if not) but maybe you have something to say to my loosely related question [here](https://stackoverflow.com/questions/46853396/why-entitys-uninitialized-collection-is-initialized-automatically-only-for-entit) – pirho Nov 09 '17 at 15:21
  • @Chris I edit the code to provide both entities and correct the typos. No, I perform the fetching of Bs inside the transaction and before detaching so Bs are fetched normally as I expect. The problem is that if I check the Bs after detaching current A they are still managed even if I use `CascadeType.ALL` – Ilias Stavrakis Nov 10 '17 at 07:31
  • @IliasStavrakis Do you fetch only the list of `B`s -as it seems in your original post- or do you also access `B`s inside the list? – pirho Nov 10 '17 at 07:34
  • @pirho I only fetch them and with debugger I test that the collection of Bs inside A is populated with the correct entities. – Ilias Stavrakis Nov 10 '17 at 07:38
  • 1
    @IliasStavrakis Put that into your question also. Or preferably a piece of code that loops all `B`s and after that the cascading detach still fails. That is because your debugging session is hard to repeat for others. – pirho Nov 10 '17 at 07:41
  • @pirho I edit the code to provide a sample code of the transactional EJB method – Ilias Stavrakis Nov 10 '17 at 07:52
  • 2
    And if you after `List bs = a.getB();` do -**before flush**- `bs.get(0)` is it the same? This is the point in answer by Chris. So not only in your debugger but in code also show you have populated list. – pirho Nov 10 '17 at 07:55
  • @IliasStavrakis did you notice my last comment (forgot to address it to you). – pirho Nov 10 '17 at 11:43
  • @pirtho Amazing, if I put bs.get(0) it then detach the entity. But why is this happens? – Ilias Stavrakis Nov 10 '17 at 13:43
  • 1
    getBs does NOT fetch the Bs. Your debugger should show that what you have is an instance of an EclipseLink proxy class, which wraps the collection, allowing it to be lazily fetched only when an instance is really needed. It is not fetched until you try to access a B from the collection, at which point the proxy will populate the list of references from the database (or shared cache). – Chris Nov 10 '17 at 15:51
  • @Chris Could you take a look at this question https://stackoverflow.com/questions/56543710/synchronization-error-when-insertable-false-in-eclipselink – Pavel_K Jun 12 '19 at 10:53