#Entity
public class Person {
#ElementCollection
private List<Location> locations;
[...]
}
#Embeddable
public class Location {
private Integer dummy;
private Date creationDate;
[...]
}
Given the following structure, I'd like to perform the HQL or CriteriaQuery equivalent of the following SQL:
SELECT
l.*
FROM
Location l
INNER JOIN
Person p ON (p.id = l.person_id)
WHERE
p.id = ? AND l.creationDate > ?
I want to get back a list of Locations that are associated with the given person and whose creationDate is after the given one.
Thanks in advance!
Mark
Edit***: I have edited the SQL, as it was kinda misleading. I don't want to query for the locations independently.
This is not possible, you cannot query an Embeddable. From the JPA Wikibook:
Embedded Collections
An ElementCollection mapping can be
used to define a collection of
Embeddable objects. This is not a
typical usage of Embeddable objects
as the objects are not embedded in the
source object's table, but stored in a
separate collection table. This is
similar to a OneToMany, except the
target object is an Embeddable
instead of an Entity. This allows
collections of simple objects to be
easily defined, without requiring the
simple objects to define an Id or
ManyToOne inverse mapping.
ElementCollection can also override
the mappings, or table for their
collection, so you can have multiple
entities reference the same Embeddable
class, but have each store their
dependent objects in a separate table.
The limitations of using an
ElementCollection instead of a
OneToMany is that the target
objects cannot be queried,
persisted, merged independently of
their parent object. They are strictly
privately-owned (dependent) objects,
the same as an Embedded mapping.
There is no cascade option on an
ElementCollection, the target
objects are always persisted, merged,
removed with their parent.
ElementCollection still can use a
fetch type and defaults to LAZY the
same as other collection mappings.
To achieve what you want, use a OneToMany and an Entity instead of an ElementCollection and an Embeddable. Or change your approach and query the Person.
The key phrase in Pascal's reply is
the target objects cannot be queried, persisted, merged independently of their parent object
As you are dependent on the parent object, you should be able to do this using something like ...
SELECT p FROM PERSON, IN (p.locations) WHERE p.id = ?1 AND locations = ?2
(Based on the reply at Execute "MEMBER OF" query against 'ElementCollection' Map fields in JP-QL (JPA 2.0) - which is actually a Map #ElementCollection which was what I was looking for an answer to!)
Related
I have two entities - Person and Car. Instead of mapping them with #OneToMany relationship, I decided to use #ElementCollection (storing car ids only), so that I could have less load on my DB, additionaly fetching cars whenever I fetch Person.
But Hibernate keeps creating new linking table like 'person_car_ids'. So my question is - how do I map Person id in 'car' table?
#Entity
public class Person implements Serializable {
#Id
private String id;
#ElementCollection
private Set<Long> carIds = new HashSet<>();
From the JPA Wiki:
An ElementCollection can be used to define a one-to-many relationship to an Embeddable object, or a Basic value (such as a collection of Strings). [...] The ElementCollection values are always stored in a separate table.
It doesn't say anywhere that it won't use a different table to store your basic values, just makes it more convenient without having to use a wrapper.
To your question:
I decided to use #ElementCollection (storing car ids only), so that I could have less load on my DB, additionaly fetching cars whenever I fetch Person.
Not sure I got it right, but if you want to fetch cars only when you need them instead of loading them when you fetch a person entity, you can use Lazy loading, which is the default when you use OneToMany annotation.
I have a table that has no associated Entity. I need to write JPQL query which will filter by that table's fields. Is it possible?
I know that I can do one of the following:
Create an Entity for that table( but it's basically a join table, so it will look strange to create an Entity class for it)
Write a native query( I don't like this approach either. If I use JPA, I must use JPQL only).
Create fully functional ManyToMany mapping( I just don't need it).
Could there be another approach?
Unfortunately you cannot do that with JPQL.
You should use SQL.
But a native query can also return Entities. Either if the returned values matches the entity or using #SqlResultSetMapping as described here:
http://javaee.support/sample/jpa-native-sql-resultset-mapping/
If you only need to join to entities on a relationship that is not mapped JPA 2.1 is able to JOIN on any table columns.
The problem was that I did not need a real many-to-many object mapping but only collection of id's in my only entity. So I came to following solution:
#ElementCollection
#CollectionTable(
name="user_to_feed",
joinColumns = #JoinColumn(name = "feed_id",referencedColumnName = "id")
)
#Column(name="user_id")
private List<Integer> userIds = new ArrayList<>();
This allows me to make following query:
select f.url from Feed f join f.userIds u where :id in u
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>();
What is the difference between using a #OneToMany and #ElementCollection annotation since both work on the one-to-many relationship?
ElementCollection is a standard JPA annotation, which is now preferred over the proprietary Hibernate annotation CollectionOfElements.
It means that the collection is not a collection of entities, but a collection of simple types (Strings, etc.) or a collection of embeddable elements (class annotated with #Embeddable).
It also means that the elements are completely owned by the containing entities: they're modified when the entity is modified, deleted when the entity is deleted, etc. They can't have their own lifecycle.
I believe #ElementCollection is mainly for mapping non-entities (embeddable or basic) while #OneToMany is used to map entities. So which one to use depend on what you want to achieve.
#ElementCollection allows you to simplify code when you want to implement one-to-many relationship with simple or embedded type. For instance in JPA 1.0 when you wanted to have a one-to-many relationship to a list of Strings, you had to create a simple entity POJO (StringWrapper) containing only primary key and the String in question:
#OneToMany
private Collection<StringWrapper> strings;
//...
public class StringWrapper {
#Id
private int id;
private String string;
}
With JPA 2.0 you can simply write:
#ElementCollection
private Collection<String> strings;
Simpler, isn't it? Note that you can still control the table and column names using #CollectionTable annotation.
See also:
Java Persistence/ElementCollection
Basic or Embedded: #ElementCollection
Entities: #OneToMany or #ManyToMany
#ElementCollection:
the relation is managed (only) by the entity in which the relation is defined
table contains id reference to the owning entity plus basic or embedded attributes
#OneToMany / #ManyToMany:
can also be managed by the other entity
join table or column(s) typically contains id references only
#ElementCollection marks a collection. This does not necessarily mean that this collection references a 1-n join.
ElementCollection can override the mappings, or table for their collection, so you can have multiple entities reference the same Embeddable class, but have each store their dependent objects in a separate table.
#ElementCollection
This annotation will be applied when there is a relation with non-entity and these associations relation was HAS-A. Every collection is created with a table and gets relation by Foreign Key.
There are two types of element collections
Index (List, Map)
Non-Index (Set)
Index: The index type collection has a table with 3 columns they are
Key Column (Foriegn Key)
Index Column (Position of data in collection)
Element Column (Data)
Non-Index: The Non-Index type collection has a table with 2 columns they are
Key Column
Element Column
Note: Here it won't have any index column because, Since a SET doesn’t retain the insertion order.
Multiplicity
This is a way to implement the HAS-A relation between two entities and the cardinality ratio depends on their relation.
You can use ElementCollection replace for you use #OneToMany. Example you can have one Project in many versions.
#ElementCollection
#CollectionTable(name="versions",
joinColumns = #JoinColumn(name="projectID"))
#LazyCollection(LazyCollectionOption.FALSE)
#JoinColumn(name="version",nullable = false)
private Set<String> versions;
You also can use #ElementCollection in mapping OGM for array in one collection.
#ElementCollection(fetch = FetchType.EAGER)
private Set<String> researchAreas;
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.