I am defining an entity, myParent, it works fine except for one thing. It has the following defined :
#OneToMany(mappedBy = "myParent", cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
private List<Child> children;
This causes a left or right join to be performed so I get two parents if there are two children. I don't want that I just want one parent, and two children.
I could change fetchtype to lazy but I didn't really want that ... ?
when I select all parents the list contains duplicate parents, I do not want the duplicates.
You should use the DISTINCT_ROOT_ENTITY ResultTransformer.
This means that each row of results is a distinct instance of the root entity.
Related
I have a class called LocType which has 30 elements and a #OneToMany relation to LocProfile which has 40 elements. I have to do some calculations to figure which LocTypeProfile should be associated once a user picks a location, so it needs to be available at jsp side.
If I define the relationship for Eager initialization like below, when I retrieve elements from LocType it gives me 30 elements with the instances where one to many relationship stored in the list - which is the behavior I want.
#OneToMany(fetch = FetchType.EAGER, mappedBy = "LocType ", cascade = CascadeType.ALL)
private List<LocTypeProfile > locTypeProfiles;
Now If I change the mapping to Lazy loading in the interest of good practice
#OneToMany(mappedBy = "creditTypes")
and try to query using a fetch
select lt from LocTypes lt left join fetch lt.locTypeProfiles
I get the 40 elements, where the data is flattened.
My question is is there a way to replicate the behavior of the Eager initialization in this scenario?
When doing a join fetch for a OneToMany relationship you need to use distinct in the select statement, otherwise an entity will be created for each row fetched.
Try:
select distinct lt from LocTypes lt left join fetch lt.locTypeProfiles
Reference: One-To-Many relationship gets duplicate objects whithout using “distinct”.Why?
I have two entities (entityA and entityB), one which contain another with #IndexedEmbedded, because when I search, I want to query by entityB as well, and return entityA.
The relationship (simplified) is as such:
public class EntityA {
private String name;
#OneToMany
#IndexedEmbedded
private List<EntityB> children;
}
public class EntityB {
#ManyToOne
#ContainedIn
private EntityA parent;
private String childName;
}
I am having issues now because relationally, the "children" in entityA can have up to 100k items. This causes OutOfMemory issue no matter if I am using FullTextSession.index or the MassIndexer.
I could actually remove the #OneToMany mapping in EntityA because when I want to access EntityB, I will usually do a query with some filtering and pagination, but if I remove the #OneToMany, then Hibernate Search will not index my EntityB.
Is there anyway to get the FullTextSession.index to perform indexing based on batch on the "children"?
I feel you should remove that relationship like one to many and many to one and write a join query to fetch the results of desired size, fetching 100k rows kills the mem.
Thnx,
Subhash
Depending on the queries you want to effectively execute, you might be able to reverse the indexing.
Instead of parking EntityA as #Indexed, you mark EntityB as #Indexed and mark its parent association with #IndexedEmbedded.
That way, you won't suffer from having to load the 100k elements.
On the query side, you will need to reverse your query as well to something like childName:Emmanuel AND parent.name:Hardy
What you will receive is EntityB instances but you can reach EntityA by simple navigation, or by using a ResultTransformer.
Note that if your link between EntityB and EntityA is lazy, you can ask Hibernate Search to adjust its fetching strategy:
Criteria criteria =
s.createCriteria(EntityB.class).setFetchMode("parent", FetchMode.JOIN);
s.createFullTextQuery(luceneQuery).setCriteriaQuery(criteria);
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
I have a domain object that holds a collection of another object through a #ManyToMany annotaion:
#ManyToMany(fetch=FetchType.LAZY,cascade = { CascadeType.MERGE, CascadeType.PERSIST})
#JoinTable(name = "join_table", joinColumns=#JoinColumn(name="a_id", referencedColumnName = "a_id"), inverseJoinColumns=#JoinColumn(name = "b_id", referencedColumnName = "b_id"))
private List<B> BsList;
In the join table i hold additional data columns.
I noticed that when i work with the object that holds the list and call setBsList() the data i had in the additional columns is deleted.
Does Hibernate re-write the rows in the join table each time?
In the join table i hold additional data columns.
If you have additional data columns, it's not a join table. It's a table with two FKs which can also be PKs. And Hibernate is doing the right thing. So, you should instead create another entity representing this "fake join table", and map it accordingly.
You shouldn't call setBsList(), you should directly modify getBsList() otherwise Hibernate has no way of tracking what has changed.
When loading, the actual List you get is a Hibernate-specific implementation which deals with the lazy loading and tracks what is added and removed, so that it can be correctly updated. If you replace this with a different list implementation it will delete the old one and only add then only add the new entries. When using Hibernate with collections, it's a good idea to make the setter protected so you can't accidentally do this.
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