From 3a2e68e981e12b7f93797e37ffa0be88686d3fe4 Mon Sep 17 00:00:00 2001 From: kaiso Date: Thu, 9 Feb 2023 22:35:08 +0100 Subject: [PATCH 1/2] [feature]#97] support spring 6 --- pom.xml | 499 +++++++++--------- .../config/RelMongoBeanPostProcessor.java | 137 ++--- .../config/RelMongoMappingContext.java | 15 +- .../PersistentPropertyConvertingCallback.java | 3 +- .../PersistentPropertyPostSavingCallback.java | 1 + .../PersistentPropertySavingCallback.java | 215 ++++---- .../events/processor/MappedByProcessor.java | 2 +- .../events/processor/RelMongoProcessor.java | 197 +++---- .../mongo/PersistentRelationResolver.java | 17 +- .../config/TestContextConfiguration.java | 50 +- .../tests/MultipleMongoTemplateRefTest.java | 6 +- .../relmongo/tests/TransactionsTest.java | 22 +- 12 files changed, 601 insertions(+), 563 deletions(-) diff --git a/pom.xml b/pom.xml index a1afd9d..2218870 100644 --- a/pom.xml +++ b/pom.xml @@ -1,269 +1,284 @@ - 4.0.0 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - io.github.kaiso.relmongo - relmongo - 3.5.0-RC4 - jar + io.github.kaiso.relmongo + relmongo + 4.0.0-RC1 + jar - relmongo - https://kaiso.github.io/relmongo/ - Java relationship-enabled domain model persistence framework for MongoDB + relmongo + https://kaiso.github.io/relmongo/ + Java relationship-enabled domain model persistence framework for MongoDB - - UTF-8 - 1.8 - 1.8 - 3.0.1 - 3.0.1 - 2.2.6.RELEASE - 5.2.5.RELEASE - 3.11.2 - 5.4.1 - 1.4.2 - 2.11.1 - 2.13.3 - + + UTF-8 + 17 + 17 + 3.0.1 + 3.0.1 + 4.0.1 + 6.0.4 + 4.8.2 + 5.4.1 + 1.4.2 + 2.11.1 + 2.19.0 + 2.0.2 + - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + - - - The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - + + + The Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + - - - Kais OMRI - kais.omri.int@gmail.com - RelMongo - https://kaiso.github.io/relmongo/ - - + + + Kais OMRI + kais.omri.int@gmail.com + RelMongo + https://kaiso.github.io/relmongo/ + + - - scm:git:git://github.com/kaiso/relmongo.git - scm:git:git@github.com:kaiso/relmongo.git - http://github.com/kaiso/relmongo/tree/master - + + scm:git:git://github.com/kaiso/relmongo.git + scm:git:git@github.com:kaiso/relmongo.git + http://github.com/kaiso/relmongo/tree/master + - + - - org.springframework.data - spring-data-mongodb - ${spring.data.version} - provided - + + org.springframework.data + spring-data-mongodb + ${spring.data.version} + provided + - - org.springframework - spring-beans - ${spring.version} - provided - + + org.springframework + spring-beans + ${spring.version} + provided + - - org.springframework - spring-core - ${spring.version} - provided - + + org.springframework + spring-core + ${spring.version} + provided + - - org.mongodb - mongodb-driver-async - ${mongo.driver.version} - + + org.mongodb + mongodb-driver-reactivestreams + ${mongo.driver.version} + - - - org.junit.jupiter - junit-jupiter-engine - ${junit.jupiter.version} - test - + + org.mongodb + mongodb-driver-sync + ${mongo.driver.version} + - - javax.annotation - javax.annotation-api - 1.3.2 - test - + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + - - org.junit.platform - junit-platform-launcher - ${junit.platform.version} - test - + + javax.annotation + javax.annotation-api + 1.3.2 + test + - - de.flapdoodle.embed - de.flapdoodle.embed.mongo - 2.2.0 - test - - - org.springframework - spring-test - ${spring.version} - test - + + org.junit.platform + junit-platform-launcher + ${junit.platform.version} + test + - - org.apache.logging.log4j - log4j-slf4j-impl - ${log4j.version} - test - - - org.apache.logging.log4j - log4j-1.2-api - ${log4j.version} - test - + + de.flapdoodle.embed + de.flapdoodle.embed.mongo + 2.2.0 + test + + + org.springframework + spring-test + ${spring.version} + test + - - org.apache.logging.log4j - log4j-api - ${log4j.version} - test - - - org.apache.logging.log4j - log4j-core - ${log4j.version} - test - - - com.fasterxml.jackson.dataformat - jackson-dataformat-yaml - ${jackson.version} - test - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - test - - + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} + test + + + org.apache.logging.log4j + log4j-1.2-api + ${log4j.version} + test + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + test + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + test + + + + org.slf4j + slf4j-reload4j + ${slf4j.version} + test + - - - - maven-compiler-plugin - 3.7.0 - - - org.jacoco - jacoco-maven-plugin - 0.8.4 - - - coverage-initialize - - prepare-agent - - - - coverage-report - post-integration-test - - report - - - - - - maven-surefire-plugin - 2.22.0 - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 - true - - ossrh - https://oss.sonatype.org/ - true - - - - org.eluder.coveralls - coveralls-maven-plugin - 4.3.0 - - vVnfMbOSQzR7tz7b8gyVM3iearDa8aUkT - - - - + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + ${jackson.version} + test + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + test + + - - - release - - - - org.apache.maven.plugins - maven-source-plugin - ${maven.plugin.source.version} - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - ${maven.plugin.javadoc.version} - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - + + + + maven-compiler-plugin + 3.7.0 + + + org.jacoco + jacoco-maven-plugin + 0.8.8 + + + coverage-initialize + + prepare-agent + + + + coverage-report + post-integration-test + + report + + + + + + maven-surefire-plugin + 2.22.0 + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.13 + true + + ossrh + https://oss.sonatype.org/ + true + + + + org.eluder.coveralls + coveralls-maven-plugin + 4.3.0 + + vVnfMbOSQzR7tz7b8gyVM3iearDa8aUkT + + + + - - - - - + + + + release + + + + org.apache.maven.plugins + maven-source-plugin + ${maven.plugin.source.version} + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven.plugin.javadoc.version} + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/io/github/kaiso/relmongo/config/RelMongoBeanPostProcessor.java b/src/main/java/io/github/kaiso/relmongo/config/RelMongoBeanPostProcessor.java index 2441edb..8955b0e 100644 --- a/src/main/java/io/github/kaiso/relmongo/config/RelMongoBeanPostProcessor.java +++ b/src/main/java/io/github/kaiso/relmongo/config/RelMongoBeanPostProcessor.java @@ -1,6 +1,8 @@ package io.github.kaiso.relmongo.config; -import io.github.kaiso.relmongo.events.processor.RelMongoProcessor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanInitializationException; @@ -9,69 +11,80 @@ import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.mapping.event.MongoMappingEvent; -import java.lang.reflect.Field; +import io.github.kaiso.relmongo.events.processor.RelMongoProcessor; public class RelMongoBeanPostProcessor implements BeanPostProcessor { - private final String mongoTemplateRef; - - public RelMongoBeanPostProcessor(String mongoTemplateRef) { - super(); - this.mongoTemplateRef = mongoTemplateRef; - } - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - - if (bean instanceof MongoTemplate && beanName.equals(mongoTemplateRef)) { - - /* - * Enhancer enhancer = new Enhancer(); - * enhancer.setSuperclass(bean.getClass()); - * enhancer.setCallbacks(new MethodInterceptor[] { new - * RelMongoTemplateInvocationHandler() }); - * - * Object proxy = enhancer.create(new Class[] { MongoDbFactory.class, - * MongoConverter.class }, - * new Object[] { ((MongoTemplate) bean).getMongoDbFactory(), ((MongoTemplate) - * bean).getConverter() }); - * - * ((MongoTemplate) proxy).setApplicationContext(applicationContext); - * - * return proxy; - */ - - try { - Field ep = MongoTemplate.class.getDeclaredField("eventPublisher"); - ep.setAccessible(true); - ep.set(bean, new RelMongoEventPublisher((MongoTemplate) bean, (ApplicationEventPublisher) ep.get(bean))); - } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { - throw new BeanInitializationException("Fatal: failed to init the RelMongo Engine", e); - } - } - - return bean; - } - - private static final class RelMongoEventPublisher implements ApplicationEventPublisher { - - private final RelMongoProcessor relMongoProcessor; - private final MongoTemplate mongoTemplate; - private final ApplicationEventPublisher eventPublisher; - - public RelMongoEventPublisher(MongoTemplate mongoTemplate, ApplicationEventPublisher eventPublisher) { - super(); - this.relMongoProcessor = new RelMongoProcessor(); - this.mongoTemplate = mongoTemplate; - this.eventPublisher = eventPublisher; - } - - @Override - public void publishEvent(Object event) { - relMongoProcessor.onApplicationEvent((MongoMappingEvent) event, mongoTemplate); - eventPublisher.publishEvent(event); - } - - } + private final String mongoTemplateRef; + + public RelMongoBeanPostProcessor(String mongoTemplateRef) { + super(); + this.mongoTemplateRef = mongoTemplateRef; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + + if (bean instanceof MongoTemplate && beanName.equals(mongoTemplateRef)) { + + /* + * Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(bean.getClass()); + * enhancer.setCallbacks(new MethodInterceptor[] { new + * RelMongoTemplateInvocationHandler() }); + * + * Object proxy = enhancer.create(new Class[] { MongoDbFactory.class, + * MongoConverter.class }, new Object[] { ((MongoTemplate) + * bean).getMongoDbFactory(), ((MongoTemplate) bean).getConverter() }); + * + * ((MongoTemplate) proxy).setApplicationContext(applicationContext); + * + * return proxy; + */ + + try { + Field ep = MongoTemplate.class.getDeclaredField("eventPublisher"); + ep.setAccessible(true); + + RelMongoEventPublisher eventPublisher = new RelMongoEventPublisher((MongoTemplate) bean, + (ApplicationEventPublisher) ep.get(bean)); + ep.set(bean, eventPublisher); + + Field edf = MongoTemplate.class.getDeclaredField("eventDelegate"); + edf.setAccessible(true); + + Object ed = edf.get(bean); + Method method = ed.getClass().getMethod("setPublisher", ApplicationEventPublisher.class); + method.setAccessible(true); + method.invoke(ed, eventPublisher); + + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException + | InvocationTargetException | NoSuchMethodException e) { + throw new BeanInitializationException("Fatal: failed to init the RelMongo Engine", e); + } + } + + return bean; + } + + private static final class RelMongoEventPublisher implements ApplicationEventPublisher { + + private final RelMongoProcessor relMongoProcessor; + private final MongoTemplate mongoTemplate; + private final ApplicationEventPublisher eventPublisher; + + public RelMongoEventPublisher(MongoTemplate mongoTemplate, ApplicationEventPublisher eventPublisher) { + super(); + this.relMongoProcessor = new RelMongoProcessor(); + this.mongoTemplate = mongoTemplate; + this.eventPublisher = eventPublisher; + } + + @Override + public void publishEvent(Object event) { + relMongoProcessor.onApplicationEvent((MongoMappingEvent) event, mongoTemplate); + eventPublisher.publishEvent(event); + } + + } } diff --git a/src/main/java/io/github/kaiso/relmongo/config/RelMongoMappingContext.java b/src/main/java/io/github/kaiso/relmongo/config/RelMongoMappingContext.java index c92ac3b..bd97b4d 100644 --- a/src/main/java/io/github/kaiso/relmongo/config/RelMongoMappingContext.java +++ b/src/main/java/io/github/kaiso/relmongo/config/RelMongoMappingContext.java @@ -1,5 +1,7 @@ package io.github.kaiso.relmongo.config; +import java.util.AbstractMap; + import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.FieldNamingStrategy; import org.springframework.data.mapping.model.Property; @@ -9,13 +11,12 @@ import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentProperty; import org.springframework.data.mongodb.core.mapping.CachingMongoPersistentProperty; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes; import org.springframework.data.util.TypeInformation; import org.springframework.lang.Nullable; -import java.util.AbstractMap; - /** * Default implementation of a {@link MappingContext} for MongoDB using * {@link BasicMongoPersistentEntity} and @@ -77,12 +78,12 @@ protected boolean shouldCreatePersistentEntityFor(TypeInformation type) { * org.springframework.data.mapping.SimpleTypeHolder) */ @Override - public MongoPersistentProperty createPersistentProperty(Property property, BasicMongoPersistentEntity owner, - SimpleTypeHolder simpleTypeHolder) { - return new CachingMongoPersistentProperty(property, owner, simpleTypeHolder, fieldNamingStrategy); - } + public MongoPersistentProperty createPersistentProperty(Property property, MongoPersistentEntity owner, + SimpleTypeHolder simpleTypeHolder) { + return new CachingMongoPersistentProperty(property, owner, simpleTypeHolder, fieldNamingStrategy); + } - /* + /* * (non-Javadoc) * * @see diff --git a/src/main/java/io/github/kaiso/relmongo/events/callback/PersistentPropertyConvertingCallback.java b/src/main/java/io/github/kaiso/relmongo/events/callback/PersistentPropertyConvertingCallback.java index d8c9301..1dc292b 100644 --- a/src/main/java/io/github/kaiso/relmongo/events/callback/PersistentPropertyConvertingCallback.java +++ b/src/main/java/io/github/kaiso/relmongo/events/callback/PersistentPropertyConvertingCallback.java @@ -59,13 +59,14 @@ public void doWith(Field field) throws IllegalAccessException { return; } - MappedByProcessor.processChild(source, null, field, ReflectionsUtil.getGenericType(field)); if (field.isAnnotationPresent(OneToMany.class)) { fillIdentifiers(field, field.getAnnotation(OneToMany.class).cascade()); } else if (field.isAnnotationPresent(OneToOne.class)) { fillIdentifiers(field, field.getAnnotation(OneToOne.class).cascade()); } + + MappedByProcessor.processChild(source, null, field, ReflectionsUtil.getGenericType(field)); } diff --git a/src/main/java/io/github/kaiso/relmongo/events/callback/PersistentPropertyPostSavingCallback.java b/src/main/java/io/github/kaiso/relmongo/events/callback/PersistentPropertyPostSavingCallback.java index 6313642..5825591 100644 --- a/src/main/java/io/github/kaiso/relmongo/events/callback/PersistentPropertyPostSavingCallback.java +++ b/src/main/java/io/github/kaiso/relmongo/events/callback/PersistentPropertyPostSavingCallback.java @@ -52,6 +52,7 @@ public PersistentPropertyPostSavingCallback(Object source, Class sourceClass, public void apply() { ReflectionUtils.doWithFields(sourceClass, this); cache.stream().forEach(mongoOperations::save); + cache.clear(); } public void doWith(Field field) throws IllegalAccessException { diff --git a/src/main/java/io/github/kaiso/relmongo/events/callback/PersistentPropertySavingCallback.java b/src/main/java/io/github/kaiso/relmongo/events/callback/PersistentPropertySavingCallback.java index f688af2..bf7229e 100644 --- a/src/main/java/io/github/kaiso/relmongo/events/callback/PersistentPropertySavingCallback.java +++ b/src/main/java/io/github/kaiso/relmongo/events/callback/PersistentPropertySavingCallback.java @@ -16,6 +16,18 @@ package io.github.kaiso.relmongo.events.callback; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.ReflectionUtils.FieldCallback; +import org.springframework.util.StringUtils; + import com.mongodb.BasicDBList; import io.github.kaiso.relmongo.annotation.CascadeType; @@ -27,18 +39,6 @@ import io.github.kaiso.relmongo.util.ReflectionsUtil; import io.github.kaiso.relmongo.util.RelMongoConstants; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.ReflectionUtils.FieldCallback; -import org.springframework.util.StringUtils; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - /** * * @author Kais OMRI @@ -46,99 +46,102 @@ */ public class PersistentPropertySavingCallback implements FieldCallback { - private Object source; - private MongoOperations mongoOperations; - private String collectionName; - - public PersistentPropertySavingCallback(Object source, String collectionName, MongoOperations mongoOperations) { - super(); - this.source = source; - this.mongoOperations = mongoOperations; - this.collectionName = collectionName; - } - - public void doWith(Field field) throws IllegalAccessException { - ReflectionUtils.makeAccessible(field); - if (field.isAnnotationPresent(OneToMany.class)) { - saveAssociation(field, field.getAnnotation(OneToMany.class).cascade(), field.getAnnotation(OneToMany.class).orphanRemoval()); - } else if (field.isAnnotationPresent(OneToOne.class) - && StringUtils.isEmpty(field.getAnnotation(OneToOne.class).mappedBy())) { - saveAssociation(field, field.getAnnotation(OneToOne.class).cascade(), field.getAnnotation(OneToOne.class).orphanRemoval()); - } - - } - - private void saveAssociation(Field field, CascadeType cascadeType, Boolean orphanRemoval) { - String name = AnnotationsUtils.getJoinProperty(field); - Object reference = null; - reference = ((org.bson.Document) source).get(field.getName()); - String childCollectionName = AnnotationsUtils.getCollectionName(field); - if (reference instanceof BasicDBList) { - BasicDBList list = new BasicDBList(); - list.addAll(((BasicDBList) reference).stream() - .map(dbObject -> this.keepOnlyIdentifier(dbObject, childCollectionName, cascadeType)) - .collect(Collectors.toList())); - ((org.bson.Document) source).remove(field.getName()); - ((org.bson.Document) source).put(name, list); - if (Boolean.TRUE.equals(orphanRemoval)) { - removeOrphans(((org.bson.Document) source).get("_id"), - list.parallelStream().map(o -> ((org.bson.Document) o).get("_id")).collect(Collectors.toList()), - name, - field); - } - } else if (reference instanceof org.bson.Document) { - ((org.bson.Document) source).remove(field.getName()); - org.bson.Document child = this.keepOnlyIdentifier(reference, childCollectionName, cascadeType); - ((org.bson.Document) source).put(name, child); - if (Boolean.TRUE.equals(orphanRemoval)) { - removeOrphans(((org.bson.Document) source).get("_id"), Arrays.asList(child.get("_id")), name, field); - } - } else if (reference == null && Boolean.TRUE.equals(orphanRemoval)) { - removeOrphans(((org.bson.Document) source).get("_id"), Collections.emptyList(), name, field); - } - - } - - private org.bson.Document keepOnlyIdentifier(Object obj, String collection, CascadeType cascadeType) { - Object objectId = ((org.bson.Document) obj).get("_id"); - if (objectId == null && !Arrays.asList(CascadeType.PERSIST, CascadeType.ALL).contains(cascadeType)) { - throw new RelMongoProcessingException( - "The entity Id must not be null when persisting without cascade ALL or PERSIST "); - } - return new org.bson.Document().append("_id", objectId).append(RelMongoConstants.RELMONGOTARGET_PROPERTY_NAME, - collection); - } - - - @SuppressWarnings({ "unchecked" }) - private void removeOrphans(Object parentId, List child, String propertyName, Field field) { - Class childClass = ReflectionsUtil.getGenericType(field); - BasicDBList result = DatabaseOperations.getDocumentsById(mongoOperations, Arrays.asList(parentId), collectionName); - if (result == null || result.isEmpty()) { - return; - } - org.bson.Document currentDocument = (org.bson.Document) result.get(0); - if (currentDocument != null) { - Object currentChild = currentDocument.get(propertyName); - List objectsToRemove = null; - if (currentChild instanceof ArrayList) { - objectsToRemove = new ArrayList<>(); - ArrayList childList = (ArrayList) currentChild; - childList.removeIf(o -> child.contains(o.get("_id"))); - objectsToRemove.addAll(childList.parallelStream().map(o -> o.get("_id")) - .collect(Collectors.toList())); - } else if (currentChild instanceof org.bson.Document) { - Object currentChildId = ((org.bson.Document) currentChild).get("_id"); - if (child.isEmpty() || !currentChildId.equals(child.get(0))) { - objectsToRemove = new ArrayList<>(); - objectsToRemove.add(currentChildId); - } - } - - if (objectsToRemove != null && !objectsToRemove.isEmpty()) { - DatabaseOperations.removeObjectsByIds(mongoOperations, childClass, objectsToRemove); - } - } - } + private Object source; + private Object document; + private MongoOperations mongoOperations; + private String collectionName; + + public PersistentPropertySavingCallback(Object source, Object document, String collectionName, + MongoOperations mongoOperations) { + super(); + this.document = document; + this.source = source; + this.mongoOperations = mongoOperations; + this.collectionName = collectionName; + } + + public void doWith(Field field) throws IllegalAccessException { + ReflectionUtils.makeAccessible(field); + if (field.isAnnotationPresent(OneToMany.class)) { + saveAssociation(field, field.getAnnotation(OneToMany.class).cascade(), + field.getAnnotation(OneToMany.class).orphanRemoval()); + } else if (field.isAnnotationPresent(OneToOne.class) + && !StringUtils.hasText(field.getAnnotation(OneToOne.class).mappedBy())) { + saveAssociation(field, field.getAnnotation(OneToOne.class).cascade(), + field.getAnnotation(OneToOne.class).orphanRemoval()); + } + + } + + private void saveAssociation(Field field, CascadeType cascadeType, Boolean orphanRemoval) { + String name = AnnotationsUtils.getJoinProperty(field); + Object reference = null; + reference = ((org.bson.Document) document).get(field.getName()); + String childCollectionName = AnnotationsUtils.getCollectionName(field); + if (reference instanceof ArrayList) { + List list = new ArrayList<>(); + list.addAll(((List) reference).stream() + .map(dbObject -> this.keepOnlyIdentifier(dbObject, childCollectionName, cascadeType)) + .collect(Collectors.toList())); + ((org.bson.Document) document).remove(field.getName()); + ((org.bson.Document) document).put(name, list); + if (Boolean.TRUE.equals(orphanRemoval)) { + removeOrphans(((org.bson.Document) document).get("_id"), + list.parallelStream().map(o -> ((org.bson.Document) o).get("_id")).collect(Collectors.toList()), + name, field); + } + } else if (reference instanceof org.bson.Document) { + ((org.bson.Document) document).remove(field.getName()); + org.bson.Document child = this.keepOnlyIdentifier(reference, childCollectionName, cascadeType); + ((org.bson.Document) document).put(name, child); + if (Boolean.TRUE.equals(orphanRemoval)) { + removeOrphans(((org.bson.Document) document).get("_id"), Arrays.asList(child.get("_id")), name, field); + } + } else if (reference == null && Boolean.TRUE.equals(orphanRemoval)) { + removeOrphans(((org.bson.Document) document).get("_id"), Collections.emptyList(), name, field); + } + + } + + private org.bson.Document keepOnlyIdentifier(Object obj, String collection, CascadeType cascadeType) { + Object objectId = ((org.bson.Document) obj).get("_id"); + if (objectId == null && !Arrays.asList(CascadeType.PERSIST, CascadeType.ALL).contains(cascadeType)) { + throw new RelMongoProcessingException( + "The entity Id must not be null when persisting without cascade ALL or PERSIST "); + } + return new org.bson.Document().append("_id", objectId).append(RelMongoConstants.RELMONGOTARGET_PROPERTY_NAME, + collection); + } + + @SuppressWarnings({ "unchecked" }) + private void removeOrphans(Object parentId, List child, String propertyName, Field field) { + Class childClass = ReflectionsUtil.getGenericType(field); + BasicDBList result = DatabaseOperations.getDocumentsById(mongoOperations, Arrays.asList(parentId), + collectionName); + if (result == null || result.isEmpty()) { + return; + } + org.bson.Document currentDocument = (org.bson.Document) result.get(0); + if (currentDocument != null) { + Object currentChild = currentDocument.get(propertyName); + List objectsToRemove = null; + if (currentChild instanceof ArrayList) { + objectsToRemove = new ArrayList<>(); + ArrayList childList = (ArrayList) currentChild; + childList.removeIf(o -> child.contains(o.get("_id"))); + objectsToRemove.addAll(childList.parallelStream().map(o -> o.get("_id")).collect(Collectors.toList())); + } else if (currentChild instanceof org.bson.Document) { + Object currentChildId = ((org.bson.Document) currentChild).get("_id"); + if (child.isEmpty() || !currentChildId.equals(child.get(0))) { + objectsToRemove = new ArrayList<>(); + objectsToRemove.add(currentChildId); + } + } + + if (objectsToRemove != null && !objectsToRemove.isEmpty()) { + DatabaseOperations.removeObjectsByIds(mongoOperations, childClass, objectsToRemove); + } + } + } } diff --git a/src/main/java/io/github/kaiso/relmongo/events/processor/MappedByProcessor.java b/src/main/java/io/github/kaiso/relmongo/events/processor/MappedByProcessor.java index e50ff23..3749c85 100644 --- a/src/main/java/io/github/kaiso/relmongo/events/processor/MappedByProcessor.java +++ b/src/main/java/io/github/kaiso/relmongo/events/processor/MappedByProcessor.java @@ -42,11 +42,11 @@ public static void processChild(Object parent, Object value, Field targetField, ReflectionUtils.doWithFields(fieldType, new FieldCallback() { @Override public void doWith(Field field) throws IllegalAccessException { - ReflectionUtils.makeAccessible(field); if (!ReflectionsUtil.getGenericType(field).equals(parent.getClass())) { return; } + ReflectionUtils.makeAccessible(field); MappedByMetadata mappedByInfos = AnnotationsUtils.getMappedByInfos(field); Object target = targetField.get(parent); diff --git a/src/main/java/io/github/kaiso/relmongo/events/processor/RelMongoProcessor.java b/src/main/java/io/github/kaiso/relmongo/events/processor/RelMongoProcessor.java index bc8cb14..c67f02c 100644 --- a/src/main/java/io/github/kaiso/relmongo/events/processor/RelMongoProcessor.java +++ b/src/main/java/io/github/kaiso/relmongo/events/processor/RelMongoProcessor.java @@ -45,103 +45,104 @@ */ public class RelMongoProcessor { - private static final Logger logger = LoggerFactory.getLogger(RelMongoProcessor.class); - - @SuppressWarnings("unchecked") - public void onApplicationEvent(MongoMappingEvent event, MongoTemplate mongoTemplate) { - try { - - if (event instanceof AfterLoadEvent) { - - onAfterLoad((AfterLoadEvent) event, mongoTemplate); - - return; - } - - if (event instanceof AbstractDeleteEvent) { - - Class eventDomainType = ((AbstractDeleteEvent) event).getType(); - - if (eventDomainType != null) { - if (event instanceof BeforeDeleteEvent) { - onBeforeDelete((BeforeDeleteEvent) event, mongoTemplate); - } - /* - * if (event instanceof AfterDeleteEvent) { - * onAfterDelete((AfterDeleteEvent) event); - * } - */ - } - - return; - - } - - if (event instanceof BeforeConvertEvent) { - onBeforeConvert((BeforeConvertEvent) event, mongoTemplate); - } else if (event instanceof BeforeSaveEvent) { - onBeforeSave((BeforeSaveEvent) event, mongoTemplate); - } else if (event instanceof AfterSaveEvent) { - onAfterSave((AfterSaveEvent) event, mongoTemplate); - } else if (event instanceof AfterConvertEvent) { - onAfterConvert((AfterConvertEvent) event, mongoTemplate); - } - - } catch (Exception e) { - logger.warn("Failed to process MappingEvent " + event.getClass().getSimpleName(), e); - } - } - - public void onAfterLoad(AfterLoadEvent event, MongoTemplate template) { - // if (event.getType().isAnnotationPresent(Document.class)) { - // PersistentPropertyLoadingCallback callback = new - // PersistentPropertyLoadingCallback(event.getSource()); - // ReflectionUtils.doWithFields(event.getType(), callback); - // List loadableObjects = - // callback.getLoadableObjects(); - // if (!loadableObjects.isEmpty()) { - // PersistentRelationResolver.resolveOnLoading(mongoOperations, loadableObjects, - // event.getSource()); - // } - // } - } - - public void onBeforeSave(BeforeSaveEvent event, MongoTemplate mongoTemplate) { - if (event.getSource().getClass().isAnnotationPresent(Document.class)) { - PersistentPropertySavingCallback callback = new PersistentPropertySavingCallback(event.getDocument(), event.getCollectionName(), mongoTemplate); - ReflectionUtils.doWithFields(event.getSource().getClass(), callback); - } - } - - public void onBeforeConvert(BeforeConvertEvent event, MongoTemplate mongoTemplate) { - if (event.getSource().getClass().isAnnotationPresent(Document.class)) { - PersistentPropertyConvertingCallback callback = new PersistentPropertyConvertingCallback(event.getSource()); - ReflectionUtils.doWithFields(event.getSource().getClass(), callback); - } - } - - public void onAfterConvert(AfterConvertEvent event, MongoTemplate mongoTemplate) { - if (event.getSource().getClass().isAnnotationPresent(Document.class)) { - PersistentPropertyPostLoadingCallback callback = new PersistentPropertyPostLoadingCallback(event.getSource(), event.getDocument(), mongoTemplate); - ReflectionUtils.doWithFields(event.getSource().getClass(), callback); - } - } - - public void onAfterSave(AfterSaveEvent event, MongoTemplate mongoTemplate) { - if (event.getSource().getClass().isAnnotationPresent(Document.class)) { - new PersistentPropertyPostSavingCallback(event.getSource(), event.getSource().getClass(), mongoTemplate) - .apply(); - - } - - } - - public void onBeforeDelete(BeforeDeleteEvent event, MongoTemplate mongoTemplate) { - if (event.getType().isAnnotationPresent(Document.class) && !event.getSource().isEmpty()) { - PersistentPropertyCascadingRemoveCallback callback = new PersistentPropertyCascadingRemoveCallback(event.getDocument(), mongoTemplate, - event.getType(), event.getCollectionName()); - callback.doProcessing(); - } - } + private static final Logger logger = LoggerFactory.getLogger(RelMongoProcessor.class); + + @SuppressWarnings("unchecked") + public void onApplicationEvent(MongoMappingEvent event, MongoTemplate mongoTemplate) { + try { + + if (event instanceof AfterLoadEvent) { + + onAfterLoad((AfterLoadEvent) event, mongoTemplate); + + return; + } + + if (event instanceof AbstractDeleteEvent) { + + Class eventDomainType = ((AbstractDeleteEvent) event).getType(); + + if (eventDomainType != null) { + if (event instanceof BeforeDeleteEvent) { + onBeforeDelete((BeforeDeleteEvent) event, mongoTemplate); + } + /* + * if (event instanceof AfterDeleteEvent) { + * onAfterDelete((AfterDeleteEvent) event); } + */ + } + + return; + + } + + if (event instanceof BeforeConvertEvent) { + onBeforeConvert((BeforeConvertEvent) event, mongoTemplate); + } else if (event instanceof BeforeSaveEvent) { + onBeforeSave((BeforeSaveEvent) event, mongoTemplate); + } else if (event instanceof AfterSaveEvent) { + onAfterSave((AfterSaveEvent) event, mongoTemplate); + } else if (event instanceof AfterConvertEvent) { + onAfterConvert((AfterConvertEvent) event, mongoTemplate); + } + + } catch (Exception e) { + logger.warn("Failed to process MappingEvent " + event.getClass().getSimpleName(), e); + } + } + + public void onAfterLoad(AfterLoadEvent event, MongoTemplate mongoTemplate) { + // if (event.getType().isAnnotationPresent(Document.class)) { + // PersistentPropertyLoadingCallback callback = new + // PersistentPropertyLoadingCallback(event.getSource()); + // ReflectionUtils.doWithFields(event.getType(), callback); + // List loadableObjects = + // callback.getLoadableObjects(); + // if (!loadableObjects.isEmpty()) { + // PersistentRelationResolver.resolveOnLoading(mongoOperations, loadableObjects, + // event.getSource()); + // } + // } + } + + public void onBeforeSave(BeforeSaveEvent event, MongoTemplate mongoTemplate) { + if (event.getSource().getClass().isAnnotationPresent(Document.class)) { + PersistentPropertySavingCallback callback = new PersistentPropertySavingCallback(event.getSource(), + event.getDocument(), event.getCollectionName(), mongoTemplate); + ReflectionUtils.doWithFields(event.getSource().getClass(), callback); + } + } + + public void onBeforeConvert(BeforeConvertEvent event, MongoTemplate mongoTemplate) { + if (event.getSource().getClass().isAnnotationPresent(Document.class)) { + PersistentPropertyConvertingCallback callback = new PersistentPropertyConvertingCallback(event.getSource()); + ReflectionUtils.doWithFields(event.getSource().getClass(), callback); + } + } + + public void onAfterConvert(AfterConvertEvent event, MongoTemplate mongoTemplate) { + if (event.getSource().getClass().isAnnotationPresent(Document.class)) { + PersistentPropertyPostLoadingCallback callback = new PersistentPropertyPostLoadingCallback( + event.getSource(), event.getDocument(), mongoTemplate); + ReflectionUtils.doWithFields(event.getSource().getClass(), callback); + } + } + + public void onAfterSave(AfterSaveEvent event, MongoTemplate mongoTemplate) { + if (event.getSource().getClass().isAnnotationPresent(Document.class)) { + new PersistentPropertyPostSavingCallback(event.getSource(), event.getSource().getClass(), mongoTemplate) + .apply(); + + } + + } + + public void onBeforeDelete(BeforeDeleteEvent event, MongoTemplate mongoTemplate) { + if (event.getType().isAnnotationPresent(Document.class) && !event.getSource().isEmpty()) { + PersistentPropertyCascadingRemoveCallback callback = new PersistentPropertyCascadingRemoveCallback( + event.getDocument(), mongoTemplate, event.getType(), event.getCollectionName()); + callback.doProcessing(); + } + } } diff --git a/src/main/java/io/github/kaiso/relmongo/mongo/PersistentRelationResolver.java b/src/main/java/io/github/kaiso/relmongo/mongo/PersistentRelationResolver.java index 796e34c..a439561 100644 --- a/src/main/java/io/github/kaiso/relmongo/mongo/PersistentRelationResolver.java +++ b/src/main/java/io/github/kaiso/relmongo/mongo/PersistentRelationResolver.java @@ -16,17 +16,18 @@ package io.github.kaiso.relmongo.mongo; -import io.github.kaiso.relmongo.lazy.LazyLoadingProxy; -import io.github.kaiso.relmongo.lazy.RelMongoLazyLoader; +import java.util.Collection; +import java.util.List; +import org.springframework.cglib.proxy.Callback; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.Factory; import org.springframework.cglib.proxy.NoOp; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.objenesis.ObjenesisStd; -import java.util.Collection; -import java.util.List; +import io.github.kaiso.relmongo.lazy.LazyLoadingProxy; +import io.github.kaiso.relmongo.lazy.RelMongoLazyLoader; public final class PersistentRelationResolver { @@ -44,11 +45,13 @@ public static Object lazyLoader(Class type, MongoOperations mongoOperations, } RelMongoLazyLoader lazyLoader = new RelMongoLazyLoader(ids, property, mongoOperations, targetClass, type, fieldName, original, parent); - enhancer.setCallback(lazyLoader); + enhancer.setInterfaces(new Class[] { LazyLoadingProxy.class, type.isInterface() ? type : NoOp.class }); - enhancer.create(); + enhancer.setAttemptLoad(true); + enhancer.setCallbackType(RelMongoLazyLoader.class); @SuppressWarnings("unchecked") - Factory factory = (Factory) objenesisStd.newInstance(enhancer.createClass()); + Factory factory = (Factory) objenesisStd.newInstance(enhancer.createClass()); + factory.setCallbacks(new Callback[] { lazyLoader }); return factory.newInstance(lazyLoader); } diff --git a/src/test/java/io/github/kaiso/relmongo/config/TestContextConfiguration.java b/src/test/java/io/github/kaiso/relmongo/config/TestContextConfiguration.java index 916f576..c7b5f5e 100644 --- a/src/test/java/io/github/kaiso/relmongo/config/TestContextConfiguration.java +++ b/src/test/java/io/github/kaiso/relmongo/config/TestContextConfiguration.java @@ -1,17 +1,15 @@ package io.github.kaiso.relmongo.config; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; +import java.lang.annotation.Annotation; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; -import de.flapdoodle.embed.mongo.MongodExecutable; -import de.flapdoodle.embed.mongo.MongodProcess; -import de.flapdoodle.embed.mongo.MongodStarter; -import de.flapdoodle.embed.mongo.config.IMongoCmdOptions; -import de.flapdoodle.embed.mongo.config.MongoCmdOptionsBuilder; -import de.flapdoodle.embed.mongo.config.MongodConfigBuilder; -import de.flapdoodle.embed.mongo.config.Net; -import de.flapdoodle.embed.mongo.distribution.Version; -import de.flapdoodle.embed.process.runtime.Network; +import javax.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,9 +22,9 @@ import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.data.annotation.Persistent; import org.springframework.data.convert.CustomConversions; -import org.springframework.data.mongodb.MongoDbFactory; +import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.SimpleMongoClientDbFactory; +import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; import org.springframework.data.mongodb.core.convert.DbRefResolver; import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; @@ -37,16 +35,18 @@ import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; -import javax.annotation.PostConstruct; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; -import java.lang.annotation.Annotation; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; +import de.flapdoodle.embed.mongo.MongodExecutable; +import de.flapdoodle.embed.mongo.MongodProcess; +import de.flapdoodle.embed.mongo.MongodStarter; +import de.flapdoodle.embed.mongo.config.IMongoCmdOptions; +import de.flapdoodle.embed.mongo.config.MongoCmdOptionsBuilder; +import de.flapdoodle.embed.mongo.config.MongodConfigBuilder; +import de.flapdoodle.embed.mongo.config.Net; +import de.flapdoodle.embed.mongo.distribution.Version; +import de.flapdoodle.embed.process.runtime.Network; public class TestContextConfiguration { @@ -128,12 +128,12 @@ public MappingMongoConverter mappingMongoConverter(MongoMappingContext mongoMapp } @Bean - public MongoDbFactory mongoDbFactory() { - return new SimpleMongoClientDbFactory(mongoClient(), getDatabaseName()); + public MongoDatabaseFactory mongoDbFactory() { + return new SimpleMongoClientDatabaseFactory(mongoClient(), getDatabaseName()); } @Bean - public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory, MappingMongoConverter mappingMongoConverter) throws Exception { + public MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDbFactory, MappingMongoConverter mappingMongoConverter) throws Exception { return new MongoTemplate(mongoDbFactory, mappingMongoConverter); } diff --git a/src/test/java/io/github/kaiso/relmongo/tests/MultipleMongoTemplateRefTest.java b/src/test/java/io/github/kaiso/relmongo/tests/MultipleMongoTemplateRefTest.java index f7e5b84..7553bf4 100644 --- a/src/test/java/io/github/kaiso/relmongo/tests/MultipleMongoTemplateRefTest.java +++ b/src/test/java/io/github/kaiso/relmongo/tests/MultipleMongoTemplateRefTest.java @@ -29,11 +29,11 @@ import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.FilterType; import org.springframework.data.annotation.Persistent; -import org.springframework.data.mongodb.MongoDbFactory; +import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.config.EnableMongoAuditing; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.SimpleMongoClientDbFactory; +import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; import org.springframework.data.mongodb.core.convert.DbRefResolver; import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; @@ -78,7 +78,7 @@ public static final class SecondTemplateRefTestConfiguration { @Bean public MongoTemplate secondMongoTemplate(MongoClient mongoClient, ApplicationContext applicationContext) throws Exception { - MongoDbFactory mongoDbFactory = new SimpleMongoClientDbFactory(mongoClient, "secondTest"); + MongoDatabaseFactory mongoDbFactory = new SimpleMongoClientDatabaseFactory(mongoClient, "secondTest"); DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory); MongoCustomConversions conversions = new MongoCustomConversions(Collections.singletonList(new RMLocalDateTimeToDateConverter())); diff --git a/src/test/java/io/github/kaiso/relmongo/tests/TransactionsTest.java b/src/test/java/io/github/kaiso/relmongo/tests/TransactionsTest.java index 5ccad74..c3159c3 100644 --- a/src/test/java/io/github/kaiso/relmongo/tests/TransactionsTest.java +++ b/src/test/java/io/github/kaiso/relmongo/tests/TransactionsTest.java @@ -1,14 +1,5 @@ package io.github.kaiso.relmongo.tests; -import io.github.kaiso.relmongo.data.model.Car; -import io.github.kaiso.relmongo.data.model.DrivingLicense; -import io.github.kaiso.relmongo.data.model.House; -import io.github.kaiso.relmongo.data.model.Passport; -import io.github.kaiso.relmongo.data.model.Person; -import io.github.kaiso.relmongo.data.model.State; -import io.github.kaiso.relmongo.data.repository.PersonRepository; -import io.github.kaiso.relmongo.tests.common.AbstractBaseTest; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -17,7 +8,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.data.mongodb.MongoDbFactory; +import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.MongoTransactionException; import org.springframework.data.mongodb.MongoTransactionManager; import org.springframework.data.mongodb.core.MongoOperations; @@ -27,6 +18,15 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +import io.github.kaiso.relmongo.data.model.Car; +import io.github.kaiso.relmongo.data.model.DrivingLicense; +import io.github.kaiso.relmongo.data.model.House; +import io.github.kaiso.relmongo.data.model.Passport; +import io.github.kaiso.relmongo.data.model.Person; +import io.github.kaiso.relmongo.data.model.State; +import io.github.kaiso.relmongo.data.repository.PersonRepository; +import io.github.kaiso.relmongo.tests.common.AbstractBaseTest; + @ContextConfiguration(classes = { TransactionsTest.class }) public class TransactionsTest extends AbstractBaseTest { @@ -75,7 +75,7 @@ public void savePersonAndDropCollection(Person person) { public static class TransactionConfig { @Bean - MongoTransactionManager transactionManager(MongoDbFactory dbFactory) { + MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) { return new MongoTransactionManager(dbFactory); } From 3f1f7c1e3d62e2c19a005fbc29d52655d636af80 Mon Sep 17 00:00:00 2001 From: kaiso Date: Thu, 21 Mar 2024 01:29:31 +0100 Subject: [PATCH 2/2] [release] 4.0.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2218870..b03dd12 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.github.kaiso.relmongo relmongo - 4.0.0-RC1 + 4.0.0 jar relmongo