HHH-11186 - Add examples for all Hibernate annotations
Document @AssociationOverride and @AttributeOverride
This commit is contained in:
parent
6049131218
commit
43f74be58e
|
@ -16,7 +16,7 @@ See the <<chapters/domain/access.adoc#access,Access type>> section for more info
|
|||
|
||||
The http://docs.oracle.com/javaee/7/api/javax/persistence/AssociationOverride.html[`@AssociationOverride`] annotation is used to override an association mapping (e.g. `@ManyToOne`, `@OneToOne`, `@OneToMany`, `@ManyToMany`) inherited from a mapped superclass or an embeddable.
|
||||
|
||||
//TODO: Add example
|
||||
See the <<chapters/domain/embeddables.adoc#embeddable-override, Overriding Embeddable types>> section for more info.
|
||||
|
||||
[[annotations-jpa-associationoverrides]]
|
||||
==== `@AssociationOverrides`
|
||||
|
@ -28,7 +28,7 @@ The http://docs.oracle.com/javaee/7/api/javax/persistence/AssociationOverrides.h
|
|||
|
||||
The http://docs.oracle.com/javaee/7/api/javax/persistence/AttributeOverride.html[`@AttributeOverride`] annotation is used to override an attribute mapping inherited from a mapped superclass or an embeddable.
|
||||
|
||||
//TODO: Add example
|
||||
See the <<chapters/domain/embeddables.adoc#embeddable-override, Overriding Embeddable types>> section for more info.
|
||||
|
||||
[[annotations-jpa-attributeoverrides]]
|
||||
==== `@AttributeOverrides`
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[[embeddables]]
|
||||
=== Embeddable types
|
||||
:sourcedir: extras
|
||||
:sourcedir: ../../../../../test/java/org/hibernate/userguide/mapping/embeddable
|
||||
:extrasdir: extras
|
||||
|
||||
Historically Hibernate called these components.
|
||||
JPA calls them embeddables.
|
||||
|
@ -19,12 +20,12 @@ Throughout this chapter and thereafter, for brevity sake, embeddable types may a
|
|||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir}/embeddable/Name.java[]
|
||||
include::{extrasdir}/embeddable/Name.java[]
|
||||
----
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir}/embeddable/Address.java[]
|
||||
include::{extrasdir}/embeddable/Address.java[]
|
||||
----
|
||||
====
|
||||
|
||||
|
@ -40,7 +41,7 @@ Most often, embeddable types are used to group multiple basic type mappings and
|
|||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir}/embeddable/Person.java[]
|
||||
include::{extrasdir}/embeddable/Person.java[]
|
||||
----
|
||||
====
|
||||
|
||||
|
@ -57,7 +58,7 @@ So, the embeddable type is represented by the `Name` class and the parent makes
|
|||
====
|
||||
[source,sql]
|
||||
----
|
||||
include::{sourcedir}/embeddable/Person1.sql[]
|
||||
include::{extrasdir}/embeddable/Person1.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
|
@ -69,7 +70,7 @@ In fact, that table could also be mapped by the following entity type instead.
|
|||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir}/embeddable/Person_alt.java[]
|
||||
include::{extrasdir}/embeddable/Person_alt.java[]
|
||||
----
|
||||
====
|
||||
|
||||
|
@ -82,7 +83,7 @@ The composition form is certainly more Object-oriented, and that becomes more ev
|
|||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir}/embeddable/Contact.java[]
|
||||
include::{extrasdir}/embeddable/Contact.java[]
|
||||
----
|
||||
====
|
||||
|
||||
|
@ -104,14 +105,14 @@ JPA defines the `@AttributeOverride` annotation to handle this scenario.
|
|||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir}/embeddable/Contact-AttributeOverride.java[]
|
||||
include::{extrasdir}/embeddable/Contact-AttributeOverride.java[]
|
||||
----
|
||||
====
|
||||
|
||||
This way, the mapping conflict is resolved by setting up explicit name-based property-column type mappings.
|
||||
|
||||
[[embeddable-multiple-namingstrategy]]
|
||||
==== ImplicitNamingStrategy
|
||||
==== Embeddables and ImplicitNamingStrategy
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
|
@ -126,14 +127,14 @@ However, for the purposes of this discussion, Hibernate has the capability to in
|
|||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir}/embeddable/component-safe-implicit-naming.java[]
|
||||
include::{extrasdir}/embeddable/component-safe-implicit-naming.java[]
|
||||
----
|
||||
====
|
||||
|
||||
====
|
||||
[source,sql]
|
||||
----
|
||||
include::{sourcedir}/embeddable/Contact-ImplicitNamingStrategy.sql[]
|
||||
include::{extrasdir}/embeddable/Contact-ImplicitNamingStrategy.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
|
@ -161,4 +162,50 @@ This usage is covered in detail in <<chapters/domain/identifiers.adoc#identifier
|
|||
[IMPORTANT]
|
||||
====
|
||||
Embeddable types that are used as collection entries, map keys or entity type identifiers cannot include their own collection mappings.
|
||||
====
|
||||
====
|
||||
|
||||
[[embeddable-override]]
|
||||
==== Overriding Embeddable types
|
||||
|
||||
If an Embeddabe type is used multiple times in some entity, you need to use the
|
||||
http://docs.oracle.com/javaee/7/api/javax/persistence/AttributeOverride.html[`@AttributeOverride`] and
|
||||
http://docs.oracle.com/javaee/7/api/javax/persistence/AssociationOverride.html[`@AssociationOverride`] annotations
|
||||
to override the default column names definied by the Embeddable.
|
||||
|
||||
Considering you have the following `Publisher` embeddable type
|
||||
which defines a `@ManyToOne` association with the `Country` entity:
|
||||
|
||||
[[embeddable-type-association-mapping-example]]
|
||||
.Embeddable type with a `@ManyToOne` association
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir}/EmbeddableOverrideTest.java[tag=embeddable-type-association-mapping-example, indent=0]
|
||||
----
|
||||
|
||||
[source,sql]
|
||||
----
|
||||
include::{extrasdir}/embeddable/embeddable-type-association-mapping-example.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
Now, if you have a `Book` entity which declares two `Publisher` embeddable types for the ebook and paperback version,
|
||||
you cannot use the default `Publisher` embeddable mapping since there will be a conflict between the two embeddable column mappings.
|
||||
|
||||
Therefore, the `Book` entity needs to override the embeddable type mappings for each `Publisher` attribute:
|
||||
|
||||
[[embeddable-type-override-mapping-example]]
|
||||
.Overriding embeddable type attributes
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir}/EmbeddableOverrideTest.java[tag=embeddable-type-override-mapping-example, indent=0]
|
||||
----
|
||||
|
||||
[source,sql]
|
||||
----
|
||||
include::{extrasdir}/embeddable/embeddable-type-override-mapping-example.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
create table Country (
|
||||
id bigint not null,
|
||||
name varchar(255),
|
||||
primary key (id)
|
||||
)
|
||||
|
||||
alter table Country
|
||||
add constraint UK_p1n05aafu73sbm3ggsxqeditd
|
||||
unique (name)
|
|
@ -0,0 +1,20 @@
|
|||
create table Book (
|
||||
id bigint not null,
|
||||
author varchar(255),
|
||||
ebook_publisher_name varchar(255),
|
||||
paper_back_publisher_name varchar(255),
|
||||
title varchar(255),
|
||||
ebook_publisher_country_id bigint,
|
||||
paper_back_publisher_country_id bigint,
|
||||
primary key (id)
|
||||
)
|
||||
|
||||
alter table Book
|
||||
add constraint FKm39ibh5jstybnslaoojkbac2g
|
||||
foreign key (ebook_publisher_country_id)
|
||||
references Country
|
||||
|
||||
alter table Book
|
||||
add constraint FK7kqy9da323p7jw7wvqgs6aek7
|
||||
foreign key (paper_back_publisher_country_id)
|
||||
references Country
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.userguide.mapping.embeddable;
|
||||
|
||||
import javax.persistence.AssociationOverride;
|
||||
import javax.persistence.AssociationOverrides;
|
||||
import javax.persistence.AttributeOverride;
|
||||
import javax.persistence.AttributeOverrides;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class EmbeddableOverrideTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Book.class,
|
||||
Country.class
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifecycle() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Country canada = new Country();
|
||||
canada.setName( "Canada" );
|
||||
entityManager.persist( canada );
|
||||
|
||||
Country usa = new Country();
|
||||
usa.setName( "USA" );
|
||||
entityManager.persist( usa );
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
Country canada = session.byNaturalId( Country.class ).using( "name", "Canada" ).load();
|
||||
Country usa = session.byNaturalId( Country.class ).using( "name", "USA" ).load();
|
||||
|
||||
Book book = new Book();
|
||||
book.setTitle( "High-Performance Java Persistence" );
|
||||
book.setAuthor( "Vlad Mihalcea" );
|
||||
book.setEbookPublisher( new Publisher( "Leanpub", canada ) );
|
||||
book.setPaperBackPublisher( new Publisher( "Amazon", usa ) );
|
||||
|
||||
entityManager.persist( book );
|
||||
} );
|
||||
}
|
||||
|
||||
//tag::embeddable-type-override-mapping-example[]
|
||||
@Entity(name = "Book")
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(
|
||||
name = "ebookPublisher.name",
|
||||
column = @Column(name = "ebook_publisher_name")
|
||||
),
|
||||
@AttributeOverride(
|
||||
name = "paperBackPublisher.name",
|
||||
column = @Column(name = "paper_back_publisher_name")
|
||||
)
|
||||
})
|
||||
@AssociationOverrides({
|
||||
@AssociationOverride(
|
||||
name = "ebookPublisher.country",
|
||||
joinColumns = @JoinColumn(name = "ebook_publisher_country_id")
|
||||
),
|
||||
@AssociationOverride(
|
||||
name = "paperBackPublisher.country",
|
||||
joinColumns = @JoinColumn(name = "paper_back_publisher_country_id")
|
||||
)
|
||||
})
|
||||
public static class Book {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String title;
|
||||
|
||||
private String author;
|
||||
|
||||
private Publisher ebookPublisher;
|
||||
|
||||
private Publisher paperBackPublisher;
|
||||
|
||||
//Getters and setters are omitted for brevity
|
||||
//end::embeddable-type-override-mapping-example[]
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public Publisher getEbookPublisher() {
|
||||
return ebookPublisher;
|
||||
}
|
||||
|
||||
public void setEbookPublisher(Publisher ebookPublisher) {
|
||||
this.ebookPublisher = ebookPublisher;
|
||||
}
|
||||
|
||||
public Publisher getPaperBackPublisher() {
|
||||
return paperBackPublisher;
|
||||
}
|
||||
|
||||
public void setPaperBackPublisher(Publisher paperBackPublisher) {
|
||||
this.paperBackPublisher = paperBackPublisher;
|
||||
}
|
||||
//tag::embeddable-type-override-mapping-example[]
|
||||
}
|
||||
//end::embeddable-type-override-mapping-example[]
|
||||
|
||||
//tag::embeddable-type-association-mapping-example[]
|
||||
@Embeddable
|
||||
public static class Publisher {
|
||||
|
||||
private String name;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
private Country country;
|
||||
|
||||
public Publisher(String name, Country country) {
|
||||
this.name = name;
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
private Publisher() {}
|
||||
|
||||
//Getters and setters are omitted for brevity
|
||||
//end::embeddable-type-association-mapping-example[]
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Country getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(Country country) {
|
||||
this.country = country;
|
||||
}
|
||||
//tag::embeddable-type-association-mapping-example[]
|
||||
}
|
||||
|
||||
@Entity(name = "Country")
|
||||
public static class Country {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
@NaturalId
|
||||
private String name;
|
||||
|
||||
//Getters and setters are omitted for brevity
|
||||
//end::embeddable-type-association-mapping-example[]
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
//tag::embeddable-type-association-mapping-example[]
|
||||
}
|
||||
//end::embeddable-type-association-mapping-example[]
|
||||
}
|
Loading…
Reference in New Issue