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.
//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`

View File

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