Skip to content

Commit 9c87485

Browse files
authored
Merge pull request #33 from kaiso/feature/#31
Feature/#31
2 parents 340cd47 + 36cc715 commit 9c87485

16 files changed

Lines changed: 457 additions & 135 deletions

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<groupId>io.github.kaiso.relmongo</groupId>
77
<artifactId>relmongo</artifactId>
8-
<version>2.2.1</version>
8+
<version>2.3.0</version>
99
<packaging>jar</packaging>
1010

1111
<name>relmongo</name>

src/main/java/io/github/kaiso/relmongo/annotation/ManyToOne.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
/**
4141
* The field that owns the relationship.
4242
*
43-
* @since 2.0.0
43+
* @since 2.2.0
4444
*/
4545
String mappedBy() default "";
4646

src/main/java/io/github/kaiso/relmongo/annotation/OneToMany.java

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,34 @@
2121
import java.lang.annotation.RetentionPolicy;
2222
import java.lang.annotation.Target;
2323

24-
2524
/**
2625
* Describes Unidirectional OneToMany relation <br>
27-
* The referenced property for this association in the target object is allways the "_id"
26+
* The referenced property for this association in the target object is allways
27+
* the "_id"
28+
*
2829
* @author Kais OMRI
2930
*/
3031
@Retention(RetentionPolicy.RUNTIME)
3132
@Target(ElementType.FIELD)
3233
public @interface OneToMany {
33-
/**
34-
* (Optional) Whether the association should be lazily loaded or must be eagerly
35-
* fetched.
36-
*/
37-
FetchType fetch() default FetchType.LAZY;
34+
35+
/**
36+
* (Optional) Whether the association should be lazily loaded or must be eagerly
37+
* fetched.
38+
*/
39+
FetchType fetch() default FetchType.LAZY;
40+
41+
/**
42+
* (Optional) The operations that must be cascaded to the target of the
43+
* association.
44+
*/
45+
CascadeType cascade() default CascadeType.NONE;
3846

39-
/**
40-
* (Optional) The operations that must be cascaded to the target of the
41-
* association.
42-
*/
43-
CascadeType cascade() default CascadeType.NONE;
47+
/**
48+
* (Optional) Whether to apply the remove operation to documents that have been
49+
* removed from the
50+
* relationship and to cascade the remove operation to those documents.
51+
* @since 2.3.0
52+
*/
53+
boolean orphanRemoval() default false;
4454
}

src/main/java/io/github/kaiso/relmongo/annotation/OneToOne.java

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,31 @@
3131
@Retention(RetentionPolicy.RUNTIME)
3232
@Target(ElementType.FIELD)
3333
public @interface OneToOne {
34-
/**
35-
* (Optional) Whether the association should be lazily loaded or must be eagerly
36-
* fetched.
37-
*/
38-
FetchType fetch() default FetchType.LAZY;
34+
/**
35+
* (Optional) Whether the association should be lazily loaded or must be eagerly
36+
* fetched.
37+
*/
38+
FetchType fetch() default FetchType.LAZY;
3939

40-
/**
41-
* (Optional) The operations that must be cascaded to the target of the
42-
* association.
43-
*/
44-
CascadeType cascade() default CascadeType.NONE;
40+
/**
41+
* (Optional) The operations that must be cascaded to the target of the
42+
* association.
43+
*/
44+
CascadeType cascade() default CascadeType.NONE;
45+
46+
/**
47+
* The field that owns the relationship.
48+
*
49+
* @since 2.2.0
50+
*/
51+
String mappedBy() default "";
52+
53+
/**
54+
* (Optional) Whether to apply the remove operation to documents that have been
55+
* removed from the
56+
* relationship and to cascade the remove operation to those documents.
57+
* @since 2.3.0
58+
*/
59+
boolean orphanRemoval() default false;
4560

46-
/**
47-
* The field that owns the relationship.
48-
*
49-
* @since 2.0.0
50-
*/
51-
String mappedBy() default "";
5261
}

src/main/java/io/github/kaiso/relmongo/events/callback/PersistentPropertyConvertingCallback.java

Lines changed: 71 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,6 @@
1616

1717
package io.github.kaiso.relmongo.events.callback;
1818

