As I can see from generated SQL while querying entities with collection mapped like this:
#ElementCollection(targetClass = Long.class, fetch = FetchType.EAGER)
#CollectionTable(name = "LIST_GAMES",
joinColumns = #JoinColumn(name = "LIST_ID", referencedColumnName = "ID"))
#Column(name = "GAME_ID")
#OrderColumn(name = "GAME_ORDER")
private List<Long> gameIds;
Hibernate doesn't add any 'order by' statements to it. So does it mean, that collection is sorted in-memory after loading? Or does it means that I need a complex index on db level like (LIST_ID, GAME_ORDER) to handle order by in the db?
UP: My question is actually HOW hibernate sorts this collection? Is It handled by hibernate in-memory or its retrieved already sorted from db? And am I need to create database index for GAME_ORDER column?
OrderBy adds an order by clause to the generated SQL to order the members of the retrieved collection by a column of the table of the target entity:
#OrderBy("GAME_ORDER ASC")
public List<Long> gameIds;
will generate a SQL query like
select ... order by game.game_order ASC
#OrderColumn defines the name of an additional column in the table, containing the index of the entity in the list.
If you change the ordering of the elements in the list, Hibernate will change the value of this column. And if your gameId have 0, 3, and 5 as values in this column, the list will be
[gameIds0, null, null, gameIds3, null, gameIds5]
see here
As per Oracle Doc #OrderColumn is:
Specifies a column that is used to maintain the persistent order of a
list. The persistence provider is responsible for maintaining the
order upon retrieval and in the database. The persistence provider is
responsible for updating the ordering upon flushing to the database to
reflect any insertion, deletion, or reordering affecting the list.
So it is likely that the collection is sorted after the data is retrieved from the database and it is done in memory.
Related
I am trying to solve following problem:
I have one table of ORDERS that contains: group of goods, type of goods and order date...this refers to table with GOODS, where group, type and valid-from is the PK + valid-to field.
I had a dream, that I can map goods to orders using JPA to save multiple SQL queries.
I believe I can basically use following aproach to map the composite relationship:
#JoinColumns({
#JoinColumn(name = "heilgruppe", referencedColumnName = "heilgruppe"),
#JoinColumn(name = "code", referencedColumnName = "heilmittel_code")
})
But I have troubles with the date attribute...because I obviously cannot just use "equals" to map one date field to another...I need to select such goods, where date of the order lies between valid-from and valid-to dates
But how to do it with JPA? Is it even possible to do it? Or am I forced to create new SQL query later in code to retrive required info?
Don't use composite PK in GOODS, but a technical ID (maybe generated) and you will avoid that problem and use this PK in ORDERS.
Or use the all GOODS composite PK in ORDERS and add valid-from in ORDERS (but it not the best way).
In this specific scenario, say I have a couple of tables {A,B} and their generated JPA classes. There is a FK relationship on a column X from Table A -> Table B and this column X in Table A has been annotated with
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "X", referencedColumnName = "X", insertable = false, updatable = false).
I have a Named JPA Query which is joining the two tables on this column "X". On execution, I get the correct results,but multiple queries are being run against the DB. On
the join's each data match, it appears a query is being generated and run against the DB. This is overburdening our Database. From my understanding, on a LAZY fetch, isn't JPA supposed to execute the master query only once and bring back the entire result-set, instead of spawning additional child queries for join's data match
You shouldn't have to do a join query to get the data. If you specify OneToOne, Hibernate associates the two objects. LAZY means that the request to get the linked object will only be launched when you need that object.
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.
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