JPA lazy fetch list invokes SELECT queries in setter - java

There is a method which returns entity from database by JPA. This entity has list for other entities, fetch type is LAZY. When I want to add object to this list I got exception:
Caused by: Exception [EclipseLink-7242] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.ValidationException
Exception Description: An attempt was made to traverse a relationship using indirection that had a null Session. This often occurs when an entity with an uninstantiated LAZY relationship is serialized and that lazy relationship is traversed after serialization. To avoid this issue, instantiate the LAZY relationship prior to serialization.
So in order to overcome this I can initialize this list by doing .size() on it. The thing is I don't really need these objects to be fetched from database so I would like to do something like this:
fetchedEntity.setMyLazyFetchList(new ArrayList<>()); which works fine. I can further access my list, but the problem is as following: set method invokes the same select queries as fetchedEntity.getMyLazyFetchList().size() does. These queries are useless as I set value to a new list, so why are they invoked?
Method fetching entity
public Competitor findAndInitializeEmptyGroups(Integer idCompetitor) {
Competitor entity = em.find(Competitor.class, idCompetitor);
System.out.println("Before set ");
entity.setGroupCompetitorList(new ArrayList<>());
System.out.print("After set lazy list size ");
System.out.print(entity.getGroupCompetitorList().size());
return entity;
}
Lazy fetch list field in entity (Competitor)
#OneToMany(mappedBy = "idCompetitor")
private List<GroupCompetitor> groupCompetitorList = new ArrayList<>();
Second end relationship field (GroupCompetitor)
#JoinColumn(name = "id_competitor", referencedColumnName = "id_competitor")
#ManyToOne(optional = false)
private Competitor idCompetitor;
What logs say:
Info: Before set
Fine: SELECT id_group_competitor, id_competitor, id_group_details FROM group_competitor WHERE (id_competitor = ?)
bind => [43]
Fine: SELECT id_group_details, end_date, start_date, version, id_competition, id_group_name FROM group_details WHERE (id_group_details = ?)
bind => [241]
...
many more SELECTs
Info: After set lazy list size
Info: 0
After replacing line
entity.setGroupCompetitorList(new ArrayList<>());
with
entity.getGroupCompetitorList().size();
And logs (they are the same except the list now consists of fetched entities):
Info: Before set
Fine: SELECT id_group_competitor, id_competitor, id_group_details FROM group_competitor WHERE (id_competitor = ?)
bind => [43]
Fine: SELECT id_group_details, end_date, start_date, version, id_competition, id_group_name FROM group_details WHERE (id_group_details = ?)
bind => [241]
...
many more SELECTs
Info: After set lazy list size
Info: 44
So my question is: why SELECT queries are invoked when I do entity.setGroupCompetitorList(new ArrayList<>());? I don't want them for the performance reasons. Is there any way to eliminate this issue or what exactly causes this behavior?
Using:
EclipseLink JPA 2.1
GlassFish 4.1
Java 8

You can't not fetch a list that is a member of an entity if you want to add an element and have the JPA provider persist it. The JPA provider has to track the owner, the ownee, and handle any cascading (which I don't see you have defined but I doubt there's a different code path for each combination of cascading options). The simplest way would be to have the list in memory and then decide what operation to perform on the DB at commit/flush time.
I believe the cause of your original exception about traversing a LAZY is due to accessing outside on a managed context. Once you return from an EJB method, the entity you're returning becomes detached. You have to reattach it to another EntityManager or make sure all the lazy relationships you're about to use have been loaded before you leave the method. Calling fetchedEntity.getMyLazyFetchList().size() would be an example of that and works fine in a single entity case. If you want to force the load of a LAZY in a list of entities I suggest you read up on LEFT JOIN FETCH clauses. I'm assuming here that your findAndInitializeEmptyGroups() method is in an EJB, judging by what looks to me like an injected EntitManager em in that method, and that the methods will get the default #TransactionAttribute(REQUIRED) treatment since I don't see any annotations to the contrary.
Now, let's go back to your original problem:
I want to add object to this list
The problem you're trying to solve is to add an element to a list without fetching the entire list. You're using a mappedBy attribute, meaning you've created a bidirectional relationship. If getGroupCompetitorList() returns an unordered list (a 'bag' in Hibernate speak) then you don't have to load the list at all. Try something like this:
Change GroupCompetitor's Integer idCompetitor to a #ManyToOne(fetch=FetchType.LAZY) Competitor competitor. Adjust the getters and setters accordingly.
Change Competitor's groupCompetitorList mappedBy to competitor. Add get/set methods.
Then you can add to the list from the child side with a method like this in the EJB:
public void addNewGroupCompetitorToCompetitor(Competitor comp, GroupCompetitor gComp) {
gComp.setCompetitor(comp);
em.persist(gComp);
em.flush();
}
The next time you fetch the Competitor again it and traverse entity.getGroupCompetitorList() (while managed by an EntityManager) it should have the new GroupCompetitor you've added. This kind of thing gets more complicated depending if comp is a new entity that has not been persisted, but that's the basic idea. It might need some adjustment to work correctly with EclipseLink, but I do the same kind of operation with Hibernate as the JPA provider and it works.

