HHH-11186 - Add examples for all Hibernate annotations

Document @MapKeyClass annotation
This commit is contained in:
Vlad Mihalcea 2017-06-01 17:58:57 +03:00
parent a4db601eca
commit 1fb9d1fb1a
8 changed files with 329 additions and 12 deletions

View File

@ -296,7 +296,7 @@ See the <<chapters/domain/collections.adoc#collections-map-unidirectional-exampl
The http://docs.oracle.com/javaee/7/api/javax/persistence/MapKeyClass.html[`@MapKeyClass`] annotation is used to specify the type of the map key of a `java.util.Map` associations.
//TODO: Add example
See the <<chapters/domain/collections.adoc#collections-map-key-class, `@MapKeyClass` mapping>> section for more info.
[[annotations-jpa-mapkeycolumn]]
==== `@MapKeyColumn`

View File

@ -611,7 +611,7 @@ while the `@Column` mapping gives the value of the `java.util.Map` in question.
Since we want to map all the calls by their associated `java.util.Date`, not by their timestamp since epoch which is a number, the entity mapping looks as follows:
[[collections-map-custom-key-type-mapping-example]]
.@MapKeyType mapping example
.`@MapKeyType` mapping example
====
[source,java]
----
@ -628,6 +628,71 @@ include::{sourcedir}/type/TimestampEpochType.java[tags=collections-map-custom-ke
The `TimestampEpochType` allows us to map a Unix timestamp since epoch to a `java.util.Date`.
But, without the `@MapKeyType` Hibernate annotation, it would nt be possible to customize the `Map` key type.
[[collections-map-key-class]]
===== Maps having an interface type as the key
Considering you have the following `PhoneNumber` interface with an implementation given by the `MobilePhone` class type:
[[collections-map-key-class-type-mapping-example]]
.`PhoneNumber` interface and the `MobilePhone` class type
====
[source,java]
----
include::{sourcedir}/MapKeyClassTest.java[tags=collections-map-key-class-type-mapping-example,indent=0]
----
====
If you want to use the `PhoneNumber` interface as a `java.util.Map` key, then you need to supply the
http://docs.oracle.com/javaee/7/api/javax/persistence/MapKeyClass.html[`@MapKeyClass`] annotation as well.
[[collections-map-key-class-mapping-example]]
.`@MapKeyClass` mapping example
====
[source,java]
----
include::{sourcedir}/MapKeyClassTest.java[tags=collections-map-key-class-mapping-example,indent=0]
----
[source,sql]
----
include::{extrasdir}/collections-map-key-class-mapping-example.sql[]
----
====
When inserting a `Person` with a `callRegister` containing 2 `MobilePhone` references,
Hibernate generates the following SQL statements:
[[collections-map-key-class-persist-example]]
.`@MapKeyClass` persist example
====
[source,java]
----
include::{sourcedir}/MapKeyClassTest.java[tags=collections-map-key-class-persist-example,indent=0]
----
[source,sql]
----
include::{extrasdir}/collections-map-key-class-persist-example.sql[]
----
====
When fetching a `Person` and accessing the `callRegister` `Map`,
Hibernate generates the following SQL statements:
[[collections-map-key-class-fetch-example]]
.`@MapKeyClass` fetch example
====
[source,java]
----
include::{sourcedir}/MapKeyClassTest.java[tags=collections-map-key-class-fetch-example,indent=0]
----
[source,sql]
----
include::{extrasdir}/collections-map-key-class-fetch-example.sql[]
----
====
[[collections-map-unidirectional]]
===== Unidirectional maps

View File

@ -4,18 +4,13 @@ create table person (
)
create table call_register (
phone_id int8 not null,
person_id int8 not null,
phone_number int4,
call_timestamp_epoch int8 not null,
primary key (phone_id, call_key)
primary key (person_id, call_key)
)
alter table if exists call_register
add constraint FKsn58spsregnjyn8xt61qkxsub
foreign key (phone_id)
references person
alter table if exists call_register
add constraint FKsn58spsregnjyn8xt61qkxsub
foreign key (phone_id)
foreign key (person_id)
references person

View File

@ -0,0 +1,24 @@
select
cr.person_id as person_i1_0_0_,
cr.call_register as call_reg2_0_0_,
cr.country_code as country_3_0_,
cr.operator_code as operator4_0_,
cr.subscriber_code as subscrib5_0_
from
call_register cr
where
cr.person_id = ?
-- binding parameter [1] as [BIGINT] - [1]
-- extracted value ([person_i1_0_0_] : [BIGINT]) - [1]
-- extracted value ([call_reg2_0_0_] : [INTEGER]) - [101]
-- extracted value ([country_3_0_] : [VARCHAR]) - [01]
-- extracted value ([operator4_0_] : [VARCHAR]) - [234]
-- extracted value ([subscrib5_0_] : [VARCHAR]) - [567]
-- extracted value ([person_i1_0_0_] : [BIGINT]) - [1]
-- extracted value ([call_reg2_0_0_] : [INTEGER]) - [102]
-- extracted value ([country_3_0_] : [VARCHAR]) - [01]
-- extracted value ([operator4_0_] : [VARCHAR]) - [234]
-- extracted value ([subscrib5_0_] : [VARCHAR]) - [789]

View File

@ -0,0 +1,18 @@
create table person (
id bigint not null,
primary key (id)
)
create table call_register (
person_id bigint not null,
call_register integer,
country_code varchar(255) not null,
operator_code varchar(255) not null,
subscriber_code varchar(255) not null,
primary key (person_id, country_code, operator_code, subscriber_code)
)
alter table call_register
add constraint FKqyj2at6ik010jqckeaw23jtv2
foreign key (person_id)
references person

View File

@ -0,0 +1,35 @@
insert into person (id) values (?)
-- binding parameter [1] as [BIGINT] - [1]
insert into call_register(
person_id,
country_code,
operator_code,
subscriber_code,
call_register
)
values
(?, ?, ?, ?, ?)
-- binding parameter [1] as [BIGINT] - [1]
-- binding parameter [2] as [VARCHAR] - [01]
-- binding parameter [3] as [VARCHAR] - [234]
-- binding parameter [4] as [VARCHAR] - [789]
-- binding parameter [5] as [INTEGER] - [102]
insert into call_register(
person_id,
country_code,
operator_code,
subscriber_code,
call_register
)
values
(?, ?, ?, ?, ?)
-- binding parameter [1] as [BIGINT] - [1]
-- binding parameter [2] as [VARCHAR] - [01]
-- binding parameter [3] as [VARCHAR] - [234]
-- binding parameter [4] as [VARCHAR] - [567]
-- binding parameter [5] as [INTEGER] - [101]

View File

@ -0,0 +1,180 @@
/*
* 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.collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapKeyClass;
import javax.persistence.MapKeyColumn;
import javax.persistence.Table;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
/**
* @author Vlad Mihalcea
*/
public class MapKeyClassTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Person.class,
};
}
@Test
public void testLifecycle() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::collections-map-key-class-persist-example[]
Person person = new Person();
person.setId( 1L );
person.getCallRegister().put( new MobilePhone( "01", "234", "567" ), 101 );
person.getCallRegister().put( new MobilePhone( "01", "234", "789" ), 102 );
entityManager.persist( person );
//end::collections-map-key-class-persist-example[]
} );
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::collections-map-key-class-fetch-example[]
Person person = entityManager.find( Person.class, 1L );
assertEquals( 2, person.getCallRegister().size() );
assertEquals(
Integer.valueOf( 101 ),
person.getCallRegister().get( MobilePhone.fromString( "01-234-567" ) )
);
assertEquals(
Integer.valueOf( 102 ),
person.getCallRegister().get( MobilePhone.fromString( "01-234-789" ) )
);
//end::collections-map-key-class-fetch-example[]
} );
}
//tag::collections-map-key-class-mapping-example[]
@Entity
@Table(name = "person")
public static class Person {
@Id
private Long id;
@ElementCollection
@CollectionTable(
name = "call_register",
joinColumns = @JoinColumn(name = "person_id")
)
@MapKeyColumn( name = "call_timestamp_epoch" )
@MapKeyClass( MobilePhone.class )
@Column(name = "call_register")
private Map<PhoneNumber, Integer> callRegister = new HashMap<>();
//Getters and setters are omitted for brevity
//end::collections-map-key-class-mapping-example[]
public void setId(Long id) {
this.id = id;
}
public Map<PhoneNumber, Integer> getCallRegister() {
return callRegister;
}
//tag::collections-map-key-class-mapping-example[]
}
//end::collections-map-key-class-mapping-example[]
//tag::collections-map-key-class-type-mapping-example[]
public interface PhoneNumber {
String get();
}
@Embeddable
public static class MobilePhone
implements PhoneNumber {
static PhoneNumber fromString(String phoneNumber) {
String[] tokens = phoneNumber.split( "-" );
if ( tokens.length != 3 ) {
throw new IllegalArgumentException( "invalid phone number: " + phoneNumber );
}
int i = 0;
return new MobilePhone(
tokens[i++],
tokens[i++],
tokens[i]
);
}
private MobilePhone() {
}
public MobilePhone(
String countryCode,
String operatorCode,
String subscriberCode) {
this.countryCode = countryCode;
this.operatorCode = operatorCode;
this.subscriberCode = subscriberCode;
}
@Column(name = "country_code")
private String countryCode;
@Column(name = "operator_code")
private String operatorCode;
@Column(name = "subscriber_code")
private String subscriberCode;
@Override
public String get() {
return String.format(
"%s-%s-%s",
countryCode,
operatorCode,
subscriberCode
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
MobilePhone that = (MobilePhone) o;
return Objects.equals( countryCode, that.countryCode ) &&
Objects.equals( operatorCode, that.operatorCode ) &&
Objects.equals( subscriberCode, that.subscriberCode );
}
@Override
public int hashCode() {
return Objects.hash( countryCode, operatorCode, subscriberCode );
}
}
//end::collections-map-key-class-type-mapping-example[]
}

View File

@ -110,7 +110,7 @@ public class MapKeyTypeTest extends BaseEntityManagerFunctionalTestCase {
@ElementCollection
@CollectionTable(
name = "call_register",
joinColumns = @JoinColumn(name = "phone_id")
joinColumns = @JoinColumn(name = "person_id")
)
@MapKeyColumn( name = "call_timestamp_epoch" )
@Column(name = "phone_number")
@ -136,7 +136,7 @@ public class MapKeyTypeTest extends BaseEntityManagerFunctionalTestCase {
@ElementCollection
@CollectionTable(
name = "call_register",
joinColumns = @JoinColumn(name = "phone_id")
joinColumns = @JoinColumn(name = "person_id")
)
@MapKeyType(
@Type(