19-
import java.lang.reflect.Field;
20-
import java.util.Arrays;
21-
import java.util.Collection;
22-
23-
import org.bson.types.ObjectId;
24-
import org.springframework.util.ReflectionUtils;
25-
import org.springframework.util.ReflectionUtils.FieldCallback;
26-
2719
import io.github.kaiso.relmongo.annotation.CascadeType;
2820
import io.github.kaiso.relmongo.annotation.OneToMany;
2921
import io.github.kaiso.relmongo.annotation.OneToOne;
@@ -34,71 +26,83 @@
3426
import io.github.kaiso.relmongo.util.ObjectIdReaderCallback;
3527
import io.github.kaiso.relmongo.util.ReflectionsUtil;
3628

29+
import org.bson.types.ObjectId;
30+
import org.springframework.util.ReflectionUtils;
31+
import org.springframework.util.ReflectionUtils.FieldCallback;
32+
33+
import java.lang.reflect.Field;
34+
import java.util.Arrays;
35+
import java.util.Collection;
36+
3737
/**
3838
*
3939
* @author Kais OMRI
4040
*
4141
*/
4242
public class PersistentPropertyConvertingCallback implements FieldCallback {
4343

44-
private Object source;
45-
46-
public PersistentPropertyConvertingCallback(Object source) {
47-
super();
48-
this.source = source;
49-
}
50-
51-
public void doWith(Field field) throws IllegalAccessException {
52-
ReflectionUtils.makeAccessible(field);
53-
54-
if (AnnotationsUtils.isMappedBy(field)) {
55-
field.set(source, null);
56-
return;
57-
}
58-
59-
MappedByProcessor.processChild(source, null, field, ReflectionsUtil.getGenericType(field));
60-
61-
if (field.isAnnotationPresent(OneToMany.class)) {
62-
fillIdentifiers(field, field.getAnnotation(OneToMany.class).cascade());
63-
} else if (field.isAnnotationPresent(OneToOne.class)) {
64-
fillIdentifiers(field, field.getAnnotation(OneToOne.class).cascade());
65-
}
66-
67-
}
68-
69-
private void fillIdentifiers(Field field, CascadeType cascadeType) throws IllegalAccessException {
70-
Object reference = field.get(source);
71-
if (reference == null) {
72-
return;
73-
}
74-
if (Arrays.asList(CascadeType.PERSIST, CascadeType.ALL).contains(cascadeType)) {
75-
if (Collection.class.isAssignableFrom(reference.getClass())) {
76-
((Collection<?>) reference).stream().forEach(this::checkIdentifier);
77-
} else {
78-
checkIdentifier(reference);
79-
}
80-
}
81-
82-
}
83-
84-
private void checkIdentifier(Object obj) {
85-
86-
try {
87-
ObjectIdReaderCallback objectIdReaderCallback = new ObjectIdReaderCallback(obj);
88-
ReflectionUtils.doWithFields(obj.getClass(), objectIdReaderCallback);
89-
if (objectIdReaderCallback.getIdField() == null) {
90-
throw new RelMongoConfigurationException("the Id field of class [" + obj.getClass()
91-
+ "] must be annotated by @Id (org.springframework.data.annotation.Id)");
92-
}
93-
Object id = objectIdReaderCallback.getIdField().get(obj);
94-
if (id == null) {
95-
objectIdReaderCallback.getIdField().set(obj, ObjectId.get());
96-
}
97-
98-
} catch (IllegalArgumentException | IllegalAccessException e) {
99-
throw new RelMongoProcessingException(e);
100-
}
101-
102-
}
44+
private Object source;
45+
46+
public PersistentPropertyConvertingCallback(Object source) {
47+
super();
48+
this.source = source;
49+
}
50+
51+
public void doWith(Field field) throws IllegalAccessException {
52+
ReflectionUtils.makeAccessible(field);
53+
54+
if (AnnotationsUtils.isMappedBy(field)) {
55+
field.set(source, null);
56+
return;
57+
}
58+
59+
MappedByProcessor.processChild(source, null, field, ReflectionsUtil.getGenericType(field));
60+
61+
if (field.isAnnotationPresent(OneToMany.class)) {
62+
fillIdentifiers(field, field.getAnnotation(OneToMany.class).cascade());
63+
} else if (field.isAnnotationPresent(OneToOne.class)) {
64+
fillIdentifiers(field, field.getAnnotation(OneToOne.class).cascade());
65+
}
66+
67+
}
68+
69+
private void fillIdentifiers(Field field, CascadeType cascadeType) throws IllegalAccessException {
70+
Object reference = field.get(source);
71+
if (reference == null) {
72+
return;
73+
}
74+
if (Arrays.asList(CascadeType.PERSIST, CascadeType.ALL).contains(cascadeType)) {
75+
if (Collection.class.isAssignableFrom(reference.getClass())) {
76+
((Collection<?>) reference).stream().forEach(this::checkIdentifier);
77+
} else {
78+
checkIdentifier(reference);
79+
}
80+
}
81+
82+
}
83+
84+
private void checkIdentifier(Object obj) {
85+
86+
try {
87+
ObjectIdReaderCallback objectIdReaderCallback = new ObjectIdReaderCallback(obj);
88+
ReflectionUtils.doWithFields(obj.getClass(), objectIdReaderCallback);
89+
Field idField = objectIdReaderCallback.getIdField();
90+
if (idField == null) {
91+
throw new RelMongoConfigurationException("the Id field of class [" + obj.getClass()
92+
+ "] must be annotated by @Id (org.springframework.data.annotation.Id)");
93+
}
94+
if (idField.get(obj) == null) {
95+
if (idField.getType().equals(ObjectId.class)) {
96+
idField.set(obj, ObjectId.get());
97+
} else if (idField.getType().equals(String.class)) {
98+
idField.set(obj, ObjectId.get().toString());
99+
}
100+
}
101+
102+
} catch (IllegalArgumentException | IllegalAccessException e) {
103+
throw new RelMongoProcessingException(e);
104+
}
105+
106+
}
103107

104108
}

