I have 2 entities as Parent and Child as OneToMany relation as
#Entity
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
#IndexColumn(name = "index", base = 1)
#Cascade(org.hibernate.annotations.CascadeType.ALL)
#LazyCollection(LazyCollectionOption.EXTRA)
private List<Child> childs = new ArrayList<Child>();
// getter and setter
}
So here what is use of #LazyCollection(LazyCollectionOption.EXTRA) and when does it will come in picture, like for which operation with child list, it will be beneficial ?
EXTRA = .size() and .contains() won't initialize the whole collection
TRUE = initialize the whole collection on first access
FALSE = Eager-Loading
There's actually no reason to use #LazyCollection.
The TRUE and FALSE values are not needed since the same behavior can be obtained with the JPA FetchType.LAZY or FetchType.EAGER.
The EXTRA value has no equivalent in JPA and was designed for very large collections. When you access an EXTRA lazy collection for the first time, the collection is not entirely loaded, as it's usually the case with any JPA collection.
Instead, each element is fetched one by one, using a secondary SELECT. This might sound like an optimization, but it's not because EXTRA lazy collections are prone to N+1 query issues.
Note that this only works for ordered collections, either List(s) that are annotated with #OrderColumn or Map(s). For bags (e.g. regular List(s) of entities that do not preserve any certain ordering), the #LazyCollection(LazyCollectionOption.EXTRA) behaves just like any other LAZY collection (the collection is fetched entirely upon being accessed for the first time).
If you have a very large collection, then you should not map it at all. Instead, you should map only the #ManyToOne side, and, instead of a parent-side collection, you should use a paginated JPQL query.
JPQL queries are much easier to tune because you can apply any filtering criteria, and you can paginate the result set.
To give you a hint, it's mainly for performance reasons, you can start reading the following links:
Second Level Cache
Hibernate Documentation
Related
I have two entities, Author and Book, connected with a one-to-many relationship. What's the difference between specifying field type as Collection<Book> and List<Book>? Aforementioned scenario is presented below:
#Entity
public class Author {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany(mappedBy = "author")
private Collection<Book> books = new ArrayList<>(); // List<Book> instead?
}
The only difference I have already noticed is that when I want to use #OrderColumn annotation I need to use List, but are there any other differences I don't know about? Should I always use Collection if I don't need an order?
Set - contains no duplicates no order
(Bag)Collection - duplicates no order
List - duplicates order
For Set you need to be carefull about hashcode and equals. And one interesting twist with Bags in relation to SQL generated:
If we are using List as a mapped attribute in hibernate without
indexed column, hibernates treats it as a Bag. Since Hibernate handles
List as a Bag (Unordered collection with non unique values. The best
feature of a bag is that you can get the number of occurrences of an
object through the API With a list, there is no way to do the same
without iterating through the whole list.) as soon as we delete and
add a element in this collection. Hibernate issues a SQL to delete all
the elements first from join table which are no supposed to be deleted
and then it re-insert all of them back from the Bag.
http://lkumarjain.blogspot.no/2013/07/why-hibernate-does-delete-all-entries.html
java.util.Collection is the most generic unordered collection of elements while the java.util.List implies existence of an iteration order.
Using #OrderColumn will give this iteration order however it might change the generated SQL query. Often it results in ORDER BY statement added to the SQL query. Without #OrderColumn the JPA provider has more flexibility but you should always measure the performance in your actual database instead of tuning it blindly.
I found in some old code strange thing (at least for me).
The field which is annotated #ManyToOne is also annotated with #BatchSize.
I always thought that #BatchSize annotation only affects when annotated at class level or on a collection (#OneToMany) and affects pre-fetching when iterating.
But maybe I am wrong and annotating #ManyToOne with #BatchSize affects something. I can't find the answer in the documentation.
Does annotating #ManyToOne with #BatchSize have sense?
I think the question refers to combining #ManyToOne and #BatchSize on the same field, e.g.:
#ManyToOne
#BatchSize(size = 5)
private User owner;
This use case is not supported by Hibernate, at least when using annotations. The only uses of batch fetching mentioned by the documentation are:
On collection fields, i.e., #OneToMany or #ManyToMany (but not #ManyToOne)
On the entity class to be fetched
E.g.:
#Entity
#BatchSize(size = 5)
public class User {
...
}
This latter case enables batching for all relationships of type User, including many-to-one relationships. However, with the annotation on the entity class it is not possible to control the behaviour on a field-by-field basis.
A search through the Hibernate source code for all uses of #BatchSize confirms the lack of support for your usage. From what I see in AnnotationBinder.java, the #BatchSize annotation is only inspected on the entity class and on fields which have some kind of #XxxToMany annotation.
#ManyToOne associated with #BatchSize could make sense only if the corresponding field is marked as lazy (lazy=true).
Indeed, if the field is not lazy, it's by definition already loaded since the enclosing entity is loaded, so the problem of database calls doesn't apply.
Imagine a Person class who has a collection of ShoesPair element (ShoesPair.class) and within this one is present an owner field marked as lazy (since optional and not really bringing an important information when retrieving a specific pair of shoes).
One wants to iterate through 25 pair of shoes (25 ShoesPair objects) in order to retrieve their owner.
If the owner field (corresponding to one person) is only annotated with #ManyToOne, there would be 25 select to database.
However, if annoted with #BatchSize(size=5), there would be merely 5 calls and so increasing performance.
From the Hibernate documentation, it is precised that batch size does not only apply with collections:
You can also enable batch fetching of collections.
Hibenate mentions especially #OneToMany cases, because these one are applied with fields that are in 90% of cases marked as lazy.
Solving N+1 query problem with Hibernate
1 Using Criteria queries with fetchMode
Criteria criteria = session.createCriteria(Customer.class);
criteria.setFetchMode("contact", FetchMode.EAGER);
2 HOL fetch join
3 #BatchSize
The #BatchSize annotation can be used to define how many identical associations to populate in a single database query. If the session has 100 customers attached to it and the mapping of the 'contact' collection is annotated with #BatchSize of size n. It means that whenever Hibernate needs to populate a lazy contact collection it checks the session and if it has more customers which their contact collections need to be populated it fetches up to n collections.
#OneToMany(mappedBy="customer",cascade=CascadeType.ALL, fetch=FetchType.LAZY)
#BatchSize(size=25)
private Set<Contact> contacts = new HashSet<Contact>();
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.
Does hibernate preserve the order of a LinkedHashSet and if so, how? In case this depends on the type of database, I'd like to know this for PostgreSQL.
Background:
I know what a LinkedHashSet is for, and the reason I'm asking this is because I'm logging the names of some functions I execute to a 'logError' table that has a many-to-many relation to some 'functionName' table.
I need these functions to remain in the same order as when I executed them, so first I find the corresponding 'functionName' objects, put them in a LinkedHashSet (after each function that failed) and then I persist the 'logError' object.
Now when I get the 'logError' object from the database again, will it still be ordered? And if so, I was curious how this is done by Hibernate.
First: I assume you are talking about a relationship between two entities. Something like
#Entity
public class A {
#ManyToMany
#JoinTable(name = "A_B", joinColumns = { #JoinColumn(name = "A_fk") }, inverseJoinColumns = { #JoinColumn(name = "B_fk") })
private Set<B> bSet = new LinkedHashSet<B>();
}
Hibernate does not preserve the order by itself!
If you have a look at the classes used when entity A is loaded from the database, then the Set bSet is of type PersistentSet, which is a wrapper around another Set, and this is (in my case) a normal HashSet. (HashSet does not preserve the order of its elements.)
Even if Hibernate used List or LinkedHashSet, it is still inadvisable to base the implementation on the natural (not guaranteed) database order. For MySQL it is some kind of anti-pattern.
But you can use the #Sort annotation (org.hibernate.annotations.Sort) to make your sorting explicit. For example:
#OneToMany(mappedBy = "as")
#Sort(type = SortType.COMPARATOR, comparator = MyBComparator.class);
public SortedSet<C> cs;
#see: Ordering return of Child objects in JPA query
Added by Łukasz Rzeszotarski on 1th Sep 2012:
But we have to remember that using #Sort annotation causes sorting objects in memory (jvm) and not in sql. Instead we can use #OrderBy annotation that causes sorting on the sql server side. Both of these annotations have in my (Łukasz Rzeszotarski ) opinion one weakness that set up ordering by default. I (Łukasz Rzeszotarski ) would rather hibernate uses own LinkedHashSet implementation when it 'sees' that order by clause is used.
#see: Hibernate ordering in sql
Sets do not have an inherent 'order', nor do database tables - you can apply an order when you query the data, but unless you preserve that order (eg by getting rows one by one and putting them into an ordered container like a linked list) then the returned data will be unordered too.
When I need to persist the order of a List of items, I use a Map and not a List.
#OneToMany(fetch = FetchType.EAGER, mappedBy = "rule", cascade = CascadeType.ALL)
#MapKey(name = "position")
private Map<Integer, RuleAction> actions = LazyMap.decorate(new LinkedHashMap<>(), FactoryUtils.instantiateFactory(RuleAction.class, new Class[] { Rule.class }, new Object[] { this }));
In this Java example, position is an Integer property of RuleAction so the order is persisted that way. I guess in C# this would look rather similar.
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