I'm new to JPA/Hibernate and I'm wondering, what is usually the best way of updating a complex entity?
For example, consider the entity below:
#Entity
public class Employee {
#Id
private long id;
#Column
private String name;
#ManyToMany
private List<Positions> positions;
// Getters and setters...
}
What is the best way to update the references to positions? Currently a service is passing me a list of positions that the employee should have. Creating them is easy:
for (long positionId : positionIdList) {
Position position = entityManager.find(positionId);
employee.getPositions.add(position);
}
entityManager.persist(employee);
However, when it comes to updating the employee, I'm not sure what the best way of updating the employees positions would be. I figure there is two options:
I parse through the list of position id's and determine if the position needs to be added/deleted (this doesn't seem like a fun process, and may end up with many delete queries)
I delete all positions and then re-add the specified positions. Is there a way in JPA/Hibernate to delete all children (in this case positions) with one sql command?
Am I thinking about this the wrong way? What do you guys recommend?
How about
employee.getPositions.clear(); // delete all existing one
// add all of them again
for (long positionId : positionIdList) {
Position position = entityManager.find(positionId);
employee.getPositions.add(position);
}
although it may not be the most efficient approach. For a detail discussion see here.
Cascading won't help here much because in ManyToMany relation the positions may not get orphaned as they may be attached to other employee (s), or even they shouldn't be deleted at all, because they can exists on their own.
JPA/Hibernate has support for this. It's called cascading. By using #ManyToMany(cascade=CascadeType.ALL) (or limit the cascade type to PERSIST and MERGE), you specify that the collection should be persisted (merged/deleted/etc) when the owning object is.
When deletion is concerned, there is a special case, when objects become "orphans" in the database. This is handled by setting orphanRemoval=true
Related
I have an entity that has a relation to a very large table ( > 100k Rows ). Now I am asking myself if I should express that relation really in my channel entity because would never call getProducts directly to load all products into memory. I would rather go through a product repositiory and query for a very specific subset for that channel.
I have only put it there so it's more readable from a client perspective.
#Entity
#Table(name = "Channel")
public class Channel {
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "product_id")
private List<Product> products;
public void setProducts(List<Product> products) {
this.products= products;
}
public List<Product> getProducts() {
return this.products;
}
}
Don't put that in a entity definition at all. You see what is often missed is that name #OneToMany is very misleading. As there is no possibility to limit the number of items you are loading. It can practically be used only when Many means Few :). The Hibernate Guru explains it better then me
Of course the join is executed lazily meaning that as long as it is not used it will do no harm. But why tempt faith. Writing query in a repo gives you ability for pagination and you can narrow the results to the one you will truly need.
The other side of the equation is more useful. So if you have on the child entity annotation #ManyToOne leave it there. It is good if you update parent from context of a child.
In short : no.
I think what you are asking may be opinion-based, but here is what I tend to do : remove all "non-navigable" relationships.
If you are using some generator to get JPA entities from a database model, the generator will create only bi-directional relationships. You need to remove all the relationships you will not used through the ORM mechanisms (for readability, maintainability and performance (in case someone would have the idea to remove the "lazy" indication or to call a getter on the collection in a transaction)).
For example, a User will be linked to a Language (many-to-one), but you don't want the (one-to-many) relationship between Language and User, even if at some point you will need to know in a report screen how many user speak which language.
one quick question for java hibernate/jpa users.
I have two tables(entities) A and B with relations as A has many B (one to many). Entity A has Set of values B in java.
Due to read performance issue i want to implement master-details denormalization, so i want to store raw Set object (maybe serialized) directly in entity A (because many to one relation cost me to much cpu time because of read by jpa (update is not an issue)).
The problem is, can i achieve something like that getBs always returns me denormalized object (so its fast) and addB adds new B to Set and updates denormalized object with new raw data that is prepared for faster read?
its oracle db.
entity example:
class A {
Long id,
String name;
Set<B> arrayOfBs;
byte[] denormalizedArrayOfB;
getArrayOfBs() {
return (Set<B>) denormalizedArrayOfB;
}
addArrayOfBs(B b) {
//persist b
// update and persist denormalizedArray with new b
}
//getters and setters...
}
class B {
Long id;
A reference;
String x;
String y;
//getters and setters...
}
That's complicated. There are better approaches to your problem:
You can simply replace the one-to-many association with a DAO query. So whenever you fetch the parent entities you won't be able to get the children collection (maybe they are way too many). But when you want to get a parent's children, you simply run a DAO query, which is also easier to filter.
You leave the children collection, but you use an in-memory cache to save the fully initialized object graph. This might sounds like a natural choice, but most likely you're going to trade consistency for performance.
i have a database table "viewmodule" with a FK to itself (parent_id) to allow recursive structures.
CREATE TABLE viewmodule (
id,
type,
parent_id,
hide);
My Java application uses JPA/Hibernate to map the entities on that table. We have fixed entity hirachy which is solved by a #Discriminator annotation that uses the "type" column of the table.
public class ViewModule implements Serializable {
private long id;
private String type;
private ViewModule parent;
private Boolean hide;
#OneToMany( targetEntity = ViewModule.class, cascade = javax.persistence.CascadeType.ALL, mappedBy = "parent" )
#Cascade( { org.hibernate.annotations.CascadeType.ALL,
org.hibernate.annotations.CascadeType.DELETE_ORPHAN } )
private Set<ViewModules> children;
(...)
}
My task is now to load all elements from this table (in deep) but left out the ones which have the field "hide" set to true.
Its a apparently simple filter mechanism. My first approach was to use the Hibernate Filter annotation, which works well on the first layer (all viewmodules with parent_id = null). But the filter does not work on the "children" relation. (In my real life model, i have an inheritance structure for the different types of the ViewModules)
Therefore i've written a small function that recursively walks through the viewModule object tree and removes the viewModules from the children relation that have hide=true;
But, as all objects are still under observation of the jpa/hibernate entityManager, every remove from a collection is directly executed as delete in the database. So my filter function removes the entity from the database, and that is a bad thing.
I tried to use the "evict" method from the hibernate session to detach the entities before filtering but that leads to a LazyInitialisationException.
So, to prevent cloning all of my object my question is how to solve this problem? Is there a way to detach the object in way that all collections are initialized? Or is there a special Kung-Fu Chuck-Norris JPA Annotation that can filter the collections?
Thanks in advance
use native query
em.createNativeQuery("select * from viewmodule where hide = false", ViewModule.class).getResultList();
This works: Filter list contained in entity returned by jpa/hibernate query
Make a new collection and add only the elements that have hide=false. You won't be able to distribute that collection together with the object, so you'd have to return it from a separate method call. For example: dao.getVisibleItems(module)
Another thing - you can remove the Cascade.DELETE (i.e. list all cascades except delete) and the orphan removal, if you don't need them.
I have the following two annotated classes that I use to build a graph:
#Entity
#Table(name = "Edge")
public class Edge
{
/* some code omitted for brevity */
#ManyToOne
#JoinColumn(name = "ixNodeFrom", nullable = false)
private Node _nodFrom;
#ManyToOne
#JoinColumn(name = "ixNodeTo", nullable = false)
private Node _nodTo;
/* some code omitted for brevity */
}
#Entity
#Table(name = "Node")
public class Node
{
/* some code omitted for brevity */
#OneToMany(mappedBy = "_nodTo")
private Set<Edge> _rgInbound;
#OneToMany(mappedBy = "_nodFrom")
private Set<Edge> _rgOutbound;
/* some code omitted for brevity */
}
Now, when I build the graph, I issue two queries to fetch all rows from either table and set up the child / parent references, for which I need the ids stored in the Edge table.
Because I have defined the relation between the two tables in JPA, accessing the edge object to get the two nodes' ids triggers two SQL statements per edge, when the JPA provider lazily * loads the associated nodes. Since I already have the node objects, and the ids have already been loaded from the edge table, I want to skip those queries, as they take an awfully long time for larger graphs.
I tried adding these lines to the Edge class, but then my JPA provider wants me to make one mapping read-only, and I can't seem to find a way how to do that:
#Column(name = "ixNodeTo")
private long _ixNodeTo;
#Column(name = "ixNodeFrom")
private long _ixNodeFrom;
I'm using Eclipselink and MySQL, if it matters.
**The default behaviour for #ManyToOne actually is eager loading, see Pascal's answer*
I got three good answers that were equally helpful, and by now none percolated to the top by public vote, so I'm merging them together here for a single comprehensive answer:
a) Change the query
You can load the whole graph at once by changing the query, thereby giving the JPA provider a chance to realize that it already has everything in memory and doesn't need to go back to the DB:
List<Node> nodes = em.createQuery(
"SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound")
.getResultList();
(via axtavt)
b) Use read-only fields for the FKs
Loading the FKs into their own fields, as described in the question, will also work if, as the JPA provider is demanding, the fields are declared to be readonly, which is done like this:
#Column(name = "ixNodeTo", insertable = false, updatable = false)
(via bravocharlie)
c) Use property access
If you are using property access instead of field access, the JPA provider also gets a chance to realize it already has the FK and doesn't need to fetch the referenced object. In short, property access means that you put the JPA annotations on the getter, thereby "promising" the JPA provider that your getter won't go and access the rest of the object. More details in this question. This will work for Hibernate, and for Eclipselink, it will work (assumed in the original answer, experimentally confirmed by me) with weaving enabled. (via Pascal Thivent)
Additionally, as Pascal points out in his answer, #ManyToOne, contrary to my original post, is not lazy-loading, but eager-loading by default, and changing that will require weaving as well.
Have you tried
#Column(name = "ixNodeTo", insertable = false, updatable = false)
How can I retrieve the foreign key from a JPA ManyToOne mapping without hitting the target table?
In theory, a JPA provider should be able to not trigger a query when calling
someEdge.getNodeFrom().getId()
as it already has the id (as FK).
I'm 100% sure Hibernate can (assuming you're using property access). In the case of EclipseLink, I don't know (if it does, it will probably requires weaving).
Because I have defined the relation between the two tables in JPA, accessing the edge object to get the two nodes' ids triggers two SQL statements per edge, when the JPA provider lazily loads the associated nodes. Since I already have the node objects, and the ids have already been loaded from the edge table, I want to skip those queries, as they take an awfully long time for larger graphs.
Note that #ManyToOne uses an EAGER strategy by default. If you want to make it LAZY, you have to decalre it explicitly (but again, this will require weaving of your classes with EclipseLink).
I think you should try to optimize your query rather than change the mapping. For example, the following query fetches the whole graph at once (tested in Hibernate):
List<Node> nodes = em.createQuery(
"SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound")
.getResultList();
How about using getReference()?
For example:
Node fkNode = em.getReference(edge.getNodeFrom()); // [1]
fkNode.getId()
[1] This will not trigger a SQL query to retrieve the nodeFrom
Taken from here: http://docs.jboss.org/hibernate/stable/core/reference/en/html/persistent-classes.html#persistent-classes-equalshashcode
I tend to use List since Criteria returns List, so it makes my code cleaner since I don't have to do conversion.
I do something like so..
#OneToMany(cascade= {CascadeType.PERSIST, CascadeType.REMOVE}, mappedBy="parent")
#Column(name="PARENT_ID")
public List<Menu> getChildMenus() {
return childMenus;
}
If I had use Set there, somewhere in my DAO I will have to convert results returned by Criteria to Set first.
I wonder what the repercussion could be by using List they way I am doing.
Set is used as the child table has a primary key such that a child can only be once in a parent. If you use a list there can be duplicate children in the list and this cannot be saved to the database.