I'm on Hibernate (5.1) and I need to add an element to a collection field on all entities matching a criteria, without the need to fetch them all.
#Entity
public class Order {
#Id
public Long id;
public Date placedOn;
#ManyToMany
public List<Item> items;
}
I need to add the same instance of a new Item to all Orders placed after a certain date. I need to use the Criteria API, not JPQL.
Is that possible? I can't find documentation on this.
P.S. The order/items case is just an example, I'm looking for a generalized method and I'll also need to adapt it to remove items from collections.
You cannot do that using CriteriaUpdate because it's not an update but many inserts because you have a ManyToMany relationship that relays on a relationship table in the database.
So you must query all Orders and then add the Item that will then generate the insert statements.
Related
According to https://developer.android.com/training/data-storage/room/relationships
We can have one-to-many relationships
public class UserWithPlaylists {
#Embedded public User user;
#Relation(
parentColumn = "userId",
entityColumn = "userCreatorId"
)
public List<Playlist> playlists;
}
#Transaction
#Query("SELECT * FROM User")
public List<UserWithPlaylists> getUsersWithPlaylists();
In both entity User and Playlist, we have added a column named sort_key
The purpose is, when we perform query, we can do the following
#Transaction
#Query("SELECT * FROM User order by sort_key")
public List<UserWithPlaylists> getUsersWithPlaylists();
We can control the order of List<UserWithPlaylists>.
But, how about List<Playlist> playlists child entity?
How can we define a custom query for child entity Playlist, so that we can have control over List<Playlist> playlists ordering?
How can we define a custom query for child entity Playlist, so that we can have control over List playlists ordering?
I'm afraid there is no out-of-box way here.
Using #Relation annotation all you have is:
5 annotation's parameters (associateBy, entity, entityColumn, parentColumn, projection. None of them has influence on order of child table's items)
Under the Relation's hood Room uses two queries - first you explicitly write in your DAO
SELECT * FROM User order by sort_key
and another - for fetching data from the child table (based on type of query's result and on #Relation parameters):
SELECT * FROM PlayList WHERE userCreatorId IN (<List of users id from the first query>)
This query is autogenerated and you #Relation annotation has no options to change item's order in it.
Of course, after getting this "unordered" result you can add some post-processing to achieve what you want manually
I'm not sure It is possible to add #Query to a field but what i'm sure is that you can use a particular collection to make the order. These are the steps:
Make the Playlist entity emplements Comparable interface, then define the compareTo method following the sort_key attribute.
Instead of list in UserWithPlaylists entity, use SortedSet or TreeSet this particular collection returns items ordered using natural order or Comparator.
You can implement this functionality in two ways :
1) You could use the Hibernate-specific #Sort annotation to perform in-memory (i.e. not in the database) sorting, using natural sorting or sorting using a Comparator you supply.
For example, assume I have an MyComparator helper class that implements Comparator. Helper class can help you sort by sort_key.
You could change Playlist collection like this:
#org.hibernate.annotations.Sort(type = SortType.COMPARATOR, comparator = MyComparator)
public Set<Playlist> playlists;
#Sort will perform sorting in-memory once the collection has been retrieved from the database.
2) You can use hibernate's #OrderBy annotation, which lets you specify a SQL fragment describing how to perform the sort.
#org.hibernate.annotations.OrderBy("sort_key")
public Set<Playlist> playlists;
Hope that helps..!!
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.
Consider following simple entity model
class Order{
int id;
String description;
//one to one eager load with join column specified
Detail details;
//one to many lazy load with mapped by specified
Collection<Item> items;
}
class Detail{
}
class Item{
String name;
//reference to order
}
Now, let's say the requirement is to load all the orders with item details by some criteria (e.g. description matching something). Simple, i write a hql like "from Order where description...". This loads 1000 entities for example and item collection is lazy loaded. I force load them within the session by calling size.
This of course led to a N+1 problem so i decided to use batch fetching for items. Just added the batch size annotation on item collection and much fewer queries as expected.
However, i am not interested in 'detail' at all but since it is a one to one eager load, there is one query per Order to load this always. I simply want to do away with these queries.
To solve this, i try to do a select without details but i am not sure how to include items (collection) in the query so that it is loaded exactly in the same way as if i was selecting all (that is, lazy loaded which then can utilize batch size on later calls). Some suggestions are to use join in the where clause but that initializes my collection with empty array list (and not with PersistentBag as is the case with Lazy loading).
Looking for solutions.
One possible solution is the following:
Create a POJO which will contain a query result. Example:
public class OrderResult {
private String description;
private String itemName;
// ... more fields, if any
public OrderResult(String desc, String itemName) {
this.description = desc;
this.itemName = itemName;
}
// getters & setters
}
Create a JPQL query using a constructor expression as:
List<OrderResult> resultList = entityManager.createQuery("SELECT NEW OrderResult(o.description, i.name) FROM Order o JOIN o.items i where <condition>", OrderResult.class).getResultList();
So you'll get a list of instances of OrderResult containing only the information you're interested in.
NOTE 1: You're talking of HQL, but HQL is the Hibernate specific legacy query language. As Hibernate is an implementation of JPA, and you tagged your question with JPA, this solution should work in your environment too.
NOTE 2: In the solution, I am using the so called constructor expression of JPQL which is defined using NEW in the select clause. The argument to the NEW operator must be a fully qualified class name,e.g., if you put the OrderResult class in a package com.mycompany.myproject.order, then the expression should look like:
SELECT NEW com.mycompany.myproject.order.OrderResult(...) FROM ...
NOTE 3: This is just to give you a hint how to implement the solution and should be considered as pseodo code.
Please don't ask me why I need to do this, as even if I think I could find another way to solve my specific problem, I want to understand HQL and its limits more.
Given the following entities
#Entity
class Child {
private String someAttribute1;
.....
private String someAttributeN;
#ManyToOne(EAGER)
private Parent parent;
}
class Parent {
private String someParent1;
....
private String someParentN;
}
If I select Child then Hibernate automatically fetches all columns from Child and Parent in a single joined SQL, and that is the typical desired case.
Sometimes I know that, for entities mapped with a large number of columns, I need only a subset.
If I select item.someAttribute1 as someAttribute1, item.someAttribute2 as someAttribute2, item.someAttribute3 as someAttribute3 from Child item etc. tied to a ResultTransformer I can let Hibernate return me only 3 columns from the SQL, or more columns if I list them. OK, that is cool and works like a charm.
However if I need to fetch only, say, 3 columns from Child and 2 from Parent, while the rest can be null, and materialize a Child entity with its relationship, I cannot write the following
select item.someAttribute1 as someAttribute1, item.someAttribute2 as someAttribute2, item.someAttribute3 as someAttribute3, item.parent.someParent1 as parent.someParent1, item.parent.someParent2 as parent.someParent2 from Child item left join item.parent
The above does not work because Hibernate does not allow an alias to be composed. It disallows me to use as parent.someName clause because aliases should probably be flat.
Just to tell a counter example, in languages such as LINQ the problem does not apply
from Child c in children
select new Child {
SomeAttribute1 = c.someAttribute1,
SomeAttribute2 = c.someAttribute2,
Parent = new Parent {
Attribute1 = c.Parent.Attribute1,
.......
}
}
With the above statement, Entity Framework will only fetch the desired columns.
I don't want to make comparison or criticism between Hibernate for Java and Entity Framework for C#, absolutely.
I only have the need to fetch a subset of the columns that compose an entity with a #ManyToOne relationship, in order to optimize memory and bandwidth usage. Some columns from the child entity and some from the parent.
I just want to know if and how is it possible in Hibernate to achieve something like that. To populate parent attribute in the result set with an object of class Parent that is populated with only a subset of columns (the rest being null is no problem). I am using ResultTransformers happily
There are two problems with it.
Hibernate doesn't allow to use nested aliases like as parent.someName in HQL. It produces a parsing error. But you can use nested aliases with Criteria using Projections.property("parent.someName").
Hibernate doesn't have a result transformer to populate result objects using nested aliases.
You can use Criteria requests with a custom result transformer as described here
How to transform a flat result set using Hibernate
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.