Skip to content

Commit 7c2a588

Browse files
alek-sysvladmihalcea
authored andcommitted
HHH-6382: Allow to use @onDelete annotation on unidirectional @onetomany associations
1 parent 1392b43 commit 7c2a588

File tree

3 files changed

+107
-9
lines changed

3 files changed

+107
-9
lines changed

hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.hibernate.annotations.LazyGroup;
4747
import org.hibernate.annotations.Loader;
4848
import org.hibernate.annotations.ManyToAny;
49+
import org.hibernate.annotations.OnDelete;
4950
import org.hibernate.annotations.OptimisticLock;
5051
import org.hibernate.annotations.OrderBy;
5152
import org.hibernate.annotations.Parameter;
@@ -480,6 +481,15 @@ public void bind() {
480481
throw new AnnotationException( message );
481482
}
482483

484+
if (!isMappedBy
485+
&& oneToMany
486+
&& property.isAnnotationPresent( OnDelete.class )
487+
&& !property.isAnnotationPresent( JoinColumn.class )) {
488+
String message = "Unidirectional one-to-many associations annotated with @OnDelete must define @JoinColumn: ";
489+
message += StringHelper.qualify( propertyHolder.getPath(), propertyName );
490+
throw new AnnotationException( message );
491+
}
492+
483493
collection.setInverse( isMappedBy );
484494

485495
//many to many may need some second pass informations

