I am using currently Spring Data JPA and I would like to map an attribute
#Entity
public class Outer {
...
Map<String, List<String>> typesToCategories;
}
Let's assume I have a tables outer and outer_type_category. The first one is trivial: only column outer_id is relevant from it
CREATE TABLE outer_types_categories (
id uuid NOT NULL,
outer_id uuid NOT NULL,
type character varying(128) NOT NULL,
category character varying(128) NOT NULL,
...
)
Which annotations should I use (if it is possible in general) to map this table to the map?
I have tried to use this
#ElementCollection
#CollectionTable(name = "outer_type_category", joinColumns = [JoinColumn(name = "outer_id")])
#MapKeyColumn(name = "type")
#Column(name = "category")
Map<String, List<String>> typesToCategories;
but in the end I see an exception:
Caused by: org.hibernate.MappingException: Could not determine type for: java.util.List, at table: outer_type_category, for columns: [org.hibernate.mapping.Column(category)]
Did I forget anything?
JPA specifies that
A persistent field or property of an entity or embeddable class may
correspond to a collection of a basic type or embeddable class
(“element collection”).
(JPA 2.2 Specification, section 2.6; emphasis added)
java.util.List is neither a basic type nor an embeddable class, so it is not among the allowed element types of an element collection. Moreover, JPA goes on to say
An embeddable class [...] that is contained within an element
collection must not contain an element collection
, so even replacing List with an embeddable class does not give you a suitable mechanism for mapping the structure you've described. If you're unwilling or unable to change the DB structure at all, then I don't think you can map your table in a manner that is in any way analogous to what you describe.
If you can at least add a new table to the DB then you can introduce a new entity representing an entry in your map, say OuterType, which has an element collection mapped to your outer_types_categories table. It would probably need to have a composite ID corresponding to (outer_id, type). Even then, the DB side would need to be set up to automatically assign values to the id column of the collection table (unless you can drop that column, which in truth does not appear to be useful for your apparent purposes), because members of an element collection are not entities, and therefore JPA does not ascribe IDs to them. Moreover, it is messy (on the JPA side) to have a column that is both part of a composite primary key and a foreign key for a related entity, as this would require.
If you have more freedom to modify the DB structure then I'd set up the aforementioned OuterType entity with a standard, surrogate ID and a bidirectional one-to-one relationship with Outer, represented on the Outer side as a map. Set up an element collection containing the category strings in OuterType using the default mapping strategy, which would use OuterType's (surrogate) ID and neither its "type" nor its "outer_id" in the collection table.
Related
I`ve noticed that in my project some composite constraint are set in a liquidbase,
for example
constraint importer_ukey unique (name, country_id, is_importer, is_manufacturer)
but then they are repeated in entity definition in #Table annotation
#Table(uniqueConstraints = {#UniqueConstraint(name = "importer_ukey",
columnNames = {"name", "country_id", "is_importer", "is_manufacturer"})})
Is there any positive in repetition of uniqueConstraint in entity ?
Other than for creating the schema based on the JPA annotations, it is also a needed at runtime for the JPA provider to order INSERT, UPDATE and DELETE statements correctly.
Unique constraints ensure that the data in a column or combination of columns is unique for each row. A table's primary key, for example, functions as an implicit unique constraint. In JPA, you represent other unique constraints with an array of UniqueConstraint annotations within the table annotation. The unique constraints you define are used during table creation to generate the proper database constraints, and may also be used at runtime to order INSERT, UPDATE , and DELETE statements. For example, suppose there is a unique constraint on the columns of field F. In the same transaction, you remove an object A and persist a new object B, both with the same F value. The JPA runtime must ensure that the SQL deleting A is sent to the database before the SQL inserting B to avoid a unique constraint violation.
Source: https://openjpa.apache.org/builds/1.0.2/apache-openjpa-1.0.2/docs/manual/jpa_overview_mapping_unq.html
So yes: it is important.
In this article http://www.jroller.com/eyallupu/entry/hibernate_the_any_annotation and also in this question How to use Hibernate #Any-related annotations?, how #Any annotation can be used was explained. But how can I get borrows for each DVD/VHS/BOOK? How can I do mapping definition on DVD/VHS/BOOK?
I don't think this is supported and, as mentioned in the documentation:
2.4.5.2. #Any
The #Any annotation defines a
polymorphic association to classes
from multiple tables. This type of
mapping always requires more than one
column. The first column holds the
type of the associated entity. The
remaining columns hold the identifier.
It is impossible to specify a foreign
key constraint for this kind of
association, so this is most certainly
not meant as the usual way of mapping
(polymorphic) associations. You should
use this only in very special cases
(eg. audit logs, user session data,
etc).
While I understand that this annotation has been introduced to map an association property to different types of entities that don't have a common ancestor entity, I think it would be better to introduce a base type the other entities would inherit from for bidirectional relations.
See also
#Any bidirectional relationship fails
Yes, the problem with bi-directional associations where one side is polymorphic (mapped with #Any or #ManyToAny), is that Hibernate auto-generates an invalid foreign key. Personally, I view this as a bug, not a usage error.
You can get around this by specifying the foreign key explicitly, i.e. not rely on Hibernate to infer it. In the Borrow <--> DVD/VHS/Book example, let's say you want a bi-directional, many-to-one association between Borrow and DVD/VHS/Book (the "item"), then you map it on the Borrow side with the polymorphic #Any mechanism to item, and on the item side with a #OneToMany to Borrow.
BUT: on the latter property/getter, you also specify, explicitly, the join column to use, e.g. "ITEM_ID". That should force Hibernate to use only "ITEM_ID", and not (as I've seen) ITEM_ID + ITEM_TYPE that it infers by default from the #Any definition on the other side.
If you don't have a "Item" superclass for DVD/VHS/Book, you'll have to declare this in each class, something like:
#Entity
class Book {
...
#OneToMany
#JoinColumn(name="item_id")
public List<Borrow> getBorrows() {
return this.borrows;
}
}
One to many documentation
Specifically in this bit:
#OneToMany
#JoinColumn(name="PART_ID")
public Set<Part> getParts() { return parts; }
Do I absolutely have to specify
name="PART_ID"
i.e. the actual column name in the database as opposed to say
name="partId"
where partId is the partId data member in the Part class. Doesn't specifying the actual column name PART_ID defeat the purpose?
When you perform a join the on clause uses column(s) from both tables, nothing guarantees that the column(s) used are the primary keys for the adjoining table. Your indicating which column on the source entities mapped table need to be used for the join. Also nothing guarantees that the field name is different from the column name in the table, since #Column can map any field to any column.
If JPA did not allow for the JoinColumn to be specified it would be enforcing rules on your database structure. Think of the implications this would have when trying to use JPA on an existing database, which could mean massive code changes if the database did not meet these conditions. I think it was a very sound choice by the specification writers to allow for the column name to be specified. Specifying the JoinColumn or even Column provides an added layer of flexibility to the architecture.
If you want to omit specifying of the joincolumn you can always follow the default JPA naming conventions.
It's because you can join on a column not represented by a field in the target entity, e.g. a parent/child relationship where the Child object does not contain a reference to the Parent object.
I'm currently coming (back) up to speed with EJB and while I was away it changed drastically (so far for the better). However, I've come across a concept that I am struggling with and would like to understand better as it seems to be used in our (where I work, not me and all the voices in my head) code quite a bit.
Here's the example I've found in a book. It's part of an example showing how to use the #EmbeddedId annotation:
#Entity
public class Employee implements java.io.Serializable
{
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name="lastName", column=#Column(name="LAST_NAME"),
#AttributeOverride(name="ssn", column=#Column(name="SSN"))
})
private EmbeddedEmployeePK pk;
...
}
The EmbeddedEmployeePK class is a fairly straightforward #Embeddable class that defines a pair of #Columns: lastName and ssn.
Oh, and I lifted this example from O'Reilly's Enterprise JavaBeans 3.1 by Rubinger & Burke.
Thanks in advance for any help you can give me.
It's saying that the attributes that make up the embedded id may have predefined (through explicit or implicit mappings) column names. By using the #AttributeOverride you're saying "ignore what other information you have with regard to what column it is stored in, and use the one I specify here".
In the UserDetails class, I have overridden homeAddress & officeAddress with Address
This One Address POJO will act for two table in DB.
DB:
Table1 Table2
STREET_NAME HOME_STREET_NAME
CITY_NAME HOME_CITY_NAME
STATE_NAME HOME_STATE_NAME
PIN_CODE HOME_PIN_CODE
The EmbeddedEmployeePK class is a fairly straightforward #Embeddable class that defines a pair of #Columns: lastName and ssn.
Not quite - EmbeddedEmployeePK defines a pair of properties, which are then mapped to columns. The #AttributeOverride annotations allow you to override the columns to which the embedded class's properties are mapped.
The use case for this is when the embeddable class is used in different situations where its column names differ, and some mechanism is required to let you change those column mappings. For example, say you have an entity which contains two separate instances of the same embeddable - they can't both map to the same column names.
JPA tries to map field names to column names in a datasource, so what you're seeing here is the translation between the name of a field variable to the name of a column in a database.
You can override also other column properties (not just names).
Let's assume that you want to change the length of SSN based on who is embedding your component. You can define an #AttributeOverride for the column like this:
#AttributeOverrides({
#AttributeOverride(name = "ssn", column = #Column(name = "SSN", length = 11))
})
private EmbeddedEmployeePK pk;
See "2.2.2.4. Embedded objects (aka components)" in the Hibernate Annotations documentation.
In order to preserve other #Column properties (such as name and nullable) keep them on the overridden column the same as you specified on the original column.
I'm trying to move some old code from hand-made persistence to Hibernate. The issue here is a peculiar mapping where the target entity type/table is defined by a column value.
Example database table:
Table "Relation":
id - my primary key
parentType - a char that specifies the type of parent (e.g. 'I' meaning an object in the Item table, 'C' in the Category table)
parentId - primary key in the table of parentType
childType - a char that specified the type of child
childId - primary key in the table of the child
The thing is that all these types (such as Item or Category) have the same abstract parent called GenericObject, which doesn't have its own table. The inheritance is TABLE_PER_CLASS.
The bad thing is that by the parentId or childId you cannot tell the type, the id is not unique among these tables. You need to look at parentType and childType.
Now how do I map this madness in Hibernate? I would like to have the parent and child mapped to a GenericObject variable, which would actually be an instance of Item or Category.
Any ideas?
Hibernate has #Any and #ManyToAny annotations for the cases when target entity is identified by type and non-unique id.
I think in your case, since both parent and child are identified this way, you need to create Relation entity with relations to parent and child annotated with #Any.