Related

Hibernate native SQL query - how to get distinct root entities with eagerly initialized one-to-many association

I have two entities Dept and Emp (real case changed and minimized). There is 1:n association between them, i.e. properties Dept.empList and Emp.dept exist with respective annotations. I want to get List<Dept> whose elements are distinct and have collection empList eagerly initialized, using native SQL query.
session.createSQLQuery("select {d.*}, {e.*} from dept d join emp e on d.id = e.dept_id")
.addEntity("d", Dept.class)
.addJoin("e", "d.empList")
//.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.list();
This query returns List<Object[2]> with instances of Dept, Emp (in that order) in the array and field Dept.empList properly initialized. That's ok.
To get distinct Depts, I thought setting transformer DISTINCT_ROOT_ENTITY (uncommenting the line) would be enough. Unfortunately, the DistinctRootEntityResultTransformer is based on RootEntityResultTransformer which treats the last element in tuple as root entity (it's hardwired). Since the order of entities is determined by sequence of addEntity, addJoin calls, the transformer mistakenly treats Emp as root entity and returns list with all Emps of all Depts.
Is there any clean way how to make Hibernate recognized the Dept as root entity, even though it is not last in entity list?
Note 1: I tried to switch order to .addJoin("e", "d.empList").addEntity("d", Dept.class). Does not work since d.empList requires d defined. Fails on HibernateSystemException : Could not determine fetch owner somewhere in Hibernate internals (org.hibernate.loader.Loader).
Note 2: I tried to define order as .addEntity("e", Emp.class).addJoin("d", "e.dept"). This seemingly works but the association is actually filled only from the "many" side. Hence the collection Dept.empList is some uninitialized proxy until requested, which invokes explicit SQL query and thus does not utilize the join in my query.
Note 3: The custom transformer looking for hard-wired index works:
.setResultTransformer(new BasicTransformerAdapter() {
public Object transformTuple(Object[] tuple, String[] aliases) {
return tuple[0];
}
public List transformList(List list) {
return DistinctResultTransformer.INSTANCE.transformList( list );
}
})
though I'm hesitant to accept such easy task could have such complicated solution.
Hibernate version: 3.6.10 (I know - legacy project :-) though I looked into source code of latest version and seems the key points don't differ).
Finally found https://stackoverflow.com/a/17210746/653539 - make duplicate call of .addEntity to force root entity on the end of list:
.addEntity("d", Dept.class)
.addJoin("e", "d.empList")
.addEntity("d", Dept.class)
It's still workaround but cleaner than mine and - based on 36 upvotes - it seems as idiomatic solution.

graphql-java and hibernate - lazy loads relations that are not even specified in query

I hope someone have experienced something similar and can help me:
I am using graphql-java (and spring, graphql-java-tools etc.) and hibernate and I am experiencing a weird issue:
Whenever i execute a query (or mutation) and load an entity via Hibernate, it automatically lazy loads the relations. I can see this when looking in Hibernates query log.
This happens even though i dont load the field in the query, and even also when i delete the field from the schema altogether.
Example, given the following schema:
query {
getAllItems: [Item!]!
}
Item {
id: String!
name: String!
owner: Person!
}
Person {
id: String!
name: String!
items: [Item!]!
}
And a Hibernate entity (pseudo code):
#Entity
class Item {
#Id
private String id
#Column
private String name
#ManyToOne(fetch = FetchType.LAZY)
private: Person
...
}
The following query:
getAllItems {
id
name
}
And a hibernate query that loads just the items, would end up with first fetching all the Items in one query, and then fetching all the owners in a seperate query each (unless a owner is the same in multiple items, then its returned from the hibernate cache).
So my thought was that graphql-java recursively scans the objects that is returned to it, which causes the hibernate proxies to fetch.
Can i be right about this, or do you think my issue is completely unrelated to graphql-java?
UPDATE:
I found out that this has nothing to do with graphql, and is caused by hibernate. My relations are setup as LAZY, but Hibernate ignores this, and makes a query for each Person. So first a query that gets all Item's and next a query for each Person (n+1). And i do not access the proxies myself.
I create the query like this (this is kotlin):
entityManager
.createQuery("SELECT i FROM Item i", Item::class.java)
.setMaxResults(1000)
.resultList
To make things clear, this has nothing to do with GraphQL.
Hibernate eagerly loads one-to-one relationships by default.
To change this behaviour annotate the person field with
#OneToOne(fetch = FetchType.LAZY)

How to avoid loading lazy bidirectional relationships with MOXy?

My question is a follow up to this comment.
I'm mixing JPA and JAXB (MOXy) annotations on the same class, which works fine most of the time. As described in the linked thread, #XmlInverseReference prevents cycle exceptions when bidirectional relationships are marshalled. But in order to detect the cycle, MOXy has to inspect the back reference of the linked entity, which leads to extra SQL SELECTs if a lazy relation needs to be populated.
To illustrate the problem in detail, consider this made-up example:
#Entity
#Access( AccessType.FIELD )
#XmlRootElement
#XmlAccessorType( XmlAccessType.FIELD )
public class Phone {
#ManyToOne
#JoinColumn( name = "employeeID" )
#XmlElement( name = "employee" )
#XmlInverseReference( mappedBy = "phones" )
private Employee employee;
private String number;
[...]
}
#Entity
#Access( AccessType.FIELD )
#XmlRootElement
#XmlAccessorType( XmlAccessType.FIELD )
public class Employee {
#OneToMany( mappedBy = "employee" )
#XmlElementWrapper( name = "phones" )
#XmlElement( name = "phone" )
#XmlInverseReference( mappedBy = "employee" )
private List<Phone> phones;
private String name;
[...]
}
Now I'd run queries on Phones with a JAX-RS method like this (using an underlying EJB):
#Inject
private PhoneService phoneService;
#GET
#Path( "/phones" )
public List<Phone> getPhonesByNumber( #QueryParam( "number" ) String number ) {
List<Phone> result = phoneService.getPhonesByNumber( number );
return result;
}
What happens is this: The JPQL query within the PhoneService EJB triggers an SQL SELECT on the Phone table (filtered by the number), and if I use a JOIN FETCH query, I can get the associated Employee with the same single SELECT statement.
When the JAX-RS method returns, the JAXB marshalling kicks in, which leads to an additional SQL SELECT: this one selects all Phones whose employeeID points to the Employee who is associated with the originally requested Phones. So the lazy relationship from Employee to Phone is resolved now, presumably because MOXy must be able to determine if the original Phone is contained in the collection.
I've tried using JPA property access and JAXB field access for the phones field, as suggested in the other thread, to no avail. I've also tried nulling out the phones field in the linked Employee instance after retrieving the result from the EJB, i.e. when my entities are detached already, but this led to an immediate SQL SELECT again (it seems like EclipseLink will do this whenever any manipulation is done to an IndirectList?). The only workaround solution I could find is to use MOXy #XmlNamedObjectGraphs with a subgraph that excludes the phones field. But that's not practical, especially if the involved entities have many attributes.
As I may need to query in the other direction too, e.g. employees by name with their associated phones, I can't just mark phones as #XmlTransient.
Does anyone have an elegant solution to suppress those extra SQL statements?
From my experience the easiest way to accomplish what you are trying is to detach all the entity classes before you pass them to a presentation layer like a JAX-RS rest api. You can even use the #OneToMany(mappedBy = "employee", cascade = CascadeType.DETACH) and EntityManager.detach() to detach your phone class and subsequently detach your employee class or vice versa. This will ensure that during the marshaling of your entity, Jax-RS doesn't trigger any SELECT statements that you wouldn't normally want.
I always detach model entities before I pass them to the presentation layer so that they can interact with the model classes how they please without affecting performance or the database.
I collected some information about EclipseLink from these three threads. Important bits:
Detached Objects get the connection need to traverse the LAZY relationship from the EntityManagerFactory and will able able to use it as long as the EntityManagerFactory is open. The connection used in not the transactional one and when you want to use the entity in a transaction it will have to be properly merged.
 
This is a special feature of TopLink's implementation where the detached instances created from non-tx reads still have access in their proxies to retrieve additional dettached instances. If the object was detached through serialization this would not be possible.
 
If you would like TopLink Essentials to not process lazy relationships after the EM has closed I would recommend filing an enhancement request in GlassFish.
I couldn't find such an enhancement request though, let alone an implemented possibility to disable this feature (on a case-by-case basis).
There are five possible workarounds I could think of, each with its own drawbacks:
Just don't mix JAXB and JPA annotations on the same class: use a different set of additionatlly instantiated JAXB classes instead and perform explicit mapping between the two views. This could be a little expensive if lots of entities are returned from a query.
Like I mentioned in my question, use MOXy's (named) object graph feature to exclude (relationship) fields from being traversed.
Use a JAXB Marshaller.Listener to exclude all uninstantiated IndirectContainers.
Since serialization is supposed to break this EclipseLink feature for detached entities, serialize them before marshalling them. Seems awkward and even more expensive though.
This comes closest to emulating turning off the feature, but also looks hackish: access the wrapping IndirectContainer and its contained ValueHolderInterface and set them to null. Sample code:
(...)
import org.eclipse.persistence.indirection.IndirectContainer;
// entities must already be detached here, otherwise SQL UPDATEs will be triggered!
Employee e = phone.getEmployee();
IndirectContainer container = (IndirectContainer) e.getPhones();
container.setValueHolder( null );
e.setPhones( null );

JPA EntityManager with Hibernate - find and remove does not delete data from database

Having trouble getting the following code to work...
I've got a JpaTransactionManager txManager autowired into this test. I know record with ID 39 does exist. It still exists at the end of the transactions, too...
TransactionStatus status = txManager.getTransaction(def);
A a = mock(A.class);
when(a.getId()).thenReturn(Long.valueOf(39));
sut.delete(a);
txManager.commit(status);
status = txManager.getTransaction(def);
a = sut.get(a.getId());
txManager.commit(status);
assertNull(a);
Code in class A:
public void delete(A a) {
a = getEntityManager().find(A.class, a.getId());
getEntityManager().remove(a);
}
Is there any reason the above assertNull check always fails? I cannot delete the object from my system no matter what I do - no error returned, and no issue with the delete reported. (As an aside, running a query directly in HQL does result in an update of the database...I just can't get it to work using the delete method supplied using JPA...)
Any assistance appreciated
You should take a look into these Hibernate classes/methods:
org/hibernate/engine/spi/ActionQueue.java executeActions(), unScheduleDeletion()
org/hibernate/event/internal/DefaultPersistEventListener.java onPersist()
I had the same problem - not being able to remove an entity. In my case, entityManager had two entities in its 'context': a parent with a list of children entities (cascade = CascadeType.ALL) and a child (from the list) to remove. So when I was trying to remove a child, parent still had a link to it, which was causing Hibernate to 'unScheduleDeletion' upon flushing.
So here is the solution:
Add orphanRemoval = true to the collection of children
Create method deleteChild(Child child) {child.setParent(null); children.remove(child);}
Use this method to delete children
Looks like another solution is to remove cascading, so that merging of parent entity wouldn't cause saving all its children. Not quite sure here (haven't checked).
Also, as far as I remember, JPA spec describes this situation.

Efficiently determining the IDs of entities referenced via OneToMany relationship

Let's say I have a Hibernate entity that declares a OneToMany relationship to a different entity:
#Entity
public class SomeEntity {
#OneToMany(fetch = FetchType.LAZY)
private List<OtherEntity> otherEntities = new LinkedList<OtherEntity>();
[...]
}
When mapping SomeEntity to the corresponding DTO, all I need are the IDs that identify OtherEntity as primary key (i.e., I am not actually interested in OtherEntity instances).
Does Hibernate support this pattern, i.e., only retrieving the IDs of entities referenced via a OneToMany relationship?
I cannot influence how SomeEntity is retrieved (i.e., I have an existing SomeEntity instance retrieved within te scope of the current Hibernate session), but let's assume that lazy loading has not yet taken place, so just retrieving the child objects' IDs (rather than the complete objects) would actually yield a performance benefit.
Well, if you only need the entities' ids and you want to be economical about it, when you get those entities from the database you should state in your query that you only want to get the ids of each entry, using projections, something like :
SELECT Entity.id as entity FROM Entity WHERE ...
This will return an array of objects of the same type as Entity's id field type.
You can try obtaining the primary key without accessing the entity itself (without otherEntities.get(0).getId()). To do this you can use the PersistenceUnitUtil class:
PersistenceUnitUtil#getIdentifier(yourEntity)
The PersistenceUnitUtil can be obtained from the EntityManagerFactory. So it could be something like:
EntityManager em = ...
PersistenceUnitUtil = em.getEntityManagerFactory().getPersistenceUnitUtil();
Unfortunately, I'm not aware if this will prevent the entity loading from occuring. However, just accessing the otherEntities collection or even obtaining references to each entity will not make the instance to be loaded; you need to invoke a method on the fetched entity in order to be sure it will be loaded.
You also might consider creating a #NamedQuery and return only the OtherEntity ID's.
HTH!
From hibernate reference, section 2.2.2.1.
http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/entity.html#entity-mapping-property
Declare your columns as lazy initialized:
#Basic(fetch = FetchType.LAZY)
private String getYourProperty() {
}
You also need to disable proxies for your entity class and byte instrument it. There is an example here:
Making a OneToOne-relation lazy
You can use the below HQL as told in the documentation to establish this.
session.createQuery(select new OtherEntity(oe.id) OtherEntity oe
where oe.parentSomeEntity.someId = :someId).list();//also set someId.
Add a constructor in OtherEntity to set the id also there should be a mapping to SomeEntity in OtherEntity.
This HQL will give you a List<OtherEntity> with only id set in the bean.

Categories

Resources