HHH-15722 @OneToMany mappedBy with a @Any
This commit is contained in:
parent
20d26a0126
commit
4308c5dac5
|
@ -15,7 +15,6 @@ import java.util.Map;
|
|||
import java.util.Properties;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.FetchMode;
|
||||
|
@ -131,6 +130,7 @@ import jakarta.persistence.CollectionTable;
|
|||
import jakarta.persistence.ConstraintMode;
|
||||
import jakarta.persistence.ElementCollection;
|
||||
import jakarta.persistence.Embedded;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.Index;
|
||||
|
@ -163,6 +163,7 @@ import static org.hibernate.boot.model.internal.AnnotatedJoinColumns.buildJoinTa
|
|||
import static org.hibernate.boot.model.internal.BinderHelper.buildAnyValue;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.checkMappedByType;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.createSyntheticPropertyReference;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.extractFromPackage;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.getCascadeStrategy;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.getFetchMode;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.getOverridableAnnotation;
|
||||
|
@ -175,7 +176,6 @@ import static org.hibernate.boot.model.internal.EmbeddableBinder.fillEmbeddable;
|
|||
import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators;
|
||||
import static org.hibernate.boot.model.internal.HCANNHelper.findAnnotation;
|
||||
import static org.hibernate.boot.model.internal.PropertyHolderBuilder.buildPropertyHolder;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.extractFromPackage;
|
||||
import static org.hibernate.boot.model.source.internal.hbm.ModelBinder.useEntityWhereClauseForCollections;
|
||||
import static org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle.fromResultCheckStyle;
|
||||
import static org.hibernate.internal.util.ReflectHelper.getDefaultSupplier;
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.hibernate.boot.spi.InFlightMetadataCollector;
|
|||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.mapping.Any;
|
||||
import org.hibernate.mapping.Collection;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.Component;
|
||||
|
@ -798,6 +799,9 @@ public class TableBinder {
|
|||
}
|
||||
return element.getColumns();
|
||||
}
|
||||
else if (value instanceof Any) {
|
||||
return ( (Any) value ).getKeyDescriptor().getColumns();
|
||||
}
|
||||
else {
|
||||
return value.getColumns();
|
||||
}
|
||||
|
|
|
@ -109,6 +109,10 @@ public class Any extends SimpleValue {
|
|||
return discriminatorDescriptor;
|
||||
}
|
||||
|
||||
public BasicValue getKeyDescriptor() {
|
||||
return keyDescriptor;
|
||||
}
|
||||
|
||||
public MetaValue getMetaMapping() {
|
||||
return metaMapping;
|
||||
}
|
||||
|
|
|
@ -765,7 +765,7 @@ public class ToOneAttributeMapping
|
|||
targetKeyPropertyNames.add( prefix );
|
||||
}
|
||||
if ( type.isComponentType() ) {
|
||||
final ComponentType componentType = (ComponentType) type;
|
||||
final CompositeType componentType = (CompositeType) type;
|
||||
final String[] propertyNames = componentType.getPropertyNames();
|
||||
final Type[] componentTypeSubtypes = componentType.getSubtypes();
|
||||
for ( int i = 0, propertyNamesLength = propertyNames.length; i < propertyNamesLength; i++ ) {
|
||||
|
|
|
@ -65,11 +65,15 @@ import org.hibernate.loader.ast.internal.CollectionLoaderSubSelectFetch;
|
|||
import org.hibernate.loader.ast.internal.LoaderSqlAstCreationState;
|
||||
import org.hibernate.loader.ast.spi.BatchLoaderFactory;
|
||||
import org.hibernate.loader.ast.spi.CollectionLoader;
|
||||
import org.hibernate.mapping.Any;
|
||||
import org.hibernate.mapping.BasicValue;
|
||||
import org.hibernate.mapping.Collection;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.Formula;
|
||||
import org.hibernate.mapping.IdentifierCollection;
|
||||
import org.hibernate.mapping.IndexedCollection;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.mapping.Selectable;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.mapping.Value;
|
||||
|
@ -128,13 +132,17 @@ import org.hibernate.sql.model.jdbc.JdbcDeleteMutation;
|
|||
import org.hibernate.sql.model.jdbc.JdbcMutationOperation;
|
||||
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
|
||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||
import org.hibernate.type.AnyType;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.CollectionType;
|
||||
import org.hibernate.type.CompositeType;
|
||||
import org.hibernate.type.EntityType;
|
||||
import org.hibernate.type.MetaType;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import static org.hibernate.internal.util.StringHelper.getNonEmptyOrConjunctionIfBothNonEmpty;
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
|
||||
import static org.hibernate.jdbc.Expectations.createExpectation;
|
||||
import static org.hibernate.sql.model.ModelMutationLogging.MODEL_MUTATION_LOGGER;
|
||||
|
@ -303,9 +311,37 @@ public abstract class AbstractCollectionPersister
|
|||
spaces[i] = tables.next();
|
||||
}
|
||||
|
||||
if ( StringHelper.isNotEmpty( collectionBootDescriptor.getWhere() ) ) {
|
||||
String where = collectionBootDescriptor.getWhere();
|
||||
/*
|
||||
* Add the predicate on the role in the WHERE clause before creating the SQL queries.
|
||||
*/
|
||||
if ( mappedByProperty != null && elementType.isEntityType() ) {
|
||||
final String entityName = ( (EntityType) elementType ).getAssociatedEntityName();
|
||||
final PersistentClass persistentClass = creationContext.getBootModel().getEntityBinding( entityName );
|
||||
final Property property = persistentClass.getRecursiveProperty( mappedByProperty );
|
||||
final Value propertyValue = property.getValue();
|
||||
if ( propertyValue instanceof Any ) {
|
||||
final Any any = (Any) propertyValue;
|
||||
final BasicValue discriminatorDescriptor = any.getDiscriminatorDescriptor();
|
||||
final AnyType anyType = any.getType();
|
||||
final MetaType metaType = (MetaType) anyType.getDiscriminatorType();
|
||||
final Object discriminatorValue = metaType.getEntityNameToDiscriminatorValueMap().get( ownerPersister.getEntityName() );
|
||||
final BasicType<Object> discriminatorBaseType = (BasicType<Object>) metaType.getBaseType();
|
||||
final String discriminatorLiteral = discriminatorBaseType.getJdbcLiteralFormatter().toJdbcLiteral(
|
||||
discriminatorValue,
|
||||
creationContext.getDialect(),
|
||||
creationContext.getSessionFactory().getWrapperOptions()
|
||||
);
|
||||
where = getNonEmptyOrConjunctionIfBothNonEmpty(
|
||||
where,
|
||||
discriminatorDescriptor.getColumn().getText() + "=" + discriminatorLiteral
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( StringHelper.isNotEmpty( where ) ) {
|
||||
hasWhere = true;
|
||||
sqlWhereString = "(" + collectionBootDescriptor.getWhere() + ")";
|
||||
sqlWhereString = "(" + where + ")";
|
||||
sqlWhereStringTemplate = Template.renderWhereStringTemplate(
|
||||
sqlWhereString,
|
||||
dialect,
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.annotations.Any;
|
||||
import org.hibernate.annotations.AnyDiscriminatorValue;
|
||||
import org.hibernate.annotations.AnyKeyJavaClass;
|
||||
import org.hibernate.cfg.JdbcSettings;
|
||||
|
||||
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.JiraKey;
|
||||
import org.hibernate.testing.orm.junit.Jpa;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
/**
|
||||
* Allows testing a @ManyToOne mappedBy relationship with a @Any as the return variable.
|
||||
*
|
||||
* @author Vincent Bouthinon
|
||||
*/
|
||||
@Jpa(
|
||||
annotatedClasses = {
|
||||
ManyToOneWithAnyTest.Library.class,
|
||||
ManyToOneWithAnyTest.Book.class,
|
||||
ManyToOneWithAnyTest.Shop.class
|
||||
},
|
||||
integrationSettings = @Setting(name = JdbcSettings.SHOW_SQL, value = "true")
|
||||
)
|
||||
@JiraKey("HHH-15722")
|
||||
class ManyToOneWithAnyTest {
|
||||
|
||||
@Test
|
||||
void testMappingManyToOneMappedByAny(EntityManagerFactoryScope scope) {
|
||||
|
||||
scope.inTransaction(
|
||||
entityManager -> {
|
||||
Library library = new Library();
|
||||
Book firstBook = new Book();
|
||||
final Set<Book> books = Set.of( firstBook, new Book() );
|
||||
library.setBooks( books );
|
||||
|
||||
entityManager.persist( library );
|
||||
entityManager.flush();
|
||||
entityManager.clear();
|
||||
|
||||
firstBook = entityManager.unwrap( Session.class )
|
||||
.byId( firstBook.getClass() )
|
||||
.load( firstBook.getId() );
|
||||
|
||||
assertNotNull( firstBook );
|
||||
|
||||
entityManager.clear();
|
||||
|
||||
library = entityManager.unwrap( Session.class )
|
||||
.byId( library.getClass() )
|
||||
.load( library.getId() );
|
||||
|
||||
assertNotNull( library );
|
||||
assertEquals( 2, library.getBooks().size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithSameIdentifiantButSubTypeDifferent(EntityManagerFactoryScope scope) {
|
||||
|
||||
scope.inTransaction(
|
||||
entityManager -> {
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
Library library = new Library();
|
||||
library.setId( 1L );
|
||||
library.setBooks( Set.of( new Book(), new Book() ) );
|
||||
|
||||
Shop shop = new Shop();
|
||||
shop.setId( 1L );
|
||||
shop.setBooks( Set.of( new Book(), new Book(), new Book() ) );
|
||||
|
||||
session.save( library );
|
||||
session.save( shop );
|
||||
session.flush();
|
||||
session.clear();
|
||||
|
||||
library = session.byId( library.getClass() ).load( library.getId() );
|
||||
assertNotNull( library );
|
||||
assertEquals( 2, library.getBooks().size() );
|
||||
|
||||
shop = session.byId( shop.getClass() ).load( shop.getId() );
|
||||
assertNotNull( shop );
|
||||
assertEquals( 3, shop.getBooks().size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "book")
|
||||
@Table(name = "TBOOK")
|
||||
public static class Book {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
@Any
|
||||
@AnyKeyJavaClass(Long.class)
|
||||
@AnyDiscriminatorValue(entity = Shop.class, discriminator = "S")
|
||||
@AnyDiscriminatorValue(entity = Library.class, discriminator = "L")
|
||||
@Column(name = "STORE_ROLE")
|
||||
@JoinColumn(name = "STORE_ID")
|
||||
private Store store;
|
||||
|
||||
public void setId(final Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Store getStore() {
|
||||
return store;
|
||||
}
|
||||
|
||||
public void setStore(final Store store) {
|
||||
this.store = store;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "library")
|
||||
@Table(name = "TLIBRARY")
|
||||
public static class Library implements Store {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
@OneToMany(mappedBy = "store", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
private Set<Book> books;
|
||||
|
||||
public void setId(final Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Set<Book> getBooks() {
|
||||
return books;
|
||||
}
|
||||
|
||||
public void setBooks(final Set<Book> books) {
|
||||
books.forEach( book -> book.setStore( this ) );
|
||||
this.books = books;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "shop")
|
||||
@Table(name = "TSHOP")
|
||||
public static class Shop implements Store {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
@OneToMany(mappedBy = "store", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
private Set<Book> books;
|
||||
|
||||
public void setId(final Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Set<Book> getBooks() {
|
||||
return books;
|
||||
}
|
||||
|
||||
public void setBooks(final Set<Book> books) {
|
||||
books.forEach( book -> book.setStore( this ) );
|
||||
this.books = books;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Store {
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue