HHH-15393 - Improve write-paths to use mapping model
This commit is contained in:
parent
33ce6a3d79
commit
ee1788c3c3
|
@ -14,7 +14,7 @@ import org.hibernate.engine.spi.IdentifierValue;
|
|||
*
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public interface CompositeIdentifierMapping extends EntityIdentifierMapping {
|
||||
public interface CompositeIdentifierMapping extends EntityIdentifierMapping, EmbeddableValuedModelPart {
|
||||
|
||||
@Override
|
||||
default IdentifierValue getUnsavedStrategy() {
|
||||
|
|
|
@ -63,6 +63,27 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
|
|||
private final AssociationKey associationKey;
|
||||
private final boolean hasConstraint;
|
||||
|
||||
public EmbeddedForeignKeyDescriptor(
|
||||
String keyTable,
|
||||
SelectableMappings keySelectableMappings,
|
||||
EmbeddableValuedModelPart keyMappingType,
|
||||
String targetTable,
|
||||
SelectableMappings targetSelectableMappings,
|
||||
EmbeddableValuedModelPart targetMappingType,
|
||||
boolean hasConstraint,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
this(
|
||||
keyMappingType,
|
||||
targetMappingType,
|
||||
keyTable,
|
||||
keySelectableMappings,
|
||||
targetTable,
|
||||
targetSelectableMappings,
|
||||
hasConstraint,
|
||||
creationProcess
|
||||
);
|
||||
}
|
||||
|
||||
public EmbeddedForeignKeyDescriptor(
|
||||
EmbeddableValuedModelPart keyMappingType,
|
||||
EmbeddableValuedModelPart targetMappingType,
|
||||
|
|
|
@ -9,7 +9,6 @@ package org.hibernate.metamodel.mapping.internal;
|
|||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.annotations.NotFoundAction;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
@ -24,6 +23,7 @@ import org.hibernate.mapping.Value;
|
|||
import org.hibernate.metamodel.mapping.AssociationKey;
|
||||
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
|
@ -34,6 +34,7 @@ import org.hibernate.metamodel.mapping.ModelPartContainer;
|
|||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||
import org.hibernate.metamodel.mapping.SelectableMappings;
|
||||
import org.hibernate.metamodel.mapping.VirtualModelPart;
|
||||
import org.hibernate.persister.collection.BasicCollectionPersister;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
|
@ -53,6 +54,8 @@ import org.hibernate.sql.ast.tree.predicate.Predicate;
|
|||
import org.hibernate.type.EntityType;
|
||||
|
||||
import static java.util.Objects.requireNonNullElse;
|
||||
import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.createInverseModelPart;
|
||||
import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.getPropertyOrder;
|
||||
|
||||
/**
|
||||
* Entity-valued collection-part mapped through a join table. Models both <ul>
|
||||
|
@ -375,8 +378,12 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple
|
|||
|
||||
final String collectionTableName = ( (BasicCollectionPersister) collectionDescriptor ).getTableName();
|
||||
|
||||
foreignKey = createJoinTablePartForeignKey( collectionTableName, elementDescriptor, creationProcess );
|
||||
// this fk will refer to the associated entity's id. if that id is not ready yet, delay this creation
|
||||
if ( getAssociatedEntityMappingType().getIdentifierMapping() == null ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreignKey = createJoinTablePartForeignKey( collectionTableName, elementDescriptor, creationProcess );
|
||||
creationProcess.registerForeignKey( this, foreignKey );
|
||||
}
|
||||
else {
|
||||
|
@ -421,21 +428,28 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple
|
|||
|
||||
private ForeignKeyDescriptor createJoinTablePartForeignKey(
|
||||
String collectionTableName,
|
||||
ManyToOne elementDescriptor,
|
||||
ManyToOne elementBootDescriptor,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
final EntityIdentifierMapping identifierMapping = getAssociatedEntityMappingType().getIdentifierMapping();
|
||||
if ( identifierMapping.getNature() == EntityIdentifierMapping.Nature.SIMPLE ) {
|
||||
final BasicEntityIdentifierMapping basicIdMapping = (BasicEntityIdentifierMapping) identifierMapping;
|
||||
final EntityMappingType associatedEntityMapping = getAssociatedEntityMappingType();
|
||||
final EntityIdentifierMapping associatedIdMapping = associatedEntityMapping.getIdentifierMapping();
|
||||
assert associatedIdMapping != null;
|
||||
|
||||
assert elementDescriptor.getColumns().size() == 1;
|
||||
final Column keyColumn = elementDescriptor.getColumns().get( 0 );
|
||||
// NOTE : `elementBootDescriptor` describes the key side of the fk
|
||||
// NOTE : `associatedIdMapping` is the target side model-part
|
||||
|
||||
// collectionTableName.keyColumnName -> targetTableName.targetColumnName
|
||||
// we have the fk target model-part and selectables via the associated entity's id mapping
|
||||
// and need to create the inverse (key) selectable-mappings and composite model-part
|
||||
|
||||
if ( associatedIdMapping.getNature() == EntityIdentifierMapping.Nature.SIMPLE ) {
|
||||
final BasicEntityIdentifierMapping targetModelPart = (BasicEntityIdentifierMapping) associatedIdMapping;
|
||||
|
||||
assert elementBootDescriptor.getColumns().size() == 1;
|
||||
final Column keyColumn = elementBootDescriptor.getColumns().get( 0 );
|
||||
|
||||
final SelectableMapping keySelectableMapping = SelectableMappingImpl.from(
|
||||
collectionTableName,
|
||||
keyColumn,
|
||||
basicIdMapping.getJdbcMapping(),
|
||||
targetModelPart.getJdbcMapping(),
|
||||
creationProcess.getCreationContext().getTypeConfiguration(),
|
||||
true,
|
||||
false,
|
||||
|
@ -444,9 +458,9 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple
|
|||
);
|
||||
|
||||
final BasicAttributeMapping keyModelPart = BasicAttributeMapping.withSelectableMapping(
|
||||
getAssociatedEntityMappingType(),
|
||||
basicIdMapping,
|
||||
basicIdMapping.getPropertyAccess(),
|
||||
associatedEntityMapping,
|
||||
targetModelPart,
|
||||
targetModelPart.getPropertyAccess(),
|
||||
true,
|
||||
false,
|
||||
keySelectableMapping
|
||||
|
@ -456,17 +470,45 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple
|
|||
// the key
|
||||
keyModelPart,
|
||||
// the target
|
||||
basicIdMapping,
|
||||
targetModelPart,
|
||||
// refers to primary key
|
||||
true,
|
||||
// has a constraint
|
||||
true,
|
||||
!elementBootDescriptor.isNullable(),
|
||||
// do not swap the sides
|
||||
false
|
||||
);
|
||||
}
|
||||
else {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
final CompositeIdentifierMapping targetModelPart = (CompositeIdentifierMapping) associatedIdMapping;
|
||||
|
||||
final SelectableMappings keySelectableMappings = SelectableMappingsImpl.from(
|
||||
collectionTableName,
|
||||
elementBootDescriptor,
|
||||
getPropertyOrder( elementBootDescriptor, creationProcess ),
|
||||
creationProcess.getCreationContext().getSessionFactory(),
|
||||
creationProcess.getCreationContext().getTypeConfiguration(),
|
||||
elementBootDescriptor.getColumnInsertability(),
|
||||
elementBootDescriptor.getColumnUpdateability(),
|
||||
creationProcess.getCreationContext().getSessionFactory().getJdbcServices().getDialect(),
|
||||
creationProcess.getSqmFunctionRegistry()
|
||||
);
|
||||
|
||||
return new EmbeddedForeignKeyDescriptor(
|
||||
collectionTableName,
|
||||
keySelectableMappings,
|
||||
createInverseModelPart(
|
||||
targetModelPart,
|
||||
associatedEntityMapping,
|
||||
this,
|
||||
keySelectableMappings,
|
||||
creationProcess
|
||||
),
|
||||
targetModelPart.getContainingTableExpression(),
|
||||
targetModelPart.getPartMappingType(),
|
||||
targetModelPart,
|
||||
!elementBootDescriptor.isNullable(),
|
||||
creationProcess
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1225,7 +1225,7 @@ public class MappingModelCreationHelper {
|
|||
}
|
||||
}
|
||||
|
||||
private static int[] getPropertyOrder(Value bootValueMapping, MappingModelCreationProcess creationProcess) {
|
||||
public static int[] getPropertyOrder(Value bootValueMapping, MappingModelCreationProcess creationProcess) {
|
||||
final ComponentType componentType;
|
||||
final boolean sorted;
|
||||
if ( bootValueMapping instanceof Collection ) {
|
||||
|
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* 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.manytoone.jointable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.EmbeddedForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.internal.ManyToManyCollectionPart;
|
||||
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
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.Test;
|
||||
|
||||
import jakarta.persistence.Basic;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Asserts the runtime model descriptors related to the inverse side
|
||||
* of a many-to-one with join-table.
|
||||
* <p/>
|
||||
* This tests simple keys. See {@link InverseManyToOneJoinTableSimpleIdTest} for
|
||||
* simple id testing
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@DomainModel( annotatedClasses = {
|
||||
InverseManyToOneJoinTableCompositeIdTest.Book.class,
|
||||
InverseManyToOneJoinTableCompositeIdTest.Author.class
|
||||
} )
|
||||
@SessionFactory
|
||||
public class InverseManyToOneJoinTableCompositeIdTest {
|
||||
@Test
|
||||
public void assertModel(SessionFactoryScope scope) {
|
||||
final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getMappingMetamodel();
|
||||
|
||||
final EntityPersister entityDescriptor = mappingMetamodel.getEntityDescriptor( Author.class );
|
||||
final PluralAttributeMapping books = (PluralAttributeMapping) entityDescriptor.findAttributeMapping( "books" );
|
||||
final ManyToManyCollectionPart booksElementDescriptor = (ManyToManyCollectionPart) books.getElementDescriptor();
|
||||
final EmbeddedForeignKeyDescriptor booksFk = (EmbeddedForeignKeyDescriptor) booksElementDescriptor.getForeignKeyDescriptor();
|
||||
|
||||
assertThat( booksFk.getKeyTable() ).isEqualTo( "book_authors" );
|
||||
booksFk.getKeyPart().forEachSelectable( (selectionIndex, selectableMapping) -> {
|
||||
final String expectedColumnName;
|
||||
if ( selectionIndex == 0 ) {
|
||||
expectedColumnName = "book_int_key";
|
||||
}
|
||||
else {
|
||||
assert selectionIndex == 1;
|
||||
expectedColumnName = "book_char_key";
|
||||
}
|
||||
assertThat( selectableMapping.getSelectionExpression() ).isEqualTo( expectedColumnName );
|
||||
} );
|
||||
|
||||
assertThat( booksFk.getTargetTable() ).isEqualTo( "books" );
|
||||
booksFk.getTargetPart().forEachSelectable( (selectionIndex, selectableMapping) -> {
|
||||
final String expectedColumnName;
|
||||
if ( selectionIndex == 0 ) {
|
||||
expectedColumnName = "int_key";
|
||||
}
|
||||
else {
|
||||
assert selectionIndex == 1;
|
||||
expectedColumnName = "char_key";
|
||||
}
|
||||
assertThat( selectableMapping.getSelectionExpression() ).isEqualTo( expectedColumnName );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usageSmokeTest(SessionFactoryScope scope) {
|
||||
createTestData( scope );
|
||||
|
||||
try {
|
||||
scope.inTransaction( (session) -> {
|
||||
final Author stephenKing = session.get( Author.class, 1 );
|
||||
verifyStephenKingBooks( stephenKing );
|
||||
} );
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final Author stephenKing = session
|
||||
.createSelectionQuery( "from Author a join fetch a.books where a.id = 1", Author.class )
|
||||
.getSingleResult();
|
||||
verifyStephenKingBooks( stephenKing );
|
||||
} );
|
||||
}
|
||||
finally {
|
||||
dropTestData( scope );
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyStephenKingBooks(Author author) {
|
||||
final List<String> bookNames = author.books.stream().map( Book::getName ).collect( Collectors.toList() );
|
||||
assertThat( bookNames ).contains( "It", "The Shining" );
|
||||
}
|
||||
|
||||
private void createTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
final Author stephenKing = new Author( 1, "Stephen King" );
|
||||
final Author johnMilton = new Author( 2, "John Milton" );
|
||||
session.persist( stephenKing );
|
||||
session.persist( johnMilton );
|
||||
|
||||
session.persist( new Book( new BookPk( 1, "king" ), "It", stephenKing ) );
|
||||
session.persist( new Book( new BookPk( 2, "king" ), "The Shining", stephenKing ) );
|
||||
|
||||
session.persist( new Book( new BookPk( 1, "milton" ), "Paradise Lost", johnMilton ) );
|
||||
} );
|
||||
}
|
||||
|
||||
private void dropTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
session.createQuery( "from Author", Author.class ).list().forEach( session::remove );
|
||||
} );
|
||||
scope.inTransaction( (session) -> {
|
||||
final Long bookCount = session.createSelectionQuery( "select count(1) from Book", Long.class ).uniqueResult();
|
||||
assertThat( bookCount ).isEqualTo( 0L );
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity( name = "Book" )
|
||||
@Table( name = "books" )
|
||||
public static class Book {
|
||||
@EmbeddedId
|
||||
private BookPk id;
|
||||
|
||||
@Basic
|
||||
private String name;
|
||||
|
||||
@ManyToOne
|
||||
@JoinTable(
|
||||
name = "book_authors",
|
||||
joinColumns = {
|
||||
@JoinColumn(name = "book_int_key", referencedColumnName = "int_key"),
|
||||
@JoinColumn(name = "book_char_key", referencedColumnName = "char_key")
|
||||
},
|
||||
inverseJoinColumns = @JoinColumn(name="author_id",nullable = false)
|
||||
)
|
||||
private Author author;
|
||||
|
||||
private Book() {
|
||||
// for use by Hibernate
|
||||
}
|
||||
|
||||
public Book(BookPk id, String name, Author author) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public BookPk getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Author getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(Author author) {
|
||||
this.author = author;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class BookPk {
|
||||
@Column( name="int_key" )
|
||||
private Integer key1;
|
||||
@Column( name="char_key" )
|
||||
private String key2;
|
||||
|
||||
private BookPk() {
|
||||
// for Hibernate
|
||||
}
|
||||
|
||||
public BookPk(Integer key1, String key2) {
|
||||
this.key1 = key1;
|
||||
this.key2 = key2;
|
||||
}
|
||||
|
||||
public Integer getKey1() {
|
||||
return key1;
|
||||
}
|
||||
|
||||
public String getKey2() {
|
||||
return key2;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "Author" )
|
||||
@Table( name = "authors" )
|
||||
public static class Author {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
@Basic
|
||||
private String name;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "author")
|
||||
private List<Book> books;
|
||||
|
||||
private Author() {
|
||||
// for use by Hibernate
|
||||
}
|
||||
|
||||
public Author(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;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class AuthorPk {
|
||||
@Column(name = "first_name")
|
||||
private String firstName;
|
||||
@Column(name = "last_name")
|
||||
private String lastName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* 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.manytoone.jointable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.ManyToManyCollectionPart;
|
||||
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
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.Test;
|
||||
|
||||
import jakarta.persistence.Basic;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Asserts the runtime model descriptors related to the inverse side
|
||||
* of a many-to-one with join-table.
|
||||
* <p/>
|
||||
* This tests simple keys. See {@link InverseManyToOneJoinTableCompositeIdTest} for
|
||||
* composite id testing
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@DomainModel( annotatedClasses = { InverseManyToOneJoinTableSimpleIdTest.Book.class, InverseManyToOneJoinTableSimpleIdTest.Author.class } )
|
||||
@SessionFactory
|
||||
public class InverseManyToOneJoinTableSimpleIdTest {
|
||||
@Test
|
||||
public void assertModel(SessionFactoryScope scope) {
|
||||
final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getMappingMetamodel();
|
||||
|
||||
final EntityPersister entityDescriptor = mappingMetamodel.getEntityDescriptor( Author.class );
|
||||
final PluralAttributeMapping books = (PluralAttributeMapping) entityDescriptor.findAttributeMapping( "books" );
|
||||
final ManyToManyCollectionPart booksElementDescriptor = (ManyToManyCollectionPart) books.getElementDescriptor();
|
||||
final SimpleForeignKeyDescriptor booksFk = (SimpleForeignKeyDescriptor) booksElementDescriptor.getForeignKeyDescriptor();
|
||||
|
||||
assertThat( booksFk.getKeyTable() ).isEqualTo( "book_authors" );
|
||||
assertThat( booksFk.getKeyPart().getSelectionExpression() ).isEqualTo( "book_id" );
|
||||
|
||||
assertThat( booksFk.getTargetTable() ).isEqualTo( "books" );
|
||||
assertThat( booksFk.getTargetPart().getSelectionExpression() ).isEqualTo( "id" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usageSmokeTest(SessionFactoryScope scope) {
|
||||
createTestData( scope );
|
||||
|
||||
try {
|
||||
scope.inTransaction( (session) -> {
|
||||
final Author stephenKing = session.get( Author.class, 1 );
|
||||
verifyStephenKingBooks( stephenKing );
|
||||
} );
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final Author stephenKing = session
|
||||
.createSelectionQuery( "from Author a join fetch a.books where a.id = 1", Author.class )
|
||||
.getSingleResult();
|
||||
verifyStephenKingBooks( stephenKing );
|
||||
} );
|
||||
}
|
||||
finally {
|
||||
dropTestData( scope );
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyStephenKingBooks(Author author) {
|
||||
final List<String> bookNames = author.books.stream().map( Book::getName ).collect( Collectors.toList() );
|
||||
assertThat( bookNames ).contains( "It", "The Shining" );
|
||||
}
|
||||
|
||||
private void createTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
final Author stephenKing = new Author( 1, "Stephen King" );
|
||||
final Author johnMilton = new Author( 2, "John Milton" );
|
||||
session.persist( stephenKing );
|
||||
session.persist( johnMilton );
|
||||
|
||||
session.persist( new Book( 1, "It", stephenKing ) );
|
||||
session.persist( new Book( 2, "The Shining", stephenKing ) );
|
||||
|
||||
session.persist( new Book( 3, "Paradise Lost", johnMilton ) );
|
||||
} );
|
||||
}
|
||||
|
||||
private void dropTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
session.createQuery( "from Author", Author.class ).list().forEach( session::remove );
|
||||
} );
|
||||
scope.inTransaction( (session) -> {
|
||||
final Long bookCount = session.createSelectionQuery( "select count(1) from Book", Long.class ).uniqueResult();
|
||||
assertThat( bookCount ).isEqualTo( 0L );
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity( name = "Book" )
|
||||
@Table( name = "books" )
|
||||
public static class Book {
|
||||
@Id
|
||||
private Integer id;
|
||||
@Basic
|
||||
private String name;
|
||||
@ManyToOne
|
||||
@JoinTable(name = "book_authors",
|
||||
joinColumns = @JoinColumn(name = "book_id"),
|
||||
inverseJoinColumns = @JoinColumn(name="author_id",nullable = false))
|
||||
private Author author;
|
||||
|
||||
private Book() {
|
||||
// for use by Hibernate
|
||||
}
|
||||
|
||||
public Book(Integer id, String name, Author author) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "Author" )
|
||||
@Table( name = "authors" )
|
||||
public static class Author {
|
||||
@Id
|
||||
private Integer id;
|
||||
@Basic
|
||||
private String name;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "author")
|
||||
private List<Book> books;
|
||||
|
||||
private Author() {
|
||||
// for use by Hibernate
|
||||
}
|
||||
|
||||
public Author(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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,10 @@
|
|||
/*
|
||||
* 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
|
||||
* 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.manytoone;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.Table;
|
||||
package org.hibernate.orm.test.mapping.manytoone.jointable;
|
||||
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
|
@ -25,6 +17,14 @@ import org.hibernate.testing.orm.junit.SessionFactory;
|
|||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
Loading…
Reference in New Issue