I'm trying to make an application that keeps an object model in sync with a database by observing all changes and then immediately persisting the objects in questions. Many of the object in the model have children in large lists or trees.
When I load an object from the database, I rely on a one-way cascading relationship to also retrieve all of its children and include them in the application.
However, it is possible to alter a field in the parent object which requires persistence and I can determine that none of the children are affected. So I would like to persist the parent, without hitting the database with all the cascaded child persists.
eg
#Entity
public class Parent {
#OneToMany(cascade=CascadeType.ALL)
public List children;
}
How can I override the cascade option when I persist a Parent object? Or should I just set it to REFRESH and make sure I never need a cascading persist?
Reading the objects from the database and persisting them rely upon two different annotations.
When you load an object, it will also get the other end of any eager (FetchType.EAGER) relationships, as defined by the fetch property on the relationship.
Depending on your JPA provider, you may have options to override this behaviour. EclipseLink, via the incredibly useful QueryHint.BATCH, certainly does.
When you persist, delete or refresh, the cascade type is what's relevant.
So, lose the cascade, keep the fetch and problem solved.
Personally I think cascade all is asking for trouble but opinions will vary.
A decent JPA provider will have a pretty sophisticated (configurable) caching scheme already. Perhaps you should be asking why you're reinventing that particular wheel?
Is it an issue of asynchronous updates purely for performance? Or is something else the reason?
Related
I searched around but I only get people asking the opposite of my question. So let's say we have:
#Entity
class Book {
...
#ManyToOne(fetch = FetchType.LAZY)
private Author author;
}
Is there a (preferably global) property/way in JPA/Hibernate to prevent from ever lazily loading the author (or any entity)?
Don't get me wrong, I don't want to use EAGER fetch. I want to prevent juniors from ever accidentally calling book.getAuthor().getName() and making another DB call. A lot of people are looking to fix their LazyInitializationException, but I basically want to force such an exception to be thrown even if there is an active session (which when using #Transactional is quite an easy mistake to make). However I also still want Author to be fetched if you properly use "JOIN FETCH Author" in your JPQL query.
My particular use case is with Spring and GraphQL.
#Transactional quite easily hides when a session is open and avoids the LazyInitializationException.
And with GraphQL you can specify which fields to get so I don't want unnecessarily joins when such fields aren't requested (here we use a Field Resolver with a DataLoader).
Would a sufficient workaround be to instead use a projection (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections) of the Book entity without the reference to the author? And by working with different projections guarantee that related entities are not unintentionally loaded?
Hibernate returns org.hibernate.collection.internal.PersistentSet as Set implementation on #OneToMany relation:
#OneToMany(mappedBy = "group", cascade = CascadeType.PERSIST)
private Set<Student> studentSet;
Hibernate tracks all changes on PersitanceSet (if some entity will is added to Set than it will be inserted into a database and etc.). Is it possible to have the same functionality for collections got by JPA EntityManager, org.hibernate.Session or by another way?
For example:
entityManager.createQuery(query, Student.class)
.setParameter("name", name)
.getResultList();
Doesn't return such kind of collection.
So I am searching the way to get elements by custom query and collect elements into a collection that Hibernates tracks all changes(inserting on adding new transient entities, updating on changing managed entities, deleting on removing from the collection)
What you're asking for is not possible in Hibernate.
Hibernate tracks all changes on PersitanceSet (if some entity will is added to Set than it will be inserted into a database and etc.)
That statement is not really accurate. Hibernate will not automatically insert an entity added to the set into a database. You need to opt in for that functionality specifically by declaring the appropriate cascading option (CascadeType.PERSIST in this case).
What Hibernate will do, however, is track associations between entities. If a collection represents the owning side of a to-many association, changes to the collection will establish/destroy associations between entities. In fact, Hibernate will track all other entity state, not just associations. That's the idea behind managed entities - to be able to work with a domain object just like with any other Java object, and let Hibernate take care about persistence behind the scenes.
A collection retrieved from a query does not represent part of a single entity state. Therefore, there would be little sense for Hibernate to track the structural state of the list. Suppose you made two queries for the same data within a single transaction. You then modify one of the result lists and leave the other intact. What do you think should happen in such a scenario?
Note that by 'not possible', I mean to say that Hibernate does not provide such a functionality out of the box. However, if you want to track changes to an arbitrary list, there are list implementations that allow that (see e.g. Glazed Lists or Apache Commons Events). You could combine them with Hibernate API to get the behavior you want.
I have two entities A and B. A has a many-to-one association towards B (A.listOfBs::List). I want to validate before B is persisted if a B was added to an already existing A.
Debugging into our JPA implementation (Hibernate), I learned that it basically cascades the association and while knowing its a parent<->child association only invokes the PrePersist-handler on B and never the BeforeUpdate on A.
Since I do not want to introduce a bi-directional mapping, I would like to know how I can validate A's children during B's persisting phase as A has special requirements (no two childs sharing the same type).
Is there any mechanism I can learn about parent<->child related events revolving around persisting or deleting children of a particular parent?
From debugging into Hibernate (another framework might be different), the persist event is handled solely on a per entity level. There is a mapping between child and parent in the persistence context that is set prior to persisting the entity (see handling/persisting an association). If one is able to access it, it would be possible for the child to notify its parent event even without a bi-directional connection. All this will continue to feel like a hack.
The best solution seems to be to listen to session level events and iterate through the list of managed entities an let those be validated regardless whether those have changed or not.
I would like to now what is the difference between CascadeType and FetchType in Hibernate?
They seem very similar but I guess they are not interchangeable, right?
When to use them? Can they be used both at the same time?
These are 2 different things:
The CascadeType in Hib. could be REFRESH, MERGE, ..., ALL you put it under the related entity and it determines the behavior of the related entity if the current entity is: refreshed, updated, deleted, e.t.c.. So whenever you entity is affected the CascadeType tells if the related entity should be affected as well.
The FetchType could be only of 2: EAGER and LAZY. This one you as well put under the related entity and it determines whether the related entity should be initialized right away when the current entity is initialized (EAGER) or only on demand (LAZY).
Cascading is used for propagating entity state transitions from a Parent entity to a Child.
Fetching is used for loading associated entities and you can have:
global fetch policies (defined through entity mapping)
query-time fetch policy (using the HQL/JPQL FETCH directive)
Both are different configurations, you can relate it with simple SQL.
Cascade tells you what happens when one entity gets updated ( on delete cascade in sql)
Fetch tells how the query is going to be executed ( join, lazy ...)
There's a big difference between the two of them.
CascadeType is a property used to define cascading in arelationship between a parent and a child.
FetchType is a property used to define fetching strategies which are used to optimize the Hibernate generated select statement, so that it
can be as efficient as possible.
You can find more about them in:
Hibernate – fetching strategies examples
Hibernate JPA Cascade Types
I've been using JPA 2.0 for a while but, sad to admit, I haven't had enough time to learn it properly. It seems like I lack the basics of how to work with Entity Manager.
Moving one step at a time, I'd like to first ask you about maintaining relationships between mapped entities. Of course I know how to create mappings between entities, different types of available associations (OneToOne, etc.) and how databases work in general. I'm purely focused on maintaining it via Entity Manager, so please do not send me to any kind of general knowledge tutorial :-).
The questions are:
Am I right that as a programmer I'm responsible for maintaining (creating/updating/removing) relationships between instances of entities?
Do I have to always update (set to null, remove from collection, etc.) instances by hand?
Plain SQL can set entities to NULL on deleting, but it seems like JPA can't do such a simple thing. It also seems like a burden to do it manually. Is there a way to achieve that with JPA?
If I have OneToMany relationship and set to NULL the entity on the Many side of the relationship. Then I persist the changes in a Set by saving the entity on the One side. Do I then have to update the entities in the Many side and set association to NULL in each instance? Seems pure silliness for one-directional bindings!
Thanks in advance!
The main thing you need to investigate is the different options you have when mapping on entity. For example in the next piece of code the cascade all option will instruct jpa to delete the child list when the parent is deleted.
#OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL }, mappedBy = "parent")
private Set<Child> events = new HashSet<Child>();
Yes. You maintain the object tree and modify it to look like what
you want.
Yes and no. If you want the entity to reference null, then yes.
For instance, if you are removing one Entity, then you should clean
up any references to it held by other entities that you are not
removing. A practical example: its good practice to let an Employee
know his/her Manager has been let go. If the Employee is going to
stay, it should either have its manager reference nulled out or set
to a different manager, before the current manager can be removed.
If the employee is going to be removed as well, then cascade remove
can cascade to all the Manager's subordinates, in which case you do
not need to clean up their references to the manager - as they are
going away too.
I don't quite understand what SQL is setting to null. Deleting
removes the row in the database, so there isn't anything to set to
null. Cleaning up a reference shouldn't be that difficult in the
object model, as JPA has a number of events to help such as
preremove preupdate etc. In the end though, the problem is with
your java objects. They are just java objects, so if you want
something done, your application will need to do it for the most
part. JPA handles building them and pushing them to the database,
not changing the state for you.
Yes and no. If you set up a bidirectional relationship, you must
maintain both sides as mentioned above. If you set the child's
parent reference to null, you should let the parent know it no
longer has a child, wouldn't you? Your parent will continue to
reference its child for as long as that Parent instance exists. So
even though the database is updated/controlled through the side that
owns a relationship, the object model will be out of synch with the
database until it is refreshed or somehow reloaded. JPA allows for
multiple levels of caching, so it all depends on your provider setup
how long that Parent instance will exist referencing a Child that no
longer exists in the database.