hibernate-core/src/main/java/org/hibernate/mapping/Collection.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -293,12 +293,6 @@ public void validate(Mapping mapping) throws MappingException {
293293
assert getKey() != null : "Collection key not bound : " + getRole();
294294
assert getElement() != null : "Collection element not bound : " + getRole();
295295

296-
if ( getKey().isCascadeDeleteEnabled() && ( !isInverse() || !isOneToMany() ) ) {
297-
throw new MappingException(
298-
"only inverse one-to-many associations may use on-delete=\"cascade\": "
299-
+ getRole()
300-
);
301-
}
302296
if ( !getKey().isValid( mapping ) ) {
303297
throw new MappingException(
304298
"collection foreign key mapping has wrong number of columns: "

hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/OneToManyTest.java

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,32 @@
66
*/
77
package org.hibernate.test.annotations.onetomany;
88

9-
import javax.persistence.PersistenceException;
109
import java.util.ArrayList;
1110
import java.util.Collection;
11+
import java.util.Collections;
1212
import java.util.HashSet;
1313
import java.util.Iterator;
1414
import java.util.List;
1515
import java.util.Set;
1616
import java.util.SortedSet;
1717
import java.util.TreeSet;
18+
import javax.persistence.CascadeType;
19+
import javax.persistence.Entity;
20+
import javax.persistence.GeneratedValue;
21+
import javax.persistence.Id;
22+
import javax.persistence.JoinColumn;
23+
import javax.persistence.OneToMany;
24+
import javax.persistence.PersistenceException;
1825

26+
import org.hibernate.AnnotationException;
1927
import org.hibernate.Hibernate;
20-
import org.hibernate.HibernateException;
2128
import org.hibernate.Session;
2229
import org.hibernate.Transaction;
30+
import org.hibernate.annotations.OnDelete;
31+
import org.hibernate.annotations.OnDeleteAction;
32+
import org.hibernate.boot.MetadataSources;
33+
import org.hibernate.boot.registry.StandardServiceRegistry;
34+
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
2335
import org.hibernate.exception.ConstraintViolationException;
2436
import org.hibernate.mapping.Column;
2537
import org.hibernate.mapping.PersistentClass;
@@ -36,6 +48,7 @@
3648
import org.junit.Test;
3749

3850
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
51+
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
3952
import static org.junit.Assert.assertEquals;
4053
import static org.junit.Assert.assertFalse;
4154
import static org.junit.Assert.assertNotNull;
@@ -345,6 +358,46 @@ public void testCascadeDelete() throws Exception {
345358
s.close();
346359
}
347360

361+
@Test
362+
public void testCascadeDeleteWithUnidirectionalAssociation() throws Exception {
363+
OnDeleteUnidirectionalOneToManyChild child = new OnDeleteUnidirectionalOneToManyChild();
364+
365+
doInHibernate( this::sessionFactory, session -> {
366+
OnDeleteUnidirectionalOneToManyParent parent = new OnDeleteUnidirectionalOneToManyParent();
367+
parent.children = Collections.singletonList( child);
368+
session.persist( parent );
369+
} );
370+
371+
doInHibernate( this::sessionFactory, session -> {
372+
session.createQuery("delete from OnDeleteUnidirectionalOneToManyParent").executeUpdate();
373+
} );
374+
375+
doInHibernate( this::sessionFactory, session -> {
376+
OnDeleteUnidirectionalOneToManyChild e1 = session.get( OnDeleteUnidirectionalOneToManyChild.class, child.id );
377+
assertNull( "delete cascade should work", e1 );
378+
} );
379+
}
380+
381+
@Test
382+
public void testOnDeleteWithoutJoinColumn() throws Exception {
383+
StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
384+
.build();
385+
386+
try {
387+
new MetadataSources( serviceRegistry )
388+
.addAnnotatedClass( OnDeleteUnidirectionalOneToMany.class )
389+
.addAnnotatedClass( ParentUnawareChild.class )
390+
.getMetadataBuilder()
391+
.build();
392+
}
393+
catch ( AnnotationException e ) {
394+
assertTrue(e.getMessage().contains( "Unidirectional one-to-many associations annotated with @OnDelete must define @JoinColumn" ));
395+
}
396+
finally {
397+
StandardServiceRegistryBuilder.destroy( serviceRegistry );
398+
}
399+
}
400+
348401
@Test
349402
public void testSimpleOneToManySet() throws Exception {
350403
Session s;
@@ -503,12 +556,53 @@ protected Class[] getAnnotatedClasses() {
503556
Person.class,
504557
Organisation.class,
505558
OrganisationUser.class,
506-
Model.class
559+
Model.class,
560+
OnDeleteUnidirectionalOneToManyParent.class,
561+
OnDeleteUnidirectionalOneToManyChild.class
507562
};
508563
}
509564

510565
@Override
511566
protected String[] getXmlFiles() {
512567
return new String[] { "org/hibernate/test/annotations/onetomany/orm.xml" };
513568
}
569+
570+
@Entity(name = "OnDeleteUnidirectionalOneToManyParent")
571+
public static class OnDeleteUnidirectionalOneToManyParent {
572+
573+
@Id
574+
@GeneratedValue
575+
Long id;
576+
577+
@OneToMany(cascade = CascadeType.ALL)
578+
@JoinColumn(name = "a_id")
579+
@OnDelete(action = OnDeleteAction.CASCADE)
580+
List<OnDeleteUnidirectionalOneToManyChild> children;
581+
}
582+
583+
@Entity(name = "OnDeleteUnidirectionalOneToManyChild")
584+
public static class OnDeleteUnidirectionalOneToManyChild {
585+
586+
@Id
587+
@GeneratedValue
588+
Long id;
589+
}
590+
591+
@Entity(name = "OnDeleteUnidirectionalOneToMany")
592+
public static class OnDeleteUnidirectionalOneToMany {
593+
594+
@Id
595+
Long id;
596+
597+
@OneToMany(cascade = CascadeType.ALL)
598+
@OnDelete(action = OnDeleteAction.CASCADE)
599+
List<ParentUnawareChild> children;
600+
}
601+
602+
@Entity(name = "ParentUnawareChild")
603+
public static class ParentUnawareChild {
604+
605+
@Id
606+
Long id;
607+
}
514608
}

0 commit comments

Comments
 (0)