src/main/java/io/github/kaiso/relmongo/events/callback/PersistentPropertyCascadingSaveCallback.java renamed to src/main/java/io/github/kaiso/relmongo/events/callback/PersistentPropertyPostSavingCallback.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,18 @@
2727
import java.lang.reflect.Field;
2828
import java.util.Arrays;
2929
import java.util.Collection;
30+
3031
/**
3132
*
3233
* @author Kais OMRI
3334
*
3435
*/
35-
public class PersistentPropertyCascadingSaveCallback implements FieldCallback {
36+
public class PersistentPropertyPostSavingCallback implements FieldCallback {
3637

3738
private Object source;
3839
private MongoOperations mongoOperations;
3940

40-
public PersistentPropertyCascadingSaveCallback(Object source, MongoOperations mongoOperations) {
41+
public PersistentPropertyPostSavingCallback(Object source, MongoOperations mongoOperations) {
4142
super();
4243
this.source = source;
4344
this.mongoOperations = mongoOperations;
@@ -46,21 +47,22 @@ public PersistentPropertyCascadingSaveCallback(Object source, MongoOperations mo
4647
public void doWith(Field field) throws IllegalAccessException {
4748
ReflectionUtils.makeAccessible(field);
4849
if (field.isAnnotationPresent(OneToMany.class)) {
49-
doCascade(field, field.getAnnotation(OneToMany.class).cascade());
50+
doProcessing(field, field.getAnnotation(OneToMany.class).cascade(), field.getAnnotation(OneToMany.class).orphanRemoval());
5051
} else if (field.isAnnotationPresent(OneToOne.class)) {
51-
doCascade(field, field.getAnnotation(OneToOne.class).cascade());
52+
doProcessing(field, field.getAnnotation(OneToOne.class).cascade(), field.getAnnotation(OneToOne.class).orphanRemoval());
5253
}
5354

5455
}
5556

56-
private void doCascade(Field field, CascadeType cascadeType) throws IllegalAccessException {
57+
private void doProcessing(Field field, CascadeType cascadeType, boolean orphanRemoval) throws IllegalAccessException {
5758
Object child = field.get(source);
5859
if (child != null) {
5960
if (Collection.class.isAssignableFrom(child.getClass())) {
6061
cascadeCollection(cascadeType, (Collection<?>) child);
61-
62+
removeOrphans(orphanRemoval, (Collection<?>) child);
6263
} else {
6364
cascadeItem(cascadeType, child);
65+
removeOrphans(orphanRemoval, child);
6466

6567
}
6668
}
@@ -79,4 +81,16 @@ private void cascadeCollection(CascadeType cascadeType, Collection<?> child) {
7981
}
8082
}
8183

84+
private void removeOrphans(Boolean orphanRemoval, Object child) {
85+
if (Boolean.TRUE.equals(orphanRemoval)) {
86+
87+
}
88+
}
89+
90+
private void removeOrphans(Boolean orphanRemoval, Collection<?> child) {
91+
if (Boolean.TRUE.equals(orphanRemoval)) {
92+
93+
}
94+
}
95+
8296
}

0 commit comments

Comments
 (0)