HHH-11186 - Add examples for all Hibernate annotations

Document @MapKeyType annotation
This commit is contained in:
Vlad Mihalcea 2017-05-24 11:42:15 +03:00
parent 26908a86f5
commit 73affabe44
5 changed files with 300 additions and 1 deletions

View File

@ -952,7 +952,7 @@ See the <<chapters/domain/basic_types.adoc#mapping-column-many-to-any, `@ManyToA
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/MapKeyType.html[`@MapKeyType`] annotation is used to specify the map key type.
//TODO: Add example
See the <<chapters/domain/collections.adoc#collections-map-custom-key-type, `@MapKeyType` mapping>> section for more info.
[[annotations-hibernate-metavalue]]
==== `@MetaValue`

View File

@ -550,6 +550,42 @@ include::{extrasdir}/collections-map-value-type-entity-key-add-example.sql[]
----
====
[[collections-map-custom-key-type]]
===== Maps with a custom key type
Hibernate defines the
https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/MapKeyType.html[`@MapKeyType`] annotation
which you can use to customize the `Map` key type.
Considering you have the following tables in your database:
[source,sql]
----
include::{extrasdir}/collections-map-custom-key-type-sql-example.sql[]
----
The `call_register` records the call history for every `person`.
However, the timestamp is stored as a Unix timestamp since epoch and we want to map all the calls
by their associated `java.util.Date`.
Therefore, the entity mapping looks as follows:
[[collections-map-custom-key-type-mapping-example]]
.@MapKeyType mapping example
====
[source,java]
----
include::{sourcedir}/MapKeyTypeTest.java[tags=collections-map-custom-key-type-mapping-example,indent=0]
----
----
include::{sourcedir}/type/TimestampEpochType.java[tags=collections-map-custom-key-type-mapping-example,indent=0]
----
====
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-unidirectional]]
===== Unidirectional maps

View File

@ -0,0 +1,21 @@
create table person (
id int8 not null,
primary key (id)
)
create table call_register (
phone_id int8 not null,
phone_number int4,
call_timestamp_epoch int8 not null,
primary key (phone_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)
references person

View File

@ -0,0 +1,160 @@
/*
* 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.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapKeyColumn;
import javax.persistence.Table;
import org.hibernate.annotations.MapKeyType;
import org.hibernate.annotations.Type;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.jpa.boot.spi.Bootstrap;
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 MapKeyTypeTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
PersonDummy.class,
};
}
@Test
public void testLifecycle() {
LocalDateTime firstCall = LocalDateTime.of(
2017, 5, 23, 18, 21, 57
);
LocalDateTime secondCall = LocalDateTime.of(
2017, 5, 23, 18, 22, 19
);
doInJPA( this::entityManagerFactory, entityManager -> {
PersonDummy person = new PersonDummy();
person.setId( 1L );
person.getPhoneRegister().put( Timestamp.valueOf( firstCall ).getTime(), 101 );
person.getPhoneRegister().put( Timestamp.valueOf( secondCall ).getTime(), 102 );
entityManager.persist( person );
} );
EntityManagerFactory entityManagerFactory = null;
try {
Map settings = buildSettings();
settings.put(
org.hibernate.jpa.AvailableSettings.LOADED_CLASSES,
Collections.singletonList(
Person.class
)
);
settings.put(
AvailableSettings.HBM2DDL_AUTO,
"none"
);
entityManagerFactory = Bootstrap.getEntityManagerFactoryBuilder(
new TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() ),
settings
).build().unwrap( SessionFactoryImplementor.class );
final EntityManagerFactory emf = entityManagerFactory;
doInJPA( () -> emf, entityManager -> {
Person person = entityManager.find( Person.class, 1L );
assertEquals(
Integer.valueOf( 101 ),
person.getCallRegister().get( Timestamp.valueOf( firstCall ) )
);
} );
}
finally {
if ( entityManagerFactory != null ) {
entityManagerFactory.close();
}
}
}
@Entity
@Table(name = "person")
public static class PersonDummy {
@Id
private Long id;
@ElementCollection
@CollectionTable(
name = "call_register",
joinColumns = @JoinColumn(name = "phone_id")
)
@MapKeyColumn( name = "call_timestamp_epoch" )
@Column(name = "phone_number")
private Map<Long, Integer> callRegister = new HashMap<>();
public void setId(Long id) {
this.id = id;
}
public Map<Long, Integer> getPhoneRegister() {
return callRegister;
}
}
//tag::collections-map-custom-key-type-mapping-example[]
@Entity
@Table(name = "person")
public static class Person {
@Id
private Long id;
@ElementCollection
@CollectionTable(
name = "call_register",
joinColumns = @JoinColumn(name = "phone_id")
)
@MapKeyType(
@Type(
type = "org.hibernate.userguide.collections.type.TimestampEpochType"
)
)
@MapKeyColumn( name = "call_timestamp_epoch" )
@Column(name = "phone_number")
private Map<Date, Integer> callRegister = new HashMap<>();
public void setId(Long id) {
this.id = id;
}
public Map<Date, Integer> getCallRegister() {
return callRegister;
}
}
//end::collections-map-custom-key-type-mapping-example[]
}

View File

@ -0,0 +1,82 @@
/*
* 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.type;
import java.sql.Timestamp;
import java.util.Comparator;
import java.util.Date;
import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
import org.hibernate.type.LiteralType;
import org.hibernate.type.StringType;
import org.hibernate.type.VersionType;
import org.hibernate.type.descriptor.java.JdbcTimestampTypeDescriptor;
import org.hibernate.type.descriptor.sql.BigIntTypeDescriptor;
/**
* @author Vlad Mihalcea
*/
//tag::collections-map-custom-key-type-mapping-example[]
public class TimestampEpochType
extends AbstractSingleColumnStandardBasicType<Date>
implements VersionType<Date>, LiteralType<Date> {
public static final TimestampEpochType INSTANCE = new TimestampEpochType();
public TimestampEpochType() {
super(
BigIntTypeDescriptor.INSTANCE,
JdbcTimestampTypeDescriptor.INSTANCE
);
}
@Override
public String getName() {
return "epoch";
}
@Override
public Date next(
Date current,
SharedSessionContractImplementor session) {
return seed( session );
}
@Override
public Date seed(
SharedSessionContractImplementor session) {
return new Timestamp( System.currentTimeMillis() );
}
@Override
public Comparator<Date> getComparator() {
return getJavaTypeDescriptor().getComparator();
}
@Override
public String objectToSQLString(
Date value,
Dialect dialect) throws Exception {
final Timestamp ts = Timestamp.class.isInstance( value )
? ( Timestamp ) value
: new Timestamp( value.getTime() );
return StringType.INSTANCE.objectToSQLString(
ts.toString(), dialect
);
}
@Override
public Date fromStringValue(
String xml) throws HibernateException {
return fromString( xml );
}
}
//end::collections-map-custom-key-type-mapping-example[]