HHH-11186 - Add examples for all Hibernate annotations

Document @AssociationOverride and @AttributeOverride
This commit is contained in:
Vlad Mihalcea 2017-06-08 11:46:32 +03:00
parent 6049131218
commit 43f74be58e
5 changed files with 310 additions and 14 deletions

View File

@ -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. 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]] [[annotations-jpa-associationoverrides]]
==== `@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. 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]] [[annotations-jpa-attributeoverrides]]
==== `@AttributeOverrides` ==== `@AttributeOverrides`

View File

@ -1,6 +1,7 @@
[[embeddables]] [[embeddables]]
=== Embeddable types === Embeddable types
:sourcedir: extras :sourcedir: ../../../../../test/java/org/hibernate/userguide/mapping/embeddable
:extrasdir: extras
Historically Hibernate called these components. Historically Hibernate called these components.
JPA calls them embeddables. JPA calls them embeddables.
@ -19,12 +20,12 @@ Throughout this chapter and thereafter, for brevity sake, embeddable types may a
==== ====
[source,java] [source,java]
---- ----
include::{sourcedir}/embeddable/Name.java[] include::{extrasdir}/embeddable/Name.java[]
---- ----
[source,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] [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] [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] [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] [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] [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. This way, the mapping conflict is resolved by setting up explicit name-based property-column type mappings.
[[embeddable-multiple-namingstrategy]] [[embeddable-multiple-namingstrategy]]
==== ImplicitNamingStrategy ==== Embeddables and ImplicitNamingStrategy
[IMPORTANT] [IMPORTANT]
==== ====
@ -126,14 +127,14 @@ However, for the purposes of this discussion, Hibernate has the capability to in
==== ====
[source,java] [source,java]
---- ----
include::{sourcedir}/embeddable/component-safe-implicit-naming.java[] include::{extrasdir}/embeddable/component-safe-implicit-naming.java[]
---- ----
==== ====
==== ====
[source,sql] [source,sql]
---- ----
include::{sourcedir}/embeddable/Contact-ImplicitNamingStrategy.sql[] include::{extrasdir}/embeddable/Contact-ImplicitNamingStrategy.sql[]
---- ----
==== ====
@ -162,3 +163,49 @@ This usage is covered in detail in <<chapters/domain/identifiers.adoc#identifier
==== ====
Embeddable types that are used as collection entries, map keys or entity type identifiers cannot include their own collection mappings. 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[]
----
====

View File

@ -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)

View File

@ -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

View File

@ -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[]
}