How cascade works for maps in JPA? - java

Consider the following case,
#OneToMany(cascade=CascadeType.ALL) // unidirectional
#MapKeyClass(KeyEntityImpl.class)
private Map<KeyEntity,ValueEntity> map=new HashMap<>();
How is the above going to work, is KeyEntity removed on removing an item from the map or the ValueEntity or both?
Thanks in advance.

I found the answer from docs.
The OneToMany annotation may be used within an embeddable class
contained within an entity class to specify a relationship to a
collection of entities. If the relationship is bidirectional, the
mappedBy element must be used to specify the relationship field or
property of the entity that is the owner of the relationship.
When the collection is a java.util.Map, the cascade element and the
orphanRemoval element apply to the map value.
Reference: http://docs.oracle.com/javaee/6/api/javax/persistence/OneToMany.html

Related

MapsId not used, is it automatically understood?

In this part of the page on Baeldung, in class CourseRegistration he is not using #MapsId("id") and he is not even using "referencedColumnName" in the JoinColumn annotation. This was not the case with previous examples on this page. I feel that MapsId and JoinColumn with referencedColumnName should have been used. If not why? He has used the above in all other examples in the same page.
I feel that MapsId and JoinColumn with referencedColumnName should have been used. If not why? He has used the above in all other examples in the same page.
In the example that you provided he uses the mappedBy attribute on the other side of the relationship, where OneToMany exists. This mappedBy attribute provides necessary information to JPA to understand how those 2 entities are related. Keep in mind entity fields represent columns in database and JPA knows what those columns are.
The entity that uses the mappedBy indicates that the other entity on the other side is the owner of that relationship between those 2 entities.
#ManyToOne does not have a mappedBy attribute since by default when you have #OneToMany together with #ManyToOne it is the side that has the #ManyToOne that is the owner of the relationship. So no mappedBy exists on #ManyToOne to short circuit this default mechanism.
But there is a way of making the entity that has the #ManyToOne to be the owner of the relationship. You have to also put above #ManyToOne the #JoinColumn(name = "ref", referencedColumnName = "ref2")
To sum up the example that you point to, uses the default mechanism where the side with the #ManyToOne is the owner of the relationship, and that is indicated directly from the other side with the mappedBy attribute on #OneToMany.
Bonus
#JoinColumn(name = "student_id") without referencedColumnName is used not to indicate the owner side of the relationship but just so the JPA is informed that the extra column that would be used as a foreign key would not have the same name as the field of the entity has. So it is just used to override the foreign key column name that JPA will use as foreign key which will not be the same with the java field name.
As for #MapsId check this answer to understand better how it is used
Both are not needed here for different reasons:
#MapsId is mostly used to share a primary key between entities. CourseRegistration is its own entity with its own primary key, thus it is not needed here. Here is a quote from the official documentation:
Designates a ManyToOne or OneToOne relationship attribute that
provides the mapping for an EmbeddedId primary key, an attribute
within an EmbeddedId primary key, or a simple primary key of the
parent entity. The value element specifies the attribute within a
composite key to which the relationship attribute corresponds. If the
entity's primary key is of the same Java type as the primary key of
the entity referenced by the relationship, the value attribute is not
specified.
(https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/MapsId.html)
referencedColumnName uses the name of the primary key of the referenced table as default and thus is not explicitly needed:
Default (only applies if single join column is being used): The same
name as the primary key column of the referenced table.
(https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/JoinColumn.html#referencedColumnName())

Mapping a complex Map in JPA

i just can't obtain a persistence with an entity which has a field like this:
private Map<String, List<String>> filterValueRange;
i've tried so far:
#ElementCollection
#JoinTable(name="ATTRIBUTE_VALUE_RANGE", joinColumns=#JoinColumn(name="ID"))
#MapKeyColumn (name="Filter_Id")
#Column(name="FILTER")
private Map<String, List<String>> filterValueRange;
But it seems there is still something missing.
Can anybody point me to the right direction?
I'm using jpa as interface, but there's Hibernate under the hood.
thanks!
Mapping nested collection relationship is not supported with JPA however you can easily overcome this by changing the object model a little, for example:
#OneToMany(mappedBy = "parent")
#MapKeyColumn (name="Filter_Id")
private Map<String, ValueRange> filterValueRange;
and in the corresponding ValueRange entity:
#ManyToOne
private Parent parent;
Alternatively you may also take a look at the following post:
JPA Map<String,String[]> mapping
I don't believe it is possible. What you are trying to achieve is to map element collection of element collections.
In case I am wrong, you should use the
#CollectionTable annotation to define your jointable.
But I think you need to define Embedable that represent value range and has its own ElementCollection of values. Than you can map this embedable to your filterValueRange and access the list of values through it.
If you don't need to query by your filterValueRange you can serialize it simply to Blob.

Difference between #OneToMany and #ElementCollection?

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;

JPA 2 -- Using #ElementCollection in CriteriaQuery

#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!)

How to apply a default-restriction on Entity-Bean #OneToMany Relationships

I have two entity models, Customer and Order. Each customer could have thousands of orders. I have a OneToMany and ManyToOne relationship between these two entities.
How do I restrict this relationship's list to only top 10 orders?
Is it possible to apply 'WHERE' condition as an attribute on #OneToMany or not?
Like:
#OneToMany("Where Order.orderNo > 100")
My problem is when the object created by Entity Manager all Orders are created in memory.
Lazy loading can not solve my consideration, because I need to get top 10 orders in default construction.
I mean if it is possible to apply 'WHERE' condition as an attribute on #OneToMany or not?
Not with standard JPA. But some providers have extensions for this. For example, Hibernate does have a #Where annotation:
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#Where(clause="1=1")
public Set<Ticket> getTickets() {
return tickets;
}
References
Hibernate Annotations Reference Guide
2.4.6. Collection related annotations
JPA does not support this. But in EclipseLink you can use an Expression to filter a relationship.
See,
http://wiki.eclipse.org/EclipseLink/Examples/JPA/MappingSelectionCriteria

Categories

Resources