HHH-11186 - Add examples for all Hibernate annotations
Document @MapKeyType annotation
This commit is contained in:
parent
26908a86f5
commit
73affabe44
|
@ -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`
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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[]
|
||||
}
|
|
@ -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[]
|
Loading…
Reference in New Issue