diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedColumn.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedColumn.java index cf3058d925..d101cb7e99 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedColumn.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedColumn.java @@ -282,6 +282,7 @@ public class AnnotatedColumn { mappingColumn.setSqlType( sqlType ); mappingColumn.setUnique( unique ); mappingColumn.setCheckConstraint( checkConstraint ); + mappingColumn.setDefaultValue( defaultValue ); if ( writeExpression != null ) { final int numberOfJdbcParams = StringHelper.count( writeExpression, '?' ); @@ -766,7 +767,7 @@ public class AnnotatedColumn { : database.getJdbcEnvironment().getIdentifierHelper().toIdentifier( column.name() ).render(); } - private void applyColumnDefault(PropertyData inferredData, int length) { + void applyColumnDefault(PropertyData inferredData, int length) { final XProperty property = inferredData.getProperty(); if ( property != null ) { final ColumnDefault columnDefault = @@ -783,7 +784,7 @@ public class AnnotatedColumn { } } - private void applyGeneratedAs(PropertyData inferredData, int length) { + void applyGeneratedAs(PropertyData inferredData, int length) { final XProperty property = inferredData.getProperty(); if ( property != null ) { final GeneratedColumn generatedColumn = diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedJoinColumn.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedJoinColumn.java index 49521ab674..ac5fd1d236 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedJoinColumn.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedJoinColumn.java @@ -76,15 +76,15 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { String mappedBy, AnnotatedJoinColumns parent, PropertyHolder propertyHolder, - String propertyName) { - final String path = qualify( propertyHolder.getPath(), propertyName ); + PropertyData inferredData) { + final String path = qualify( propertyHolder.getPath(), inferredData.getPropertyName() ); final JoinColumn[] overrides = propertyHolder.getOverriddenJoinColumn( path ); if ( overrides != null ) { //TODO: relax this restriction - throw new AnnotationException("Property '" + path - + "' overrides mapping specified using '@JoinColumnOrFormula'"); + throw new AnnotationException( "Property '" + path + + "' overrides mapping specified using '@JoinColumnOrFormula'" ); } - return buildJoinColumn( joinColumn, null, mappedBy, parent, propertyHolder, propertyName, "" ); + return buildJoinColumn( joinColumn, null, mappedBy, parent, propertyHolder, inferredData, "" ); } public static AnnotatedJoinColumn buildJoinFormula( @@ -108,19 +108,18 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { String mappedBy, AnnotatedJoinColumns parent, PropertyHolder propertyHolder, - String propertyName, + PropertyData inferredData, String defaultColumnSuffix) { if ( joinColumn != null ) { if ( !isEmptyOrNullAnnotationValue( mappedBy ) ) { - throw new AnnotationException( - "Association '" + getRelativePath( propertyHolder, propertyName ) - + "' is 'mappedBy' a different entity and may not explicitly specify the '@JoinColumn'" - ); + throw new AnnotationException( "Association '" + + getRelativePath( propertyHolder, inferredData.getPropertyName() ) + + "' is 'mappedBy' a different entity and may not explicitly specify the '@JoinColumn'" ); } - return explicitJoinColumn( joinColumn, comment, parent, propertyName, defaultColumnSuffix ); + return explicitJoinColumn( joinColumn, comment, parent, inferredData, defaultColumnSuffix ); } else { - return implicitJoinColumn( parent, propertyName, defaultColumnSuffix ); + return implicitJoinColumn( parent, inferredData, defaultColumnSuffix ); } } @@ -128,7 +127,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { JoinColumn joinColumn, Comment comment, AnnotatedJoinColumns parent, - String propertyName, + PropertyData inferredData, String defaultColumnSuffix) { final AnnotatedJoinColumn column = new AnnotatedJoinColumn(); column.setComment( comment != null ? comment.value() : null ); @@ -136,19 +135,20 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { // column.setJoins( joins ); // column.setPropertyHolder( propertyHolder ); if ( isEmpty( column.getLogicalColumnName() ) && isNotEmpty( defaultColumnSuffix ) ) { - column.setLogicalColumnName( propertyName + defaultColumnSuffix ); + column.setLogicalColumnName( inferredData.getPropertyName() + defaultColumnSuffix ); } // column.setPropertyName( getRelativePath( propertyHolder, propertyName ) ); column.setImplicit( false ); column.setParent( parent ); column.applyJoinAnnotation( joinColumn, null ); + column.applyColumnDefault( inferredData, parent.getColumns().size() ); column.bind(); return column; } private static AnnotatedJoinColumn implicitJoinColumn( AnnotatedJoinColumns parent, - String propertyName, + PropertyData inferredData, String defaultColumnSuffix) { final AnnotatedJoinColumn column = new AnnotatedJoinColumn(); // column.setContext( context ); @@ -157,13 +157,14 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { // column.setPropertyName( getRelativePath( propertyHolder, propertyName ) ); // property name + suffix is an "explicit" column name if ( isNotEmpty( defaultColumnSuffix ) ) { - column.setLogicalColumnName( propertyName + defaultColumnSuffix ); + column.setLogicalColumnName( inferredData.getPropertyName() + defaultColumnSuffix ); column.setImplicit( false ); } else { column.setImplicit( true ); } column.setParent( parent ); + column.applyColumnDefault( inferredData, parent.getColumns().size() ); column.bind(); return column; } @@ -401,7 +402,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { static AnnotatedJoinColumn buildImplicitJoinTableJoinColumn( AnnotatedJoinColumns parent, PropertyHolder propertyHolder, - String propertyName) { + PropertyData inferredData) { final AnnotatedJoinColumn column = new AnnotatedJoinColumn(); column.setImplicit( true ); column.setNullable( false ); //I break the spec, but it's for good @@ -417,7 +418,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { static AnnotatedJoinColumn buildExplicitJoinTableJoinColumn( AnnotatedJoinColumns parent, PropertyHolder propertyHolder, - String propertyName, + PropertyData inferredData, JoinColumn joinColumn) { final AnnotatedJoinColumn column = new AnnotatedJoinColumn(); column.setImplicit( true ); @@ -428,7 +429,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { column.setNullable( false ); //I break the spec, but it's for good //done after the annotation to override it column.setParent( parent ); - column.applyJoinAnnotation( joinColumn, propertyName ); + column.applyJoinAnnotation( joinColumn, inferredData.getPropertyName() ); column.bind(); return column; } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedJoinColumns.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedJoinColumns.java index 8ee3d37078..0e14863eb8 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedJoinColumns.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedJoinColumns.java @@ -67,13 +67,13 @@ public class AnnotatedJoinColumns extends AnnotatedColumns { String mappedBy, Map joins, PropertyHolder propertyHolder, - String propertyName, + PropertyData inferredData, MetadataBuildingContext context) { final AnnotatedJoinColumns parent = new AnnotatedJoinColumns(); parent.setBuildingContext( context ); parent.setJoins( joins ); parent.setPropertyHolder( propertyHolder ); - parent.setPropertyName( getRelativePath( propertyHolder, propertyName ) ); + parent.setPropertyName( getRelativePath( propertyHolder, inferredData.getPropertyName() ) ); parent.setMappedBy( mappedBy ); for ( JoinColumnOrFormula columnOrFormula : joinColumnOrFormulas ) { final JoinFormula formula = columnOrFormula.formula(); @@ -82,23 +82,23 @@ public class AnnotatedJoinColumns extends AnnotatedColumns { AnnotatedJoinColumn.buildJoinFormula( formula, parent ); } else { - AnnotatedJoinColumn.buildJoinColumn( column, mappedBy, parent, propertyHolder, propertyName ); + AnnotatedJoinColumn.buildJoinColumn( column, mappedBy, parent, propertyHolder, inferredData ); } } return parent; } static AnnotatedJoinColumns buildJoinColumnsWithFormula( - String propertyName, JoinFormula joinFormula, Map secondaryTables, PropertyHolder propertyHolder, + PropertyData inferredData, MetadataBuildingContext context) { final AnnotatedJoinColumns joinColumns = new AnnotatedJoinColumns(); joinColumns.setBuildingContext( context ); joinColumns.setJoins( secondaryTables ); joinColumns.setPropertyHolder( propertyHolder ); - joinColumns.setPropertyName( getRelativePath( propertyHolder, propertyName ) ); + joinColumns.setPropertyName( getRelativePath( propertyHolder, inferredData.getPropertyName() ) ); AnnotatedJoinColumn.buildJoinFormula( joinFormula, joinColumns ); return joinColumns; } @@ -109,7 +109,7 @@ public class AnnotatedJoinColumns extends AnnotatedColumns { String mappedBy, Map joins, PropertyHolder propertyHolder, - String propertyName, + PropertyData inferredData, MetadataBuildingContext buildingContext) { return buildJoinColumnsWithDefaultColumnSuffix( joinColumns, @@ -117,7 +117,7 @@ public class AnnotatedJoinColumns extends AnnotatedColumns { mappedBy, joins, propertyHolder, - propertyName, + inferredData, "", buildingContext ); @@ -129,9 +129,10 @@ public class AnnotatedJoinColumns extends AnnotatedColumns { String mappedBy, Map joins, PropertyHolder propertyHolder, - String propertyName, + PropertyData inferredData, String defaultColumnSuffix, MetadataBuildingContext context) { + final String propertyName = inferredData.getPropertyName(); final String path = qualify( propertyHolder.getPath(), propertyName ); final JoinColumn[] overriddes = propertyHolder.getOverriddenJoinColumn( path ); final JoinColumn[] actualColumns = overriddes == null ? joinColumns : overriddes; @@ -148,7 +149,7 @@ public class AnnotatedJoinColumns extends AnnotatedColumns { mappedBy, parent, propertyHolder, - propertyName, + inferredData, defaultColumnSuffix ); } @@ -161,7 +162,7 @@ public class AnnotatedJoinColumns extends AnnotatedColumns { mappedBy, parent, propertyHolder, - propertyName, + inferredData, defaultColumnSuffix ); } @@ -173,21 +174,21 @@ public class AnnotatedJoinColumns extends AnnotatedColumns { JoinColumn[] joinColumns, Map secondaryTables, PropertyHolder propertyHolder, - String propertyName, + PropertyData inferredData, String mappedBy, MetadataBuildingContext context) { final AnnotatedJoinColumns parent = new AnnotatedJoinColumns(); parent.setBuildingContext( context ); parent.setJoins( secondaryTables ); parent.setPropertyHolder( propertyHolder ); - parent.setPropertyName( getRelativePath( propertyHolder, propertyName ) ); + parent.setPropertyName( getRelativePath( propertyHolder, inferredData.getPropertyName() ) ); parent.setMappedBy( mappedBy ); if ( joinColumns == null ) { - AnnotatedJoinColumn.buildImplicitJoinTableJoinColumn( parent, propertyHolder, propertyName ); + AnnotatedJoinColumn.buildImplicitJoinTableJoinColumn( parent, propertyHolder, inferredData ); } else { for ( JoinColumn joinColumn : joinColumns ) { - AnnotatedJoinColumn.buildExplicitJoinTableJoinColumn( parent, propertyHolder, propertyName, joinColumn ); + AnnotatedJoinColumn.buildExplicitJoinTableJoinColumn( parent, propertyHolder, inferredData, joinColumn ); } } return parent; diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/ColumnsBuilder.java b/hibernate-core/src/main/java/org/hibernate/cfg/ColumnsBuilder.java index 5cddcd9ea2..e9254ab02e 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/ColumnsBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/ColumnsBuilder.java @@ -136,7 +136,7 @@ class ColumnsBuilder { oneToMany != null ? oneToMany.mappedBy() : "", entityBinder.getSecondaryTables(), propertyHolder, - inferredData.getPropertyName(), + inferredData, buildingContext ); } @@ -182,7 +182,7 @@ class ColumnsBuilder { null, entityBinder.getSecondaryTables(), propertyHolder, - inferredData.getPropertyName(), + inferredData, buildingContext ); } @@ -194,7 +194,7 @@ class ColumnsBuilder { oneToOneAnn != null ? oneToOneAnn.mappedBy() : null, entityBinder.getSecondaryTables(), propertyHolder, - inferredData.getPropertyName(), + inferredData, buildingContext ); } @@ -202,8 +202,6 @@ class ColumnsBuilder { private AnnotatedJoinColumns buildExplicitJoinColumns(XProperty property, PropertyData inferredData) { // process @JoinColumns before @Columns to handle collection of entities properly - final String propertyName = inferredData.getPropertyName(); - final JoinColumn[] joinColumnAnnotations = getJoinColumnAnnotations( property, inferredData ); if ( joinColumnAnnotations != null ) { return AnnotatedJoinColumns.buildJoinColumns( @@ -212,7 +210,7 @@ class ColumnsBuilder { null, entityBinder.getSecondaryTables(), propertyHolder, - propertyName, + inferredData, buildingContext ); } @@ -224,7 +222,7 @@ class ColumnsBuilder { null, entityBinder.getSecondaryTables(), propertyHolder, - propertyName, + inferredData, buildingContext ); } @@ -232,10 +230,10 @@ class ColumnsBuilder { if ( property.isAnnotationPresent( JoinFormula.class) ) { final JoinFormula joinFormula = getOverridableAnnotation( property, JoinFormula.class, buildingContext ); return AnnotatedJoinColumns.buildJoinColumnsWithFormula( - propertyName, joinFormula, entityBinder.getSecondaryTables(), propertyHolder, + inferredData, buildingContext ); } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/ToOneFkSecondPass.java b/hibernate-core/src/main/java/org/hibernate/cfg/ToOneFkSecondPass.java index 99a22d5f69..7f08fc267b 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/ToOneFkSecondPass.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/ToOneFkSecondPass.java @@ -63,7 +63,7 @@ public class ToOneFkSecondPass extends FkSecondPass { } final PersistentClass persistentClass = buildingContext.getMetadataCollector() .getEntityBinding( entityClassName ); - Property property = persistentClass.getIdentifierProperty(); + final Property property = persistentClass.getIdentifierProperty(); if ( path == null ) { return false; } @@ -82,7 +82,7 @@ public class ToOneFkSecondPass extends FkSecondPass { localPath = path.substring( 3 ); } - Component component = (Component) valueIdentifier; + final Component component = (Component) valueIdentifier; for ( Property idProperty : component.getProperties() ) { if ( localPath.equals( idProperty.getName() ) || localPath.startsWith( idProperty.getName() + "." ) ) { return true; diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java index 93cfe2094c..fafe32bfab 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java @@ -388,7 +388,7 @@ public abstract class CollectionBinder { null, entityBinder.getSecondaryTables(), propertyHolder, - inferredData.getPropertyName(), + inferredData, "_KEY", context ); @@ -706,7 +706,7 @@ public abstract class CollectionBinder { annJoins, entityBinder.getSecondaryTables(), propertyHolder, - inferredData.getPropertyName(), + inferredData, mappedBy, buildingContext ) ); @@ -714,7 +714,7 @@ public abstract class CollectionBinder { annInverseJoins, entityBinder.getSecondaryTables(), propertyHolder, - inferredData.getPropertyName(), + inferredData, mappedBy, buildingContext ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ondelete/toone/ToOneOnDeleteSetNullTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/ondelete/toone/ToOneOnDeleteSetNullTest.java new file mode 100644 index 0000000000..20f0418677 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/ondelete/toone/ToOneOnDeleteSetNullTest.java @@ -0,0 +1,137 @@ +package org.hibernate.orm.test.ondelete.toone; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; +import org.hibernate.dialect.SybaseDialect; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.SkipForDialect; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + + +@DomainModel( + annotatedClasses = { + ToOneOnDeleteSetNullTest.Parent.class, + ToOneOnDeleteSetNullTest.Child.class, + ToOneOnDeleteSetNullTest.GrandChild.class + } +) +@SessionFactory +public class ToOneOnDeleteSetNullTest { + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncateMappedObjects(); + } + + @Test + @SkipForDialect( + dialectClass = SybaseDialect.class, + matchSubTypes = true, + reason = "Sybase does not support on delete actions" + ) + public void testManyToOne(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent(); + parent.id = 1L; + session.persist( parent ); + + Child child1 = new Child(); + child1.id = 1L; + child1.parent = parent; + session.persist( child1 ); + + GrandChild grandChild11 = new GrandChild(); + grandChild11.id = 1L; + grandChild11.parent = child1; + session.persist( grandChild11 ); + + Child child2 = new Child(); + child2.id = 2L; + child2.parent = parent; + session.persist( child2 ); + + GrandChild grandChild21 = new GrandChild(); + grandChild21.id = 2L; + grandChild21.parent = child2; + session.persist( grandChild21 ); + + GrandChild grandChild22 = new GrandChild(); + grandChild22.id = 3L; + grandChild22.parent = child2; + session.persist( grandChild22 ); + } + ); + + scope.inTransaction( + session -> { + assertNotNull( session.get(Child.class, 1L).parent ); + assertNotNull( session.get(Child.class, 2L).parent ); + assertNotNull( session.get(GrandChild.class, 2L).parent ); + assertNotNull( session.get(GrandChild.class, 3L).parent ); + } + ); + scope.inTransaction( + session -> { + Parent parent = session.get( Parent.class, 1L ); + session.remove( parent ); + } + ); + scope.inTransaction( + session -> { + assertNull( session.get(Child.class, 1L).parent ); + assertNull( session.get(Child.class, 2L).parent ); + assertNotNull( session.get(GrandChild.class, 2L).parent ); + assertNotNull( session.get(GrandChild.class, 3L).parent ); + assertNull( session.get( Parent.class, 1L ) ); + } + ); + } + + + @Entity(name = "Parent") + public static class Parent { + + @Id + private Long id; + + private String name; + } + + @Entity(name = "Child") + public static class Child { + + @Id + private Long id; + + private String name; + + @ManyToOne + @OnDelete(action = OnDeleteAction.SET_NULL) + private Parent parent; + } + + @Entity(name = "GrandChild") + public static class GrandChild { + + @Id + private Long id; + + private String name; + + @ManyToOne + @OnDelete(action = OnDeleteAction.SET_DEFAULT) + @ColumnDefault("null") + private Child parent; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ondelete/toone/ToOneOnDeleteTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/ondelete/toone/ToOneOnDeleteTest.java index e64fa9dee9..6fdc6f66e3 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/ondelete/toone/ToOneOnDeleteTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/ondelete/toone/ToOneOnDeleteTest.java @@ -3,7 +3,6 @@ package org.hibernate.orm.test.ondelete.toone; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToOne; import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; @@ -16,6 +15,9 @@ import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + /** * @author Vlad Mihalcea @@ -31,21 +33,15 @@ import org.junit.jupiter.api.Test; public class ToOneOnDeleteTest { @AfterEach - public void tearDown(SessionFactoryScope scope){ - scope.inTransaction( - session -> { - session.createQuery( "delete from Parent" ).executeUpdate(); - session.createQuery( "delete from Child" ).executeUpdate(); - session.createQuery( "delete from GrandChild" ).executeUpdate(); - } - ); + public void tearDown(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncateMappedObjects(); } @Test @SkipForDialect( dialectClass = SybaseDialect.class, matchSubTypes = true, - reason = "HHH-13559 on-delete=\"cascade\" is not supported for unidirectional to-one associations using Sybase" + reason = "Sybase does not support on delete actions" ) public void testManyToOne(SessionFactoryScope scope) { scope.inTransaction( @@ -83,11 +79,27 @@ public class ToOneOnDeleteTest { scope.inTransaction( session -> { - Parent parent = session.get( Parent.class, 1L ); - session.delete( parent ); + assertNotNull( session.get(Child.class, 1L) ); + assertNotNull( session.get(Child.class, 2L) ); + assertNotNull( session.get(GrandChild.class, 2L) ); + assertNotNull( session.get(GrandChild.class, 3L) ); } ); - } + scope.inTransaction( + session -> { + Parent parent = session.get( Parent.class, 1L ); + session.remove( parent ); + } + ); + scope.inTransaction( + session -> { + assertNull( session.get(Child.class, 1L) ); + assertNull( session.get(Child.class, 2L) ); + assertNull( session.get(GrandChild.class, 2L) ); + assertNull( session.get(GrandChild.class, 3L) ); + assertNull( session.get( Parent.class, 1L ) ); + } + ); } @Entity(name = "Parent")