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