JPA / EclipseLink lazy collections not loading - java

I have a User entity with this mapping:
#OneToMany(mappedBy = "supervisor", fetch = LAZY, cascade = [CascadeType.REFRESH])
List<Group> supervisedGroups = new ArrayList<Group>()
and a Group entity with this mapping:
#ManyToOne(fetch = LAZY, cascade = [CascadeType.REFRESH])
#JoinColumn(name = "supervisor")
User supervisor
I fetch a user thanks to a repository
User user = userRepository.findById(id)
The findById method is wrapped by a transaction (method is intercepted by a transaction manager advice), and the JPA unit of work lasts as long as the request last (session per view).
When I get the user, I do a
user.getSupervisedGroups()
This returns me an empty list. The collection type is the Eclipselinks's IndirectList and even if i call the size() method it does nothing more.
But if I execute
entitymanager.refresh(user)
user.getSupervisedGroups()
Then I have 2 items in my list... Why ? Does it means EclipseLink does not support at all LAZY fetching on collections ?

My guess is you have corrupted that objects in the shared L2 cache.
When you inserted this object, did you add the correct objects to the collection? My guess is you have a bidirectional OneToMany/ManyToOne relationship and when inserting the One side and not adding to the Many side. You must maintain your object's relationships correctly.
See,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Object_corruption.2C_one_side_of_the_relationship_is_not_updated_after_updating_the_other_side

The answer by James might be the origin of the problem. Another solution is to disable EclipseLinks L2 cache:
Persistence Unit property:
<property name="eclipselink.cache.shared.default" value="false"/>
Or JPA 2.0 persistence unit element:
<shared-cache-mode>NONE</shared-cache-mode>
Source: https://wiki.eclipse.org/EclipseLink/FAQ/How_to_disable_the_shared_cache%3F

Change your fetch attributes fetch = LAZY to fetch=FetchType.LAZY from both entities likes this;
#OneToMany(mappedBy = "supervisor", fetch=FetchType.LAZY, cascade = CascadeType.REFRESH)

Related

Hibernate select with birectionnal mapping

On a bidirectional relationship beetwen two entities (a ControlTable made up of ControlSteps), i'm simply trying by different ways to request a ControlTable by knowing the collection ControlSteps of it. I know that it's not recommended to have this bidirectionnal mapping but i need to know each childs of a parent, and the parent for each child.
I configured it like this in ControlTable class:
#OneToMany(mappedBy = "controlTable",cascade = CascadeType.ALL, fetch=FetchType.EAGER)
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Fetch(FetchMode.JOIN)
private Set<ControlStep> controlSteps;
And like this for ControlStep class :
#ManyToOne(optional=false, fetch=FetchType.LAZY)
#JoinColumn(name="ctrl_table_id", referencedColumnName = "id")
private ControlTable controlTable;
When i use the default JPA query findAll(), it's not working to get the list of ControlTables (or only one) because it's requesting recursively the parent in the child's parent (infinite response).
In another way, itried to put all in LAZY loading, with an HQL query fetching the childs, but the result is the same.
Do you have any idea of how to get these collections without problems?
Thank you very much by advance
Found it. The problem was Spring Data Rest and JSON transformation, for more details :
Infinite Recursion with Jackson JSON and Hibernate JPA issue

cascade type save update in Hibernate

I am using hibernate with JPA annotations for relationship mapping.
I have three entities in my code User Group & User_Group
User & Group are in a ManyToMany relationship.
User_Group is a kinda bridge table but with some additional fields. So here is the modified mapping code.
User
#Entity
#Table(name = "USERS")
public class User {
#OneToMany(mappedBy = "user")
private Set<UserGroup> userGroups
}
Group
#Entity
#Table(name = "GROUPS")
public class Group {
#OneToMany(mappedBy = "group")
private Set<UserGroup> userGroups
}
UserGroup
#Entity
#Table(name = "USERS_GROUPS")
public class UserGroup {
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "GROUP_ID")
private Group group;
}
When I set the user & group object to the usergroup & save it.
User user = new User("tommy", "ymmot", "tommy#gmail.com");
Group group = new Group("Coders");
UserGroup userGroup = new UserGroup();
userGroup.setGroup(group);
userGroup.setUser(user);
userGroup.setActivated(true);
userGroup.setRegisteredDate(new Date());
session.save(userGroup);
Things work fine. With CascadeType.ALL the group object & user object are updated too. But when I delete the userGroup object. The child object are deleted too.
Deletion of child objects is a strict no no.
There is no CascadeType.SAVE-UPDATE in JPA, which just does save or update but no delete. How do I achieve this.
If I remove the CascadeType.ALL from the mapping the child objects don't get updated & I need them to be updated.
SAVE_UPDATE is for save(), update(), and saveOrUpdate(), which are 3 Hibernate-proprietary methods. JPA only has persist() and merge(). So, if you want to use cascading on Hibernate-proprietary methods, you'll need to use Hibernate-proprietary annotations. In this case, Cascade.
Or you could stop using the Hibernate Session, and use the standard JPA API instead.
CascadeType.ALL includes CascadeType.REMOVE too.
The solution is to use all CascadeType.* you need except CascadeType.REMOVE, like so:
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE}))
in your UserGroup definitions.
It's almost always a code smell when propagating from child to parent entity, it should be the other way round.
From Cascading best practices:
Cascading only makes sense only for Parent – Child associations (the
Parent entity state transition being cascaded to its Child entities).
Cascading from Child to Parent is not very useful and usually, it’s a
mapping code smell.
From Hibernate best practices:
Avoid cascade remove for huge relationships
Most developers (myself included) get a little nervous when they see a
CascadeType.REMOVE definition for a relationship. It tells Hibernate
to also delete the related entities when it deletes this one. There is
always the fear that the related entity also uses cascade remove for
some of its relationships and that Hibernate might delete more
database records than intended. During all the years I’ve worked with
Hibernate, this has never happened to me, and I don’t think it’s a
real issue. But cascade remove makes it incredibly hard to understand
what exactly happens if you delete an entity. And that’s something you
should always avoid. If you have a closer look at how Hibernate
deletes the related entities, you will find another reason to avoid
it. Hibernate performs 2 SQL statements for each related entity: 1
SELECT statement to fetch the entity from the database and 1 DELETE
statement to remove it. This might be OK, if there are only 1 or 2
related entities but creates performance issues if there are large
numbers of them.

EntityNotFoundException when removing from OneToMany collection

I have two entities connected with a bidirectional OneToMany/ManyToOne relationship.
#Entity
class One {
#OneToMany(mappedBy = "one", orphanRemoval = true, fetch = FetchType.EAGER)
private Set<Many> manies;
// ...
}
#Entity
class Many {
#ManyToOne
#JoinColumn(name = "one_id", nullable = false)
private One one;
// ...
}
When I want to remove a Many instance, I remove it from its One's manies Set and delete it from the database. If I take the same One instance and save it again (because I changed anything, it doesn't have to be related to the relationship), I get an exception:
javax.persistence.EntityNotFoundException: Unable to find test.Many with id 12345
The ID (in this example 12345) is the ID of the just removed entity. Note that removal of the entity 12345 succeeded: The transaction commits successfully and the row is removed from the database.
Adding and removing instances is done with Spring Data repositories. Removing looks more or less like this:
one.getManies().remove(manyToRemove);
oneDao.save(one);
manyDao.delete(manyToRemove);
I debugged a little and found out that the Set is a Hibernate PersistentSet which contains a field storedSnapshot. This in turn is another Set that still references the removed entity. I have no idea why this reference is not removed from the snapshot but I suspect this is the problem: I think Hibernate tries to remove the entity a second time because it's in the snapshot but not in the actual collection. I searched for quite a while but I didn't encounter others with a similar problem.
I use Hibernate 4.2.2 and Spring Data 1.6.0.
Am I doing something inherently wrong? How can I fix this?
I'm having the same problem.
A workaround is to replace the entire collection.
Set<Many> maniesBkp = one.getManies(); // Instance of PersistentSet
maniesBkp.remove(manyToRemove);
Set<Many> manies = new HashSet<Many>();
manies.addAll(maniesBkp);
one.setManies(manies);
...
manyDao.delete(manyToRemove);
...
oneDao.save(one);
As your #OneToMany relation has orphanRemoval = true, you don't have to explicitly remove the child element from the database - just remove it from the collection and save the parent element.
Try add CascadeType.REMOVE and orphanRemoval
#OneToMany(mappedBy = "one", orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.REMOVE, orphanRemoval = true)
Than perform delete following
one.getManies().remove(manyToRemove);
oneDao.save(one);
Edited:
I have created POC, look on the UserRepositoryIntegrationTest.departmentTestCase (code)

Delete cascade hangs in JPA when large number of objects

I have a JPA Entities like this:
#Entity
class MyEntity{
#JsonIgnore
#OneToMany(mappedBy = "application", cascade = ALL, fetch = LAZY)
private List<MyChildEnity> myChildEntities;
}
...
#Entity
class MyChildEnity {
#ManyToOne(optional = false, fetch = FetchType.LAZY, cascade = { REFRESH,
DETACH })
#JoinColumn(name = "APPLICATION_ID")
private MyEntity application;
}
I access this entity from a REST call. When the number of elements is very large, and I try to delete the MyEntity Object the REST call hangs and then timeout. For small number of elements in MyChildEnity table it works fine. When I debugged, I saw that JPA fetches one record at a time and deletes it. This is too slow and too much work done.
Is this an expected behavior? Shouldn't JPA be intelligent to convert this to a single DELETE call on the MyChildEnity table.
I'm using OpenJPA with Derby and DB2 database.
The reason why you get one delete statement for each element probably has something to do with the fact that JPA let you do something pre- and post removal. If you write a JPQL with a deletestatement you are able to bypass the callback mechanism and delete everything in a single request.
Documentation for entity listeners and callbacks. (This is JPA functionality).

JPA persisting objects without calling persist

I have an entity class Document and another one called Space. The relation:
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST,
CascadeType.MERGE, CascadeType.REFRESH}, optional = true)
#ForeignKey(name = "FK_TO_SPACE__DOCUMENT")
#IndexedEmbedded(prefix = DocumentDefaultFields.SPACE_TO_PREFIX)
private Space toSpace;
Well, i do query the db and take some docs into a LinkedList.
This list is binded to a dataTable from where i can do some update operations like:
<a:commandLink value="move" action="#{moveDocsOperation.moveDocumentToNewSpace(entity)}" reRender="confim,origTable,newTable"/>
and the method:
public void moveDocumentToNewSpace(final Document document) {
log.info("~~move document #0 from space #1 to space #2", document.getDocumentId(), origSpace.getPath(), newSpace.getPath());
document.setToSpace(newSpace);
origSpaceDocuments.remove(document);
newSpaceDocuments.add(document);
entityAuditer.auditBean(document, Crud.UPDATE);
}
I do not understand why, when setting the toSpace of the document entity, the update is also done in DB without actually doing PERSIST....
do you know WHY?
When you load an object via the hibernate session, it is managed by that session. When you make changes, at flush time the changes in the object are synchronized with the database.
So calling persist() is not needed to persist data modifications. (Related: http://techblog.bozho.net/?p=227)
One way you can get round this and make changes to the entity without persisting to the database is by removing from session:
org.hibernate.Session session = (Session) em.getDelegate();
session.evict(yrEnity);

Categories

Resources