HHH-13096 - Document that composite identifier cannot use auto-generated properties

This commit is contained in:
Vlad Mihalcea 2018-11-12 15:14:01 +02:00
parent 84bc30d34a
commit f4e36a1bea
5 changed files with 308 additions and 1 deletions

View File

@ -99,7 +99,7 @@ The restriction that a composite identifier has to be represented by a "primary
Hibernate does allow composite identifiers to be defined without a "primary key class", although that modeling technique is deprecated and therefore omitted from this discussion.
====
The attributes making up the composition can be either basic, composite, ManyToOne.
The attributes making up the composition can be either basic, composite, `@ManyToOne`.
Note especially that collections and one-to-ones are never appropriate.
[[identifiers-composite-aggregated]]
@ -208,6 +208,69 @@ include::{sourcedir}/IdManyToOneTest.java[tag=identifiers-composite-id-fetching-
----
====
[[identifiers-composite-generated]]
==== Composite identifiers with generated properties
When using composite identifiers, the underlying identifier properties must be manually assigned by the user.
Automatically generated properties are not supported can be used to generate the value of an underlying property that makes the composite identifier.
Therefore, you cannot use any of the automatic property generator described by the <<chapters/domain/basic_types.adoc#mapping-generated, generated properties section>> like `@Generated`, `@CreationTimestamp` or `@ValueGenerationType` or database-generated values.
Nevertheless, you can still generate the identifier properties prior to constructing the composite identifier, as illustrated by the following examples.
Assuming we have the following `EventId` composite identifier and an `Event` entity which uses the aforementioned composite identifier.
[[identifiers-composite-generated-mapping-example]]
.The Event entity and EventId composite identifier
====
[source,java]
----
include::{sourcedir}/composite/Event.java[tag=identifiers-composite-generated-mapping-example, indent=0]
----
[source,java]
----
include::{sourcedir}/composite/EventId.java[tag=identifiers-composite-generated-mapping-example, indent=0]
----
====
[[identifiers-composite-generated-in-memory]]
===== In-memory generated composite identifier properties
If you want to generate the composite identifier properties in-memory,
you need to do that as follows:
[[identifiers-composite-generated-in-memory-example]]
.In-memory generated composite identifier properties example
====
[source,java]
----
include::{sourcedir}/composite/EmbeddedIdInMemoryGeneratedValueTest.java[tag=identifiers-composite-generated-in-memory-example, indent=0]
----
====
Notice that the `createdOn` property of the `EventId` composite identifier was generated by the data access code and assigned to the
identifier prior to persisting the `Event` entity.
[[identifiers-composite-generated-database]]
===== Database generated composite identifier properties
If you want to generate the composite identifier properties using a database function or stored procedure,
you could to do it as illustrated by the following example.
[[identifiers-composite-generated-database-example]]
.Database generated composite identifier properties example
====
[source,java]
----
include::{sourcedir}/composite/EmbeddedIdDatabaseGeneratedValueTest.java[tag=identifiers-composite-generated-database-example, indent=0]
----
====
Notice that the `createdOn` property of the `EventId` composite identifier was generated by calling the `CURRENT_TIMESTAMP` database function,
and we assigned it to the composite identifier prior to persisting the `Event` entity.
[[identifiers-generators]]
==== Generated identifier values

View File

@ -0,0 +1,67 @@
/*
* 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.identifier.composite;
import java.sql.Timestamp;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
/**
* @author Vlad Mihalcea
*/
@RequiresDialect(H2Dialect.class)
public class EmbeddedIdDatabaseGeneratedValueTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Event.class };
}
@Test
@TestForIssue(jiraKey = "HHH-13096")
public void test() {
final EventId eventId = doInJPA( this::entityManagerFactory, entityManager -> {
//tag::identifiers-composite-generated-database-example[]
Timestamp currentTimestamp = (Timestamp) entityManager
.createNativeQuery(
"SELECT CURRENT_TIMESTAMP" )
.getSingleResult();
EventId id = new EventId();
id.setCategory( 1 );
id.setCreatedOn( currentTimestamp );
Event event = new Event();
event.setId( id );
event.setKey( "Temperature" );
event.setValue( "9" );
entityManager.persist( event );
//end::identifiers-composite-generated-database-example[]
return event.getId();
} );
doInJPA( this::entityManagerFactory, entityManager -> {
Event event = entityManager.find( Event.class, eventId );
assertEquals( "Temperature", event.getKey() );
assertEquals( "9", event.getValue() );
return event.getId();
} );
}
}

View File

@ -0,0 +1,58 @@
/*
* 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.identifier.composite;
import java.sql.Timestamp;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
/**
* @author Vlad Mihalcea
*/
public class EmbeddedIdInMemoryGeneratedValueTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Event.class };
}
@Test
@TestForIssue(jiraKey = "HHH-13096")
public void test() {
final EventId eventId = doInJPA( this::entityManagerFactory, entityManager -> {
//tag::identifiers-composite-generated-in-memory-example[]
EventId id = new EventId();
id.setCategory( 1 );
id.setCreatedOn( new Timestamp( System.currentTimeMillis() ) );
Event event = new Event();
event.setId( id );
event.setKey( "Temperature" );
event.setValue( "9" );
entityManager.persist( event );
//end::identifiers-composite-generated-in-memory-example[]
return event.getId();
} );
doInJPA( this::entityManagerFactory, entityManager -> {
Event event = entityManager.find( Event.class, eventId );
assertEquals( "Temperature", event.getKey() );
assertEquals( "9", event.getValue() );
return event.getId();
} );
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.identifier.composite;
import javax.persistence.Entity;
import javax.persistence.Id;
/**
* @author Vlad Mihalcea
*/
//tag::identifiers-composite-generated-mapping-example[]
@Entity
class Event {
@Id
private EventId id;
private String key;
private String value;
//Getters and setters are omitted for brevity
//end::identifiers-composite-generated-mapping-example[]
public EventId getId() {
return id;
}
public void setId(EventId id) {
this.id = id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
//tag::identifiers-composite-generated-mapping-example[]
}
//end::identifiers-composite-generated-mapping-example[]

View File

@ -0,0 +1,64 @@
/*
* 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.identifier.composite;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Objects;
import javax.persistence.Embeddable;
/**
* @author Vlad Mihalcea
*/
//tag::identifiers-composite-generated-mapping-example[]
@Embeddable
class EventId implements Serializable {
private Integer category;
private Timestamp createdOn;
//Getters and setters are omitted for brevity
//end::identifiers-composite-generated-mapping-example[]
public Integer getCategory() {
return category;
}
public void setCategory(Integer category) {
this.category = category;
}
public Timestamp getCreatedOn() {
return createdOn;
}
public void setCreatedOn(Timestamp createdOn) {
this.createdOn = createdOn;
}
//tag::identifiers-composite-generated-mapping-example[]
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
EventId that = (EventId) o;
return Objects.equals( category, that.category ) &&
Objects.equals( createdOn, that.createdOn );
}
@Override
public int hashCode() {
return Objects.hash( category, createdOn );
}
}
//end::identifiers-composite-generated-mapping-example[]