HHH-11186 - Add examples for all Hibernate annotations
Document @MapKeyClass annotation
This commit is contained in:
parent
a4db601eca
commit
1fb9d1fb1a
|
@ -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`
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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]
|
|
@ -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
|
|
@ -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]
|
|
@ -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[]
|
||||
}
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue