OneToMany relationship is not working
Asked Answered
A

5

21

My Tables:

Product: id, name

Offer: id, value, product_id

Entities:

@Entity
@Table(name="product")
public class Product implements Serializable {
    @OneToMany(mappedBy="product")
    private Set<Offer> offers;
    ...
}

@Entity
@Table(name="offer")
public class Offer implements Serializable {
    @ManyToOne
    @JoinColumn(name="PRODUCT_ID")
    private Product product;
    ...
}

When I try to get some data from table Product, I get a java.lang.NullPointerException, and this code: product.getOffers() returns:

{IndirectSet: not instantiated}

How to fix this?

Amary answered 28/11, 2011 at 20:22 Comment(3)
Which orm framework are you using ??Charlotte
I'm using JPA as an ORM Framework.Amary
Emanuel: I think @Charlotte was asking which JPA implementation you are using.Teeth
A
29

This not an error message. The print instruction results in toString() being invoked on the underlying IndirectSet.

TopLink will place an IndirectSet in the instance variable when the containing domain object is read from the datatabase. With the first message sent to the IndirectSet, the contents are fetched from the database and normal Set behavior is resumed.

IndirectCollection types are specifically implemented not to instantiate on toString():

For debugging purposes, #toString() will not trigger a database read.

However, any other call on the indirect collection, e.g., size() or isEmpty() will instantiate the object.

The database read is ultimately triggered when one of the "delegated" methods makes the first call to getDelegate(), which in turn calls buildDelegate(), which sends the message getValue() to the value holder. The value holder performs the database read. With the first message sent to the IndirectSet, the contents are fetched from the database and normal Set behavior is resumed.

See also IndirectList: not instantiated

Ana answered 23/8, 2012 at 22:1 Comment(3)
+1'd this response; very informative, as I was wondering why eclipselink indirectlist isEmpty() was showing up in Java Visual VM as I was reviewing, comparing, and reporting on performance after replacing JPA queries of indirectlist with just checking if list != null and !list.isEmpty(). This absolutely solved a performance issue I had; was accessing the database over 600 times, at first, but with the latest implementation, accessing the database only 5 times!Sadesadella
+1'd. Though the answer is for TopLink, it holds true for EclipseLink as well.Simplism
@Simplism EclipseLink is based on TopLink (fyi)Bough
C
14

If you get {IndirectSet: not instantiated} when accessing product.getOffers() than most probably you're executing this code outside of the transaction.

By default @OneToMany and @ManyToMany relationships are lazy loaded which means that, for better performance, you'll get data fetched only when you want to access it for the first time. This must happen within an active transaction.
If you don't access this data within this scope than you cannot access this data no more. You should either put your invocation code within the active transaction or change the collection to be eager instead of lazy:

@OneToMany(mappedBy="product", fetch=FetchType.EAGER)
Cannady answered 28/11, 2011 at 22:32 Comment(7)
I acualy tried it out myself with a JUnit test and I did wrap around final EntityTransaction txn = em.getTransaction(); txn.begin(); and txn.commit() but I still get the same behavour.Nock
So, you did invoke your logic within the EntityManager's transaction (and you did load and object and access it without leaving the tx) and you still get the same exception?Cannady
I don't get an "exception" I got the {Indirect set: not instantiated}Nock
You're right - it's not an exception; but still - is the rest of my sentence correct?Cannady
To be honest I never tried outside the transaction. But I don't doubt you anyway. Everything I do is wrapped in a transaction even simple reads.Nock
Thanks, this solved my problem. However, I had to remove some of the "reverse" relationships I had in my classes, because the eager fetching ignited a firestorm of queries. Other than using an eager fetching scheme, is there another way to make JPA load the referenced objects only when they're accessed?Hannibal
Don't think so... Either you load the data when needed while being in a transaction (rationalle behind LAZY) or you load it eagerly so you don't need a tx to access it (effective EAGER). You can eagerly load the objects using FetchType.EAGER, using JPQL JOIN FETCH or by iterating over elements of the collection while being in tx. Nevertheless, all of these options are effectively eagerly loading whole collection.Cannady
N
7

Here's what I did

// force load of the set.
entity.getSecrets().isEmpty();
System.out.println(entity.getSecrets());
Nock answered 3/4, 2012 at 4:6 Comment(2)
Why do you invoke the entity.getSecrets().isEmpty()? For what I know, it doesn't load your collection as you still need to access the object itself; the sysout you posted should just do the trick (despite the fact that it's not a great way to invoke object's toString method just to do the lazy loading..)Cannady
You would think so, but it does not appear to do so with EclipseLink. It may be a "feature" anyway the .isEmpty() forces the set to load I find.Nock
K
0

This solved my issue

@OneToMany(mappedBy = "columnName", cascade = { CascadeType.ALL}, fetch=FetchType.EAGER)

Knell answered 26/4, 2018 at 14:23 Comment(0)
T
0

I am using Eclipselink 2.5.2 version as my ORM vendor and I have faced this issue in one-to-many JPA mapping while lazy loading the containing entities. The workaround that is working for me is:-

final List<ContainingEntity> list = Collections.unmodifiableList(new ArrayList<> 
                                    (containerEntity.getContainingEntityList());

The Mapping from Container Entity side is:

@OneToMany(mappedBy = containerEntity, cascade = CascadeType.ALL, fetchType = FetchType.LAZY)
private List<ContainingEntity> containingEntityList;

Mapping from Containing Entity side is:

@ManyToOne
@JoinColumn(name = "container_entity_id")
private ContainerEntity containerEntity;
Taffy answered 3/1, 2020 at 8:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.