Introduce `VirtualIdEmbeddable` and `IdClassEmbeddable` + instantiators
Prep work for EmbeddableInstantiator - initializer Still need to - integrate EmbeddableInstantiator work - integrate embedded forms. `VirtualIdEmbeddable` does not really need it as it can use the id-mapping itself as the embedded form. But `IdClassEmbedded` should really be integrated - integrate `VirtualKeyEmbeddable` and `VirtualKeyEmbedded` for use as inverse composite fks - share `#finishInit` handling for `EmbeddableMappingType`, `VirtualIdEmbeddable` and `IdClassEmbeddable` - ability to use the containing composite owner as the parent of a composite (legacy behavior is to always use the "first" entity
This commit is contained in:
parent
01c6b144ce
commit
29ed0a0566
|
@ -48,7 +48,7 @@ public final class EntityKey implements Serializable {
|
||||||
public EntityKey(Object id, EntityPersister persister) {
|
public EntityKey(Object id, EntityPersister persister) {
|
||||||
this.persister = persister;
|
this.persister = persister;
|
||||||
if ( id == null ) {
|
if ( id == null ) {
|
||||||
throw new AssertionFailure( "null identifier" );
|
throw new AssertionFailure( "null identifier (" + persister.getEntityName() + ")" );
|
||||||
}
|
}
|
||||||
this.identifier = id;
|
this.identifier = id;
|
||||||
this.hashCode = generateHashCode();
|
this.hashCode = generateHashCode();
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.metamodel.mapping;
|
package org.hibernate.metamodel.mapping;
|
||||||
|
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.property.access.spi.PropertyAccess;
|
import org.hibernate.property.access.spi.PropertyAccess;
|
||||||
import org.hibernate.sql.results.graph.Fetchable;
|
import org.hibernate.sql.results.graph.Fetchable;
|
||||||
|
@ -39,11 +40,22 @@ public interface AttributeMapping extends ModelPart, ValueMapping, Fetchable, Pr
|
||||||
* Convenient access to getting the value for this attribute from the "owner"
|
* Convenient access to getting the value for this attribute from the "owner"
|
||||||
*/
|
*/
|
||||||
default Object getValue(Object container, SharedSessionContractImplementor session) {
|
default Object getValue(Object container, SharedSessionContractImplementor session) {
|
||||||
|
return getValue( container, session.getSessionFactory() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenient access to getting the value for this attribute from the "owner"
|
||||||
|
*/
|
||||||
|
default Object getValue(Object container, SessionFactoryImplementor sessionFactory) {
|
||||||
return getPropertyAccess().getGetter().get( container );
|
return getPropertyAccess().getGetter().get( container );
|
||||||
}
|
}
|
||||||
|
|
||||||
default void setValue(Object container, Object value, SharedSessionContractImplementor session) {
|
default void setValue(Object container, Object value, SharedSessionContractImplementor session) {
|
||||||
getPropertyAccess().getSetter().set( container, value, session.getSessionFactory() );
|
setValue( container, value, session.getSessionFactory() );
|
||||||
|
}
|
||||||
|
|
||||||
|
default void setValue(Object container, Object value, SessionFactoryImplementor sessionFactory) {
|
||||||
|
getPropertyAccess().getSetter().set( container, value, sessionFactory );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -811,12 +811,8 @@ public class EmbeddableMappingTypeImpl implements EmbeddableMappingType, Selecta
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( int i = 0; i < attributeMappings.size(); i++ ) {
|
for ( int i = 0; i < attributeMappings.size(); i++ ) {
|
||||||
attributeMappings.get( i )
|
final AttributeMapping attributeMapping = attributeMappings.get( i );
|
||||||
.getAttributeMetadataAccess()
|
attributeMapping.setValue( compositeInstance, resolvedValues[i], sessionFactory );
|
||||||
.resolveAttributeMetadata( null )
|
|
||||||
.getPropertyAccess()
|
|
||||||
.getSetter()
|
|
||||||
.set( compositeInstance, resolvedValues[i], sessionFactory );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,12 +29,14 @@ import org.hibernate.sql.results.graph.Fetch;
|
||||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||||
import org.hibernate.sql.results.graph.Initializer;
|
import org.hibernate.sql.results.graph.Initializer;
|
||||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||||
import org.hibernate.sql.results.graph.entity.AbstractEntityInitializer;
|
|
||||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||||
import org.hibernate.sql.results.internal.NullValueAssembler;
|
import org.hibernate.sql.results.internal.NullValueAssembler;
|
||||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||||
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.java.spi.EntityJavaTypeDescriptor;
|
import org.hibernate.type.descriptor.java.spi.EntityJavaTypeDescriptor;
|
||||||
|
|
||||||
|
import static java.lang.Boolean.FALSE;
|
||||||
|
import static java.lang.Boolean.TRUE;
|
||||||
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
|
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,6 +87,10 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
||||||
this.resolvedValues = new Object[ numOfAttrs ];
|
this.resolvedValues = new Object[ numOfAttrs ];
|
||||||
this.assemblers = arrayList( numOfAttrs );
|
this.assemblers = arrayList( numOfAttrs );
|
||||||
|
|
||||||
|
// todo (6.0) - why not just use the "maps id" form right here?
|
||||||
|
// ( (CompositeIdentifierMapping) embedded ).getMappedIdEmbeddableTypeDescriptor()
|
||||||
|
// in theory this should let us avoid the explicit maps-id handling via `setPropertyValuesOnTarget`
|
||||||
|
|
||||||
embeddableTypeDescriptor.visitFetchables(
|
embeddableTypeDescriptor.visitFetchables(
|
||||||
stateArrayContributor -> {
|
stateArrayContributor -> {
|
||||||
final Fetch fetch = resultDescriptor.findFetch( stateArrayContributor );
|
final Fetch fetch = resultDescriptor.findFetch( stateArrayContributor );
|
||||||
|
@ -126,12 +132,21 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resolveKey(RowProcessingState rowProcessingState) {
|
public FetchParentAccess findFirstEntityDescriptorAccess() {
|
||||||
|
return getFetchParentAccess().findFirstEntityDescriptorAccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resolveKey(RowProcessingState processingState) {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
public void resolveInstance(RowProcessingState processingState) {
|
||||||
|
reallyResolve( processingState );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reallyResolve(RowProcessingState processingState) {
|
||||||
if ( compositeInstance != null ) {
|
if ( compositeInstance != null ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -140,14 +155,25 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
||||||
// which we access through the fetch parent access.
|
// which we access through the fetch parent access.
|
||||||
// If this model part is an identifier, we must construct the instance as this is called during resolveKey
|
// If this model part is an identifier, we must construct the instance as this is called during resolveKey
|
||||||
final EmbeddableMappingType embeddableTypeDescriptor = embedded.getEmbeddableTypeDescriptor();
|
final EmbeddableMappingType embeddableTypeDescriptor = embedded.getEmbeddableTypeDescriptor();
|
||||||
|
final JavaType<?> embeddableJtd = embeddableTypeDescriptor.getMappedJavaTypeDescriptor();
|
||||||
|
|
||||||
if ( fetchParentAccess != null && embeddableTypeDescriptor.getMappedJavaTypeDescriptor().getJavaTypeClass()
|
if ( fetchParentAccess != null &&
|
||||||
.isAssignableFrom( fetchParentAccess.getInitializedPart().getJavaTypeDescriptor().getJavaTypeClass() )
|
embeddableJtd.getJavaTypeClass().isAssignableFrom( fetchParentAccess.getInitializedPart().getJavaTypeDescriptor().getJavaTypeClass() )
|
||||||
&& embeddableTypeDescriptor.getMappedJavaTypeDescriptor() instanceof EntityJavaTypeDescriptor<?>
|
&& embeddableJtd instanceof EntityJavaTypeDescriptor<?>
|
||||||
&& !( embedded instanceof CompositeIdentifierMapping )
|
&& !( embedded instanceof CompositeIdentifierMapping )
|
||||||
&& !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embedded.getFetchableName() ) ) {
|
&& !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embedded.getFetchableName() ) ) {
|
||||||
fetchParentAccess.resolveInstance( rowProcessingState );
|
EmbeddableLoadingLogger.INSTANCE.debugf(
|
||||||
|
"Linking composite instance to fetch-parent [%s] - %s",
|
||||||
|
navigablePath,
|
||||||
|
fetchParentAccess
|
||||||
|
);
|
||||||
|
fetchParentAccess.resolveInstance( processingState );
|
||||||
compositeInstance = fetchParentAccess.getInitializedInstance();
|
compositeInstance = fetchParentAccess.getInitializedInstance();
|
||||||
|
EmbeddableLoadingLogger.INSTANCE.debugf(
|
||||||
|
"Done linking composite instance to fetch-parent [%s] - %s",
|
||||||
|
navigablePath,
|
||||||
|
compositeInstance
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( compositeInstance == null ) {
|
if ( compositeInstance == null ) {
|
||||||
|
@ -164,37 +190,24 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initializeInstance(RowProcessingState processingState) {
|
public void initializeInstance(RowProcessingState processingState) {
|
||||||
final Initializer initializer = processingState.resolveInitializer( navigablePath.getParent() );
|
|
||||||
|
|
||||||
handleParentInjection( processingState );
|
|
||||||
|
|
||||||
EmbeddableLoadingLogger.INSTANCE.debugf(
|
EmbeddableLoadingLogger.INSTANCE.debugf(
|
||||||
"Initializing composite instance [%s]",
|
"Initializing composite instance [%s]",
|
||||||
navigablePath
|
navigablePath
|
||||||
);
|
);
|
||||||
|
|
||||||
boolean areAllValuesNull = true;
|
handleParentInjection( processingState );
|
||||||
for ( int i = 0; i < assemblers.size(); i++ ) {
|
|
||||||
final DomainResultAssembler<?> assembler = assemblers.get( i );
|
|
||||||
final Object contributorValue = assembler.assemble(
|
|
||||||
processingState,
|
|
||||||
processingState.getJdbcValuesSourceProcessingState().getProcessingOptions()
|
|
||||||
);
|
|
||||||
|
|
||||||
resolvedValues[i] = contributorValue;
|
extractRowState( processingState );
|
||||||
if ( contributorValue != null ) {
|
|
||||||
areAllValuesNull = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !createEmptyCompositesEnabled && areAllValuesNull ) {
|
if ( !createEmptyCompositesEnabled && allValuesNull == TRUE ) {
|
||||||
compositeInstance = null;
|
compositeInstance = null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
notifyResolutionListeners( compositeInstance );
|
notifyResolutionListeners( compositeInstance );
|
||||||
if ( compositeInstance instanceof HibernateProxy ) {
|
if ( compositeInstance instanceof HibernateProxy ) {
|
||||||
if ( initializer != this ) {
|
final Initializer parentInitializer = processingState.resolveInitializer( navigablePath.getParent() );
|
||||||
( (AbstractEntityInitializer) initializer ).registerResolutionListener(
|
if ( parentInitializer != this ) {
|
||||||
|
( (FetchParentAccess) parentInitializer ).registerResolutionListener(
|
||||||
entity -> setPropertyValuesOnTarget( entity, processingState.getSession() )
|
entity -> setPropertyValuesOnTarget( entity, processingState.getSession() )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -214,11 +227,28 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
||||||
// A possible alternative could be to initialize the resolved values for primitive fields to their default value,
|
// A possible alternative could be to initialize the resolved values for primitive fields to their default value,
|
||||||
// but that might cause unexpected outcomes for Hibernate 5 users that use createEmptyCompositesEnabled when updating.
|
// but that might cause unexpected outcomes for Hibernate 5 users that use createEmptyCompositesEnabled when updating.
|
||||||
// You can see the need for this by running EmptyCompositeEquivalentToNullTest
|
// You can see the need for this by running EmptyCompositeEquivalentToNullTest
|
||||||
else if ( !areAllValuesNull ) {
|
else if ( allValuesNull == FALSE ) {
|
||||||
setPropertyValuesOnTarget( compositeInstance, processingState.getSession() );
|
setPropertyValuesOnTarget( compositeInstance, processingState.getSession() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void extractRowState(RowProcessingState processingState) {
|
||||||
|
allValuesNull = true;
|
||||||
|
for ( int i = 0; i < assemblers.size(); i++ ) {
|
||||||
|
final DomainResultAssembler<?> assembler = assemblers.get( i );
|
||||||
|
final Object contributorValue = assembler.assemble(
|
||||||
|
processingState,
|
||||||
|
processingState.getJdbcValuesSourceProcessingState().getProcessingOptions()
|
||||||
|
);
|
||||||
|
|
||||||
|
resolvedValues[i] = contributorValue;
|
||||||
|
if ( contributorValue != null ) {
|
||||||
|
allValuesNull = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void handleParentInjection(RowProcessingState processingState) {
|
private void handleParentInjection(RowProcessingState processingState) {
|
||||||
final PropertyAccess parentInjectionAccess = embedded.getParentInjectionAttributePropertyAccess();
|
final PropertyAccess parentInjectionAccess = embedded.getParentInjectionAttributePropertyAccess();
|
||||||
if ( parentInjectionAccess == null ) {
|
if ( parentInjectionAccess == null ) {
|
||||||
|
@ -229,8 +259,7 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
||||||
// todo (6.0) : should we initialize the composite instance if we get here and it is null (not NULL_MARKER)?
|
// todo (6.0) : should we initialize the composite instance if we get here and it is null (not NULL_MARKER)?
|
||||||
|
|
||||||
// we want to avoid injection for `NULL_MARKER`
|
// we want to avoid injection for `NULL_MARKER`
|
||||||
final Object compositeInstance = getCompositeInstance();
|
if ( compositeInstance == null || compositeInstance == NULL_MARKER ) {
|
||||||
if ( compositeInstance == null ) {
|
|
||||||
EmbeddableLoadingLogger.INSTANCE.debugf(
|
EmbeddableLoadingLogger.INSTANCE.debugf(
|
||||||
"Skipping parent injection for null embeddable [%s]",
|
"Skipping parent injection for null embeddable [%s]",
|
||||||
navigablePath
|
navigablePath
|
||||||
|
@ -342,11 +371,12 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
||||||
@Override
|
@Override
|
||||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||||
compositeInstance = null;
|
compositeInstance = null;
|
||||||
|
allValuesNull = null;
|
||||||
clearResolutionListeners();
|
clearResolutionListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FetchParentAccess findFirstEntityDescriptorAccess() {
|
public String toString() {
|
||||||
return getFetchParentAccess().findFirstEntityDescriptorAccess();
|
return getClass().getSimpleName() + "(" + navigablePath + ") : `" + getInitializedPart().getJavaTypeDescriptor().getJavaTypeClass() + "`";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* 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.orm.test.mapping.cid.idclass; /**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Basic;
|
||||||
|
|
||||||
|
@Entity(name = "Customer")
|
||||||
|
@Table(name = "customers")
|
||||||
|
public class Customer {
|
||||||
|
@Id
|
||||||
|
public Integer id;
|
||||||
|
@Basic
|
||||||
|
public String name;
|
||||||
|
|
||||||
|
protected Customer() {
|
||||||
|
// for Hibernate use
|
||||||
|
}
|
||||||
|
|
||||||
|
public Customer(Integer id, String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* 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.orm.test.mapping.cid.idclass;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
@DomainModel( annotatedClasses = {
|
||||||
|
Customer.class,
|
||||||
|
Order.class,
|
||||||
|
Payment.class
|
||||||
|
})
|
||||||
|
@SessionFactory
|
||||||
|
public class NestedIdClassTests {
|
||||||
|
@Test
|
||||||
|
public void smokeTest(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
final String qry = "from Payment p join fetch p.id.order o join fetch o.id.customer";
|
||||||
|
session.createQuery( qry ).list();
|
||||||
|
});
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
final String qry = "from Payment p join fetch p.order o join fetch o.customer";
|
||||||
|
session.createQuery( qry ).list();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void smokeTest2(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
final String qry = "from Payment p";
|
||||||
|
final Payment payment = session.createQuery( qry, Payment.class ).uniqueResult();
|
||||||
|
assertThat( payment ).isNotNull();
|
||||||
|
assertThat( payment.accountNumber ).isNotNull();
|
||||||
|
assertThat( payment.order ).isNotNull();
|
||||||
|
|
||||||
|
assertThat( payment.order.orderNumber ).isNotNull();
|
||||||
|
assertThat( payment.order.customer ).isNotNull();
|
||||||
|
|
||||||
|
assertThat( payment.order.customer.id ).isNotNull();
|
||||||
|
assertThat( payment.order.customer.name ).isNotNull();
|
||||||
|
});
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
final String qry = "from Payment p join fetch p.order o join fetch o.customer";
|
||||||
|
session.createQuery( qry ).list();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void smokeTest3(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
final Payment payment = session.get( Payment.class, new PaymentId( new OrderId( 1, 1 ), "123" ) );
|
||||||
|
assertThat( payment ).isNotNull();
|
||||||
|
assertThat( payment.accountNumber ).isNotNull();
|
||||||
|
assertThat( payment.order ).isNotNull();
|
||||||
|
|
||||||
|
assertThat( payment.order.orderNumber ).isNotNull();
|
||||||
|
assertThat( payment.order.customer ).isNotNull();
|
||||||
|
|
||||||
|
assertThat( payment.order.customer.id ).isNotNull();
|
||||||
|
assertThat( payment.order.customer.name ).isNotNull();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void createTestData(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
final Customer acme = new Customer( 1, "acme" );
|
||||||
|
final Customer spacely = new Customer( 2, "spacely" );
|
||||||
|
session.persist( acme );
|
||||||
|
session.persist( spacely );
|
||||||
|
|
||||||
|
final Order acmeOrder1 = new Order( acme, 1, 123F );
|
||||||
|
final Order acmeOrder2 = new Order( acme, 2, 123F );
|
||||||
|
session.persist( acmeOrder1 );
|
||||||
|
session.persist( acmeOrder2 );
|
||||||
|
|
||||||
|
final Payment payment = new Payment( acmeOrder1, "123" );
|
||||||
|
session.persist( payment );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void dropTestData(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
session.createQuery( "delete Payment" ).executeUpdate();
|
||||||
|
session.createQuery( "delete Order" ).executeUpdate();
|
||||||
|
session.createQuery( "delete Customer" ).executeUpdate();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* 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.orm.test.mapping.cid.idclass; /**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.IdClass;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
@Entity(name = "Order")
|
||||||
|
@Table(name = "orders")
|
||||||
|
@IdClass( OrderId.class )
|
||||||
|
public class Order {
|
||||||
|
@Id
|
||||||
|
@ManyToOne
|
||||||
|
public Customer customer;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
public Integer orderNumber;
|
||||||
|
|
||||||
|
public Float amount;
|
||||||
|
|
||||||
|
protected Order() {
|
||||||
|
// for Hibernate use
|
||||||
|
}
|
||||||
|
|
||||||
|
public Order(Customer customer, Integer orderNumber, Float amount) {
|
||||||
|
this.customer = customer;
|
||||||
|
this.orderNumber = orderNumber;
|
||||||
|
this.amount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Customer getCustomer() {
|
||||||
|
return customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomer(Customer customer) {
|
||||||
|
this.customer = customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getOrderNumber() {
|
||||||
|
return orderNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrderNumber(Integer orderNumber) {
|
||||||
|
this.orderNumber = orderNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float getAmount() {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAmount(Float amount) {
|
||||||
|
this.amount = amount;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* 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.orm.test.mapping.cid.idclass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class OrderId {
|
||||||
|
Integer customer;
|
||||||
|
Integer orderNumber;
|
||||||
|
|
||||||
|
public OrderId() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderId(Integer customer, Integer orderNumber) {
|
||||||
|
this.customer = customer;
|
||||||
|
this.orderNumber = orderNumber;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* 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.orm.test.mapping.cid.idclass; /**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
|
||||||
|
import jakarta.persistence.Basic;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.IdClass;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
@Entity(name = "Payment")
|
||||||
|
@Table(name = "payments")
|
||||||
|
@IdClass( PaymentId.class )
|
||||||
|
public class Payment {
|
||||||
|
@Id
|
||||||
|
@ManyToOne
|
||||||
|
public Order order;
|
||||||
|
|
||||||
|
@Basic
|
||||||
|
public String accountNumber;
|
||||||
|
|
||||||
|
protected Payment() {
|
||||||
|
// for Hibernate use
|
||||||
|
}
|
||||||
|
|
||||||
|
public Payment(Order order, String accountNumber) {
|
||||||
|
this.order = order;
|
||||||
|
this.accountNumber = accountNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Order getOrder() {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrder(Order order) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccountNumber() {
|
||||||
|
return accountNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccountNumber(String accountNumber) {
|
||||||
|
this.accountNumber = accountNumber;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* 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.orm.test.mapping.cid.idclass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class PaymentId {
|
||||||
|
private OrderId order;
|
||||||
|
private String accountNumber;
|
||||||
|
|
||||||
|
public PaymentId() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PaymentId(OrderId order, String accountNumber) {
|
||||||
|
this.order = order;
|
||||||
|
this.accountNumber = accountNumber;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue