HHH-15509 correctly support @NotFound @ManyToMany
1. error if a non-@ManyToMany collection is @NotFound 2. disable constraint generation for @NotFound @ManyToMany 3. allow lazy fetching for @NotFound @ManyToMany 4. rework a completely bogus test so it makes sense
This commit is contained in:
parent
5b907ae8b1
commit
7d34f86a95
|
@ -1771,8 +1771,8 @@ public final class AnnotationBinder {
|
|||
final JavaType<?> domainJtd = jtdRegistry.resolveDescriptor( domainJavaClass );
|
||||
final JavaType<?> relationalJtd = jtdRegistry.resolveDescriptor( relationalJavaClass );
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
final JpaAttributeConverterImpl valueConverter = new JpaAttributeConverterImpl(
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
final JpaAttributeConverterImpl<?,?> valueConverter = new JpaAttributeConverterImpl(
|
||||
bean,
|
||||
converterJtd,
|
||||
domainJtd,
|
||||
|
@ -2474,10 +2474,7 @@ public final class AnnotationBinder {
|
|||
&& isToManyAssociationWithinEmbeddableCollection(propertyHolder) ) {
|
||||
throw new AnnotationException(
|
||||
"@OneToMany, @ManyToMany or @ElementCollection cannot be used inside an @Embeddable that is also contained within an @ElementCollection: "
|
||||
+ BinderHelper.getPath(
|
||||
propertyHolder,
|
||||
inferredData
|
||||
)
|
||||
+ BinderHelper.getPath(propertyHolder, inferredData)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2485,10 +2482,7 @@ public final class AnnotationBinder {
|
|||
&& manyToManyAnn != null && !manyToManyAnn.mappedBy().isEmpty() ) {
|
||||
throw new AnnotationException(
|
||||
"Explicit @OrderColumn on inverse side of @ManyToMany is illegal: "
|
||||
+ BinderHelper.getPath(
|
||||
propertyHolder,
|
||||
inferredData
|
||||
)
|
||||
+ BinderHelper.getPath(propertyHolder, inferredData)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2519,7 +2513,13 @@ public final class AnnotationBinder {
|
|||
collectionBinder.setPropertyHolder(propertyHolder);
|
||||
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
|
||||
NotFound notFound = property.getAnnotation( NotFound.class );
|
||||
collectionBinder.setNotFoundAction( notFound == null ? null : notFound.action() );
|
||||
if ( notFound != null ) {
|
||||
if ( manyToManyAnn == null ) {
|
||||
throw new AnnotationException("collection annotated @NotFound is not a @ManyToMany association: "
|
||||
+ BinderHelper.getPath(propertyHolder, inferredData) );
|
||||
}
|
||||
collectionBinder.setNotFoundAction( notFound.action() );
|
||||
}
|
||||
collectionBinder.setCollectionType( inferredData.getProperty().getElementClass() );
|
||||
collectionBinder.setAccessType( inferredData.getDefaultAccess() );
|
||||
|
||||
|
@ -3022,7 +3022,7 @@ public final class AnnotationBinder {
|
|||
return null;
|
||||
}
|
||||
|
||||
protected static Class<? extends CompositeUserType<?>> resolveTimeZoneStorageCompositeUserType(
|
||||
static Class<? extends CompositeUserType<?>> resolveTimeZoneStorageCompositeUserType(
|
||||
XProperty property,
|
||||
XClass returnedClass,
|
||||
MetadataBuildingContext context) {
|
||||
|
|
|
@ -60,7 +60,6 @@ import org.hibernate.annotations.ListIndexJdbcType;
|
|||
import org.hibernate.annotations.ListIndexJdbcTypeCode;
|
||||
import org.hibernate.annotations.Loader;
|
||||
import org.hibernate.annotations.ManyToAny;
|
||||
import org.hibernate.annotations.NotFound;
|
||||
import org.hibernate.annotations.NotFoundAction;
|
||||
import org.hibernate.annotations.OnDelete;
|
||||
import org.hibernate.annotations.OnDeleteAction;
|
||||
|
@ -971,7 +970,6 @@ public abstract class CollectionBinder {
|
|||
ManyToMany manyToMany = property.getAnnotation( ManyToMany.class );
|
||||
ElementCollection elementCollection = property.getAnnotation( ElementCollection.class );
|
||||
ManyToAny manyToAny = property.getAnnotation( ManyToAny.class );
|
||||
NotFound notFound = property.getAnnotation( NotFound.class );
|
||||
|
||||
FetchType fetchType;
|
||||
if ( oneToMany != null ) {
|
||||
|
@ -991,55 +989,35 @@ public abstract class CollectionBinder {
|
|||
"Define fetch strategy on a property not annotated with @ManyToOne nor @OneToMany nor @CollectionOfElements"
|
||||
);
|
||||
}
|
||||
if ( notFound != null ) {
|
||||
collection.setLazy( false );
|
||||
|
||||
if ( lazy != null ) {
|
||||
collection.setExtraLazy( lazy.value() == LazyCollectionOption.EXTRA );
|
||||
if ( lazy != null ) {
|
||||
collection.setLazy( !( lazy.value() == LazyCollectionOption.FALSE ) );
|
||||
collection.setExtraLazy( lazy.value() == LazyCollectionOption.EXTRA );
|
||||
}
|
||||
else {
|
||||
collection.setLazy( fetchType == FetchType.LAZY );
|
||||
collection.setExtraLazy( false );
|
||||
}
|
||||
|
||||
if ( fetch != null ) {
|
||||
if ( fetch.value() == org.hibernate.annotations.FetchMode.JOIN ) {
|
||||
collection.setFetchMode( FetchMode.JOIN );
|
||||
collection.setLazy( false );
|
||||
}
|
||||
|
||||
if ( fetch != null ) {
|
||||
if ( fetch.value() != null ) {
|
||||
collection.setFetchMode( fetch.value().getHibernateFetchMode() );
|
||||
if ( fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT ) {
|
||||
collection.setSubselectLoadable( true );
|
||||
collection.getOwner().setSubselectLoadableCollections( true );
|
||||
}
|
||||
}
|
||||
else if ( fetch.value() == org.hibernate.annotations.FetchMode.SELECT ) {
|
||||
collection.setFetchMode( FetchMode.SELECT );
|
||||
}
|
||||
else if ( fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT ) {
|
||||
collection.setFetchMode( FetchMode.SELECT );
|
||||
collection.setSubselectLoadable( true );
|
||||
collection.getOwner().setSubselectLoadableCollections( true );
|
||||
}
|
||||
else {
|
||||
collection.setFetchMode( AnnotationBinder.getFetchMode( fetchType ) );
|
||||
throw new AssertionFailure( "Unknown FetchMode: " + fetch.value() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( lazy != null ) {
|
||||
collection.setLazy( !( lazy.value() == LazyCollectionOption.FALSE ) );
|
||||
collection.setExtraLazy( lazy.value() == LazyCollectionOption.EXTRA );
|
||||
}
|
||||
else {
|
||||
collection.setLazy( fetchType == FetchType.LAZY );
|
||||
collection.setExtraLazy( false );
|
||||
}
|
||||
if ( fetch != null ) {
|
||||
if ( fetch.value() == org.hibernate.annotations.FetchMode.JOIN ) {
|
||||
collection.setFetchMode( FetchMode.JOIN );
|
||||
collection.setLazy( false );
|
||||
}
|
||||
else if ( fetch.value() == org.hibernate.annotations.FetchMode.SELECT ) {
|
||||
collection.setFetchMode( FetchMode.SELECT );
|
||||
}
|
||||
else if ( fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT ) {
|
||||
collection.setFetchMode( FetchMode.SELECT );
|
||||
collection.setSubselectLoadable( true );
|
||||
collection.getOwner().setSubselectLoadableCollections( true );
|
||||
}
|
||||
else {
|
||||
throw new AssertionFailure( "Unknown FetchMode: " + fetch.value() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
collection.setFetchMode( AnnotationBinder.getFetchMode( fetchType ) );
|
||||
}
|
||||
collection.setFetchMode( AnnotationBinder.getFetchMode( fetchType ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2254,7 +2232,7 @@ public abstract class CollectionBinder {
|
|||
* collection element
|
||||
* Otherwise delegates to the usual algorithm
|
||||
*/
|
||||
public static void bindManytoManyInverseFk(
|
||||
public void bindManytoManyInverseFk(
|
||||
PersistentClass referencedEntity,
|
||||
AnnotatedJoinColumn[] columns,
|
||||
SimpleValue value,
|
||||
|
@ -2302,6 +2280,9 @@ public abstract class CollectionBinder {
|
|||
}
|
||||
else {
|
||||
BinderHelper.createSyntheticPropertyReference( columns, referencedEntity, null, value, true, buildingContext );
|
||||
if ( notFoundAction == NotFoundAction.IGNORE ) {
|
||||
value.disableForeignKey();
|
||||
}
|
||||
TableBinder.bindFk( referencedEntity, null, columns, value, unique, buildingContext );
|
||||
}
|
||||
}
|
||||
|
@ -2334,12 +2315,6 @@ public abstract class CollectionBinder {
|
|||
this.notFoundAction = notFoundAction;
|
||||
}
|
||||
|
||||
public void setIgnoreNotFound(boolean ignoreNotFound) {
|
||||
this.notFoundAction = ignoreNotFound
|
||||
? NotFoundAction.IGNORE
|
||||
: null;
|
||||
}
|
||||
|
||||
public void setMapKeyColumns(AnnotatedColumn[] mapKeyColumns) {
|
||||
this.mapKeyColumns = mapKeyColumns;
|
||||
}
|
||||
|
|
|
@ -6,46 +6,40 @@
|
|||
*/
|
||||
package org.hibernate.orm.test.annotations.formula;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
|
||||
import org.hibernate.LazyInitializationException;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.annotations.NotFound;
|
||||
import org.hibernate.annotations.NotFoundAction;
|
||||
import org.hibernate.cfg.AnnotationBinder;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.logger.LoggerInspectionRule;
|
||||
import org.hibernate.testing.logger.Triggerable;
|
||||
import org.hibernate.testing.transaction.TransactionUtil2;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil2.fromTransaction;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@TestForIssue(jiraKey = "HHH-12770")
|
||||
public class JoinFormulaOneToManyNotIgnoreLazyFetchingTest extends BaseEntityManagerFunctionalTestCase {
|
||||
@TestForIssue(jiraKey = {"HHH-12770", "HHH-15545"})
|
||||
public class ManyToManyNotIgnoreLazyFetchingTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Rule
|
||||
public LoggerInspectionRule logInspection = new LoggerInspectionRule(
|
||||
|
@ -80,6 +74,9 @@ public class JoinFormulaOneToManyNotIgnoreLazyFetchingTest extends BaseEntityMan
|
|||
Stock stock2 = new Stock();
|
||||
stock2.setId( 2L );
|
||||
entityManager.persist( stock2 );
|
||||
entityManager.flush();
|
||||
|
||||
entityManager.remove(code);
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -88,8 +85,13 @@ public class JoinFormulaOneToManyNotIgnoreLazyFetchingTest extends BaseEntityMan
|
|||
|
||||
assertFalse( triggerable.wasTriggered() );
|
||||
|
||||
List<Stock> stocks = fromTransaction( entityManagerFactory().unwrap( SessionFactoryImplementor.class ), (session) -> {
|
||||
return session.createQuery("SELECT s FROM Stock s order by id", Stock.class ).getResultList();
|
||||
List<Stock> stocks = fromTransaction( entityManagerFactory().unwrap( SessionFactoryImplementor.class ), session -> {
|
||||
List<Stock> list = session.createQuery("select s from Stock s order by id", Stock.class).getResultList();
|
||||
for (Stock s: list) {
|
||||
assertFalse( Hibernate.isInitialized( s.getCodes() ) );
|
||||
Hibernate.initialize( s.getCodes() );
|
||||
}
|
||||
return list;
|
||||
} );
|
||||
|
||||
assertThat( stocks ).hasSize( 2 );
|
||||
|
@ -97,7 +99,30 @@ public class JoinFormulaOneToManyNotIgnoreLazyFetchingTest extends BaseEntityMan
|
|||
final Stock firstStock = stocks.get( 0 );
|
||||
final Stock secondStock = stocks.get( 1 );
|
||||
|
||||
assertThat( firstStock.getCodes() ).hasSize( 1 );
|
||||
assertThat( firstStock.getCodes() ).hasSize( 0 );
|
||||
assertThat( secondStock.getCodes() ).hasSize( 0 );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEagerLoading() {
|
||||
|
||||
assertFalse( triggerable.wasTriggered() );
|
||||
|
||||
List<Stock> stocks = fromTransaction( entityManagerFactory().unwrap( SessionFactoryImplementor.class ),
|
||||
session -> session.createQuery("select s from Stock s left join fetch s.codes order by s.id", Stock.class)
|
||||
.getResultList()
|
||||
);
|
||||
|
||||
assertThat( stocks ).hasSize( 2 );
|
||||
|
||||
for (Stock s: stocks) {
|
||||
assertTrue( Hibernate.isInitialized( s.getCodes() ) );
|
||||
}
|
||||
|
||||
final Stock firstStock = stocks.get( 0 );
|
||||
final Stock secondStock = stocks.get( 1 );
|
||||
|
||||
assertThat( firstStock.getCodes() ).hasSize( 0 );
|
||||
assertThat( secondStock.getCodes() ).hasSize( 0 );
|
||||
}
|
||||
|
||||
|
@ -108,10 +133,15 @@ public class JoinFormulaOneToManyNotIgnoreLazyFetchingTest extends BaseEntityMan
|
|||
@Column(name = "ID")
|
||||
private Long id;
|
||||
|
||||
@OneToMany
|
||||
@ManyToMany
|
||||
@NotFound(action = NotFoundAction.IGNORE)
|
||||
@JoinColumn(name = "CODE_ID", referencedColumnName = "ID")
|
||||
private List<StockCode> codes = new ArrayList<>( );
|
||||
@JoinTable(name = "STOCK_BY_CODE",
|
||||
joinColumns = @JoinColumn(name = "STOCK_ID", referencedColumnName = "ID"),
|
||||
inverseJoinColumns = {
|
||||
@JoinColumn(name = "CODE_ID", referencedColumnName = "ID"),
|
||||
@JoinColumn(name = "CODE_TYPE", referencedColumnName = "TYPE")
|
||||
})
|
||||
private List<StockCode> codes = new ArrayList<>();
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
|
@ -36,8 +36,7 @@ public class OneToManyNotAuditedNullEntity implements Serializable {
|
|||
private String data;
|
||||
|
||||
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
@NotFound(action = NotFoundAction.IGNORE)
|
||||
@OneToMany(fetch = FetchType.EAGER)
|
||||
@JoinTable(joinColumns = @JoinColumn(name = "O2MNotAudited_id"))
|
||||
private List<UnversionedStrTestEntity> references = new ArrayList<UnversionedStrTestEntity>();
|
||||
|
||||
|
|
Loading…
Reference in New Issue