From 18c294a1ee638581b7c2726c6661fd7348499f6e Mon Sep 17 00:00:00 2001 From: Kais OMRI Date: Sun, 24 Nov 2019 21:52:20 +0100 Subject: [PATCH] [B#44] E11000 duplicate key error collection --- pom.xml | 31 +-- .../kaiso/relmongo/annotation/ManyToOne.java | 3 + .../kaiso/relmongo/annotation/OneToMany.java | 3 + .../kaiso/relmongo/annotation/OneToOne.java | 3 + .../config/PersistenceConfiguration.java | 8 +- .../events/processor/RelMongoProcessor.java | 7 +- .../relmongo/mongo/DatabaseOperations.java | 6 +- .../config/TestContextConfiguration.java | 216 +++++++++++++++++ .../github/kaiso/relmongo/data/model/Car.java | 30 ++- .../kaiso/relmongo/data/model/House.java | 1 - .../data/repository/AddressRepository.java | 13 + .../data/repository/PersonRepository.java | 4 +- .../kaiso/relmongo/tests/AggregationTest.java | 47 ++++ .../kaiso/relmongo/tests/AuditingTest.java | 61 +++++ .../kaiso/relmongo/tests/CascadingTest.java | 27 +-- .../kaiso/relmongo/tests/MappedByTest.java | 19 +- .../relmongo/tests/OrphanRemovalTest.java | 15 +- .../relmongo/tests/PersonRepositoryTest.java | 76 +++--- .../tests/common/AbstractBaseTest.java | 224 +++--------------- 19 files changed, 481 insertions(+), 313 deletions(-) create mode 100644 src/test/java/io/github/kaiso/relmongo/config/TestContextConfiguration.java create mode 100644 src/test/java/io/github/kaiso/relmongo/data/repository/AddressRepository.java create mode 100644 src/test/java/io/github/kaiso/relmongo/tests/AggregationTest.java create mode 100644 src/test/java/io/github/kaiso/relmongo/tests/AuditingTest.java diff --git a/pom.xml b/pom.xml index b526eba..268213c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.github.kaiso.relmongo relmongo - 3.0.1 + 3.1.0 jar relmongo @@ -18,9 +18,9 @@ 1.8 3.0.1 3.0.1 - 2.1.8.RELEASE - 5.1.7.RELEASE - 3.9.1 + 2.2.1.RELEASE + 5.2.1.RELEASE + 3.11.2 5.4.1 1.4.2 @@ -61,21 +61,17 @@ org.springframework.data spring-data-mongodb - - - org.mongodb - mongo-java-driver - - ${spring.data.version} provided + org.springframework spring-beans ${spring.version} provided + org.springframework spring-core @@ -83,20 +79,13 @@ provided - - org.mongodb - mongodb-driver - ${mongo.driver.version} - provided - - org.mongodb - mongo-java-driver - ${mongo.driver.version} + org.mongodb + mongodb-driver-async + ${mongo.driver.version} - org.junit.jupiter @@ -115,7 +104,7 @@ de.flapdoodle.embed de.flapdoodle.embed.mongo - 2.1.1 + 2.2.0 test diff --git a/src/main/java/io/github/kaiso/relmongo/annotation/ManyToOne.java b/src/main/java/io/github/kaiso/relmongo/annotation/ManyToOne.java index cccd4f7..93ef1c1 100644 --- a/src/main/java/io/github/kaiso/relmongo/annotation/ManyToOne.java +++ b/src/main/java/io/github/kaiso/relmongo/annotation/ManyToOne.java @@ -16,6 +16,8 @@ package io.github.kaiso.relmongo.annotation; +import org.springframework.data.annotation.Reference; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -30,6 +32,7 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) +@Reference public @interface ManyToOne { /** * (Optional) Whether the association should be lazily loaded or must be eagerly diff --git a/src/main/java/io/github/kaiso/relmongo/annotation/OneToMany.java b/src/main/java/io/github/kaiso/relmongo/annotation/OneToMany.java index 17ade36..281686b 100644 --- a/src/main/java/io/github/kaiso/relmongo/annotation/OneToMany.java +++ b/src/main/java/io/github/kaiso/relmongo/annotation/OneToMany.java @@ -16,6 +16,8 @@ package io.github.kaiso.relmongo.annotation; +import org.springframework.data.annotation.Reference; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -30,6 +32,7 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) +@Reference public @interface OneToMany { /** diff --git a/src/main/java/io/github/kaiso/relmongo/annotation/OneToOne.java b/src/main/java/io/github/kaiso/relmongo/annotation/OneToOne.java index 1c701a2..c60fe8e 100644 --- a/src/main/java/io/github/kaiso/relmongo/annotation/OneToOne.java +++ b/src/main/java/io/github/kaiso/relmongo/annotation/OneToOne.java @@ -16,6 +16,8 @@ package io.github.kaiso.relmongo.annotation; +import org.springframework.data.annotation.Reference; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -30,6 +32,7 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) +@Reference public @interface OneToOne { /** * (Optional) Whether the association should be lazily loaded or must be eagerly diff --git a/src/main/java/io/github/kaiso/relmongo/config/PersistenceConfiguration.java b/src/main/java/io/github/kaiso/relmongo/config/PersistenceConfiguration.java index cd6917d..db317f3 100644 --- a/src/main/java/io/github/kaiso/relmongo/config/PersistenceConfiguration.java +++ b/src/main/java/io/github/kaiso/relmongo/config/PersistenceConfiguration.java @@ -21,17 +21,19 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.core.MongoOperations; + /** * - * @author Kais OMRI + * @author Kais OMRI (kaiso) * */ @Configuration public class PersistenceConfiguration { @Bean - public RelMongoProcessor mongoEventListener() { - return new RelMongoProcessor(); + public RelMongoProcessor mongoEventListener(MongoOperations mongoOperations) { + return new RelMongoProcessor(mongoOperations); } } 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 ff09e5c..cab8b3b 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 @@ -25,7 +25,6 @@ import io.github.kaiso.relmongo.model.LoadableObjectsMetadata; import io.github.kaiso.relmongo.mongo.PersistentRelationResolver; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener; @@ -46,8 +45,12 @@ */ public class RelMongoProcessor extends AbstractMongoEventListener { - @Autowired private MongoOperations mongoOperations; + + public RelMongoProcessor(MongoOperations mongoOperations) { + super(); + this.mongoOperations = mongoOperations; + } @Override public void onAfterLoad(AfterLoadEvent event) { diff --git a/src/main/java/io/github/kaiso/relmongo/mongo/DatabaseOperations.java b/src/main/java/io/github/kaiso/relmongo/mongo/DatabaseOperations.java index 28c28f4..9fec8ea 100644 --- a/src/main/java/io/github/kaiso/relmongo/mongo/DatabaseOperations.java +++ b/src/main/java/io/github/kaiso/relmongo/mongo/DatabaseOperations.java @@ -72,9 +72,9 @@ public static Collection findByIds(MongoOperations mongoOperations, Class } public static T findByPropertyValue(MongoOperations mongoOperations, Class clazz, String propertyName, Object value) { - Query query = new Query(); - query.addCriteria(Criteria.where(propertyName).is(value)); - return mongoOperations.findOne(query, clazz); + BasicDBObject query = new BasicDBObject(propertyName, new BasicDBObject("$eq", value)); + FindIterable result = mongoOperations.getCollection(mongoOperations.getCollectionName(clazz)).find(query).limit(1); + return result.iterator().hasNext() ? mongoOperations.getConverter().read(clazz,result.iterator().next()) : null; } public static void saveObjects(MongoOperations mongoOperations, Object obj) { diff --git a/src/test/java/io/github/kaiso/relmongo/config/TestContextConfiguration.java b/src/test/java/io/github/kaiso/relmongo/config/TestContextConfiguration.java new file mode 100644 index 0000000..ef1cd3c --- /dev/null +++ b/src/test/java/io/github/kaiso/relmongo/config/TestContextConfiguration.java @@ -0,0 +1,216 @@ +package io.github.kaiso.relmongo.config; + +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; + +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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Primary; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.data.annotation.Persistent; +import org.springframework.data.convert.CustomConversions; +import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; +import org.springframework.data.mongodb.config.EnableMongoAuditing; +import org.springframework.data.mongodb.core.convert.DbRefResolver; +import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; +import org.springframework.data.mongodb.core.convert.MappingMongoConverter; +import org.springframework.data.mongodb.core.convert.MongoCustomConversions; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; + +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.List; +import java.util.Set; + +@EnableRelMongo +@EnableMongoAuditing +@EnableMongoRepositories(basePackages = { "io.github.kaiso.relmongo.data.repository.impl", "io.github.kaiso.relmongo.data.repository" }) +@ComponentScan(basePackages = { "io.github.kaiso.relmongo.tests" }) +public class TestContextConfiguration extends AbstractMongoClientConfiguration { + + private static final Logger logger = LoggerFactory.getLogger(TestContextConfiguration.class); + + /** + * please store Starter or RuntimeConfig in a static final field + * if you want to use artifact store caching (or else disable caching) + */ + private static final MongodStarter starter = MongodStarter.getDefaultInstance(); + + private static MongodExecutable _mongodExe; + private static MongodProcess _mongod; + @Autowired + private ApplicationContext applicationContext; + private static MongoClient _mongo; + + @PostConstruct + public void init() { + try { + synchronized (this) { + logger.info("Attempt to start MongoDB process..."); + if (_mongod == null || !_mongod.isProcessRunning()) { + logger.info("Starting MongoDB process..."); + IMongoCmdOptions cmdOptions = new MongoCmdOptionsBuilder().verbose(true).build(); + _mongodExe = starter.prepare(new MongodConfigBuilder().version(Version.Main.PRODUCTION) + .net(new Net("localhost", 55777, Network.localhostIsIPv6())).cmdOptions(cmdOptions ).build()); + _mongod = _mongodExe.start(); + + _mongo = MongoClients.create("mongodb://localhost:55777"); + logger.info("MongoDB started"); + } + } + } catch (Exception e) { + logger.error("failed to start MongoDB ", e); + } + // _mongo = new MongoClient(Arrays.asList(new ServerAddress("127.0.0.1", + // 30001),new ServerAddress("127.0.0.1", 30002),new ServerAddress("127.0.0.1", + // 30003))); + } + + @Override + protected void finalize() throws Throwable { + logger.info("Finalizing {}", getClass()); + logger.info("Stopping MongoDB process..."); + _mongod.stop(); + _mongodExe.stop(); + logger.info("MongoDB process stopped"); + super.finalize(); + } + + @Bean + @Override + public MongoClient mongoClient() { + return _mongo; + } + + @Bean + @Primary + @Override + public MongoMappingContext mongoMappingContext() throws ClassNotFoundException { + MongoMappingContext context = new MongoMappingContext(); + context.setInitialEntitySet(new EntityScanner(applicationContext) + .scan(Document.class, Persistent.class)); + context.setSimpleTypeHolder(customConversions().getSimpleTypeHolder()); + context.afterPropertiesSet(); + return context; + } + + @Bean + @Primary + @Override + public CustomConversions customConversions() { + return new MongoCustomConversions(Collections.singletonList(new RMLocalDateTimeToDateConverter())); + } + + @Bean + @Override + public MappingMongoConverter mappingMongoConverter() throws Exception { + DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory()); + MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext()); + converter.setCustomConversions(customConversions()); + converter.afterPropertiesSet(); + return converter; + } + + @Override + public String getDatabaseName() { + return "test"; + } + + @Override + public Collection getMappingBasePackages() { + return Collections.singleton("io.github.kaiso.relmongo.data"); + } + + public class EntityScanner { + + private final ApplicationContext context; + + /** + * Create a new {@link EntityScanner} instance. + * + * @param context + * the source application context + */ + public EntityScanner(ApplicationContext context) { + Assert.notNull(context, "Context must not be null"); + this.context = context; + } + + /** + * Scan for entities with the specified annotations. + * + * @param annotationTypes + * the annotation types used on the entities + * @return a set of entity classes + * @throws ClassNotFoundException + * if an entity class cannot be loaded + */ + @SafeVarargs + public final Set> scan(Class... annotationTypes) + throws ClassNotFoundException { + List packages = getPackages(); + if (packages.isEmpty()) { + return Collections.emptySet(); + } + ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider( + false); + scanner.setEnvironment(this.context.getEnvironment()); + scanner.setResourceLoader(this.context); + for (Class annotationType : annotationTypes) { + scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType)); + } + Set> entitySet = new HashSet<>(); + for (String basePackage : packages) { + if (StringUtils.hasText(basePackage)) { + for (BeanDefinition candidate : scanner + .findCandidateComponents(basePackage)) { + entitySet.add(ClassUtils.forName(candidate.getBeanClassName(), + this.context.getClassLoader())); + } + } + } + return entitySet; + } + + private List getPackages() { + return Collections.singletonList("io.github.kaiso.relmongo.data"); + } + + } + + public class RMLocalDateTimeToDateConverter implements Converter { + public Date convert(LocalDateTime source) { + return Date.from(source.atZone(ZoneId.systemDefault()).toInstant()); + } + } + +} diff --git a/src/test/java/io/github/kaiso/relmongo/data/model/Car.java b/src/test/java/io/github/kaiso/relmongo/data/model/Car.java index 17aa3ef..660a0f7 100644 --- a/src/test/java/io/github/kaiso/relmongo/data/model/Car.java +++ b/src/test/java/io/github/kaiso/relmongo/data/model/Car.java @@ -4,6 +4,7 @@ import org.bson.types.ObjectId; import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; @Document @@ -14,9 +15,24 @@ public class Car { private String manufacturer; private Color color; + @Indexed(unique = true) + private int identifier; + @ManyToOne(mappedBy = "cars") private Person owner; + + + public Car(int identifier) { + super(); + this.identifier = identifier; + } + + public Car() { + super(); + } + + public ObjectId getId() { return id; } @@ -49,12 +65,20 @@ public void setOwner(Person owner) { this.owner = owner; } + public int getIdentifier() { + return identifier; + } + + public void setIdentifier(int identifier) { + this.identifier = identifier; + } + @Override public String toString() { return "Car{" + - "id=" + id + - ", manufacturer='" + manufacturer + '\'' + - '}'; + "id=" + id + + ", manufacturer='" + manufacturer + '\'' + + '}'; } } diff --git a/src/test/java/io/github/kaiso/relmongo/data/model/House.java b/src/test/java/io/github/kaiso/relmongo/data/model/House.java index 13287c1..d215d95 100644 --- a/src/test/java/io/github/kaiso/relmongo/data/model/House.java +++ b/src/test/java/io/github/kaiso/relmongo/data/model/House.java @@ -3,7 +3,6 @@ import io.github.kaiso.relmongo.annotation.FetchType; import io.github.kaiso.relmongo.annotation.ManyToOne; -import org.bson.types.ObjectId; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; diff --git a/src/test/java/io/github/kaiso/relmongo/data/repository/AddressRepository.java b/src/test/java/io/github/kaiso/relmongo/data/repository/AddressRepository.java new file mode 100644 index 0000000..7881623 --- /dev/null +++ b/src/test/java/io/github/kaiso/relmongo/data/repository/AddressRepository.java @@ -0,0 +1,13 @@ +package io.github.kaiso.relmongo.data.repository; + +import io.github.kaiso.relmongo.data.model.Address; + +import org.springframework.data.mongodb.repository.MongoRepository; + +import java.util.Optional; + +public interface AddressRepository extends MongoRepository{ + + + Optional
findById(String id); +} diff --git a/src/test/java/io/github/kaiso/relmongo/data/repository/PersonRepository.java b/src/test/java/io/github/kaiso/relmongo/data/repository/PersonRepository.java index 2a90ca2..7aa2372 100644 --- a/src/test/java/io/github/kaiso/relmongo/data/repository/PersonRepository.java +++ b/src/test/java/io/github/kaiso/relmongo/data/repository/PersonRepository.java @@ -1,10 +1,10 @@ package io.github.kaiso.relmongo.data.repository; -import java.util.Optional; +import io.github.kaiso.relmongo.data.model.Person; import org.springframework.data.mongodb.repository.MongoRepository; -import io.github.kaiso.relmongo.data.model.Person; +import java.util.Optional; public interface PersonRepository extends MongoRepository, PersonRepositoryCustom { diff --git a/src/test/java/io/github/kaiso/relmongo/tests/AggregationTest.java b/src/test/java/io/github/kaiso/relmongo/tests/AggregationTest.java new file mode 100644 index 0000000..c6a649a --- /dev/null +++ b/src/test/java/io/github/kaiso/relmongo/tests/AggregationTest.java @@ -0,0 +1,47 @@ +package io.github.kaiso.relmongo.tests; + +import io.github.kaiso.relmongo.data.model.House; +import io.github.kaiso.relmongo.data.model.Person; +import io.github.kaiso.relmongo.data.repository.PersonRepository; +import io.github.kaiso.relmongo.lazy.LazyLoadingProxy; +import io.github.kaiso.relmongo.tests.common.AbstractBaseTest; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.aggregation.Aggregation; +import org.springframework.data.mongodb.core.aggregation.AggregationResults; + +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +public class AggregationTest extends AbstractBaseTest { + + @Autowired + private PersonRepository repository; + + @Test + public void shouldFetchThroughAggregation() { + House house = new House(); + house.setAddress("Paris"); + + House house1 = new House(); + house.setAddress("Bir El Hafey"); + + Person person = new Person(); + person.setName("Dave"); + person.setEmail("dave@mail.com"); + + person.setHouses(Arrays.asList(new House[] { house, house1 })); + repository.save(person); + + Aggregation aggregation = Aggregation.newAggregation(Aggregation.lookup("houses", "houses._id", "_id", "houses")); + AggregationResults result = mongoOperations.aggregate(aggregation, "people", Person.class); + Assertions.assertFalse(result.getMappedResults().isEmpty()); + Assertions.assertEquals(result.getMappedResults().get(0).getHouses().size(), 2); + assertFalse(result.getMappedResults().get(0).getHouses() instanceof LazyLoadingProxy); + + } + +} diff --git a/src/test/java/io/github/kaiso/relmongo/tests/AuditingTest.java b/src/test/java/io/github/kaiso/relmongo/tests/AuditingTest.java new file mode 100644 index 0000000..a308635 --- /dev/null +++ b/src/test/java/io/github/kaiso/relmongo/tests/AuditingTest.java @@ -0,0 +1,61 @@ +package io.github.kaiso.relmongo.tests; + +import io.github.kaiso.relmongo.data.model.Address; +import io.github.kaiso.relmongo.data.model.Person; +import io.github.kaiso.relmongo.data.repository.AddressRepository; +import io.github.kaiso.relmongo.data.repository.PersonRepository; +import io.github.kaiso.relmongo.tests.common.AbstractBaseTest; + +import org.bson.Document; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class AuditingTest extends AbstractBaseTest { + + @Autowired + private PersonRepository repository; + + @Autowired + private AddressRepository addressRepository; + + @Test + public void shouldPopulateAuditingOnCascade() { + Address address1 = new Address(); + Address address2 = new Address(); + address1.setLocation("1st street"); + address2.setLocation("2st street"); + Person person = new Person(); + person.setName("Dave"); + person.setEmail("dave@mail.com"); + person.setAddresses(new LinkedList
(Arrays.asList(address1, address2))); + repository.save(person); + Consumer f = new Consumer() { + @Override + public void accept(Document t) { + assertNotNull(t.get("lastModifiedDate")); + } + }; + mongoOperations.getCollection("addresses").find().forEach(f); + } + + @Test + public void shouldPopulateAuditingOnSave() { + Address address1 = new Address(); + address1.setLocation("1st street"); + addressRepository.save(address1); + Consumer f = new Consumer() { + @Override + public void accept(Document t) { + assertNotNull(t.get("lastModifiedDate")); + } + }; + mongoOperations.getCollection("addresses").find().forEach(f); + } + +} diff --git a/src/test/java/io/github/kaiso/relmongo/tests/CascadingTest.java b/src/test/java/io/github/kaiso/relmongo/tests/CascadingTest.java index b23f09b..8aeccfc 100644 --- a/src/test/java/io/github/kaiso/relmongo/tests/CascadingTest.java +++ b/src/test/java/io/github/kaiso/relmongo/tests/CascadingTest.java @@ -5,8 +5,6 @@ import io.github.kaiso.relmongo.data.model.Car; import io.github.kaiso.relmongo.data.model.Color; 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.DrivingLicenseRepository; @@ -16,11 +14,8 @@ import org.bson.Document; import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.test.context.ContextConfiguration; import java.util.ArrayList; import java.util.Arrays; @@ -36,7 +31,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -@ContextConfiguration(classes = { CascadingTest.class }) + public class CascadingTest extends AbstractBaseTest { @Autowired @@ -45,18 +40,6 @@ public class CascadingTest extends AbstractBaseTest { @Autowired private DrivingLicenseRepository drivingLicenseRepository; - @Autowired - private MongoOperations mongoOperations; - - @BeforeEach - public void beforeEach() { - mongoOperations.dropCollection(DrivingLicense.class); - mongoOperations.dropCollection(Passport.class); - mongoOperations.dropCollection(Person.class); - mongoOperations.dropCollection(House.class); - mongoOperations.dropCollection(Car.class); - mongoOperations.dropCollection(State.class); - } @Test public void shouldCascadeSaveOneToOneObject() { @@ -96,11 +79,11 @@ public void accept(Document t) { @Test public void shouldCascadeSaveOneToManyObjects() { - Car car1 = new Car(); + Car car1 = new Car(1); car1.setColor(Color.BLUE); car1.setManufacturer("BMW"); - Car car2 = new Car(); + Car car2 = new Car(2); car2.setColor(Color.BLUE); car2.setManufacturer("BMW"); @@ -139,11 +122,11 @@ public void accept(Document t) { @Test public void shouldCascadeRemoveCollection() { - Car car1 = new Car(); + Car car1 = new Car(1); car1.setColor(Color.BLUE); car1.setManufacturer("BMW"); - Car car2 = new Car(); + Car car2 = new Car(2); car2.setColor(Color.BLUE); car2.setManufacturer("BMW"); diff --git a/src/test/java/io/github/kaiso/relmongo/tests/MappedByTest.java b/src/test/java/io/github/kaiso/relmongo/tests/MappedByTest.java index eeec2c7..3d54b2d 100644 --- a/src/test/java/io/github/kaiso/relmongo/tests/MappedByTest.java +++ b/src/test/java/io/github/kaiso/relmongo/tests/MappedByTest.java @@ -14,11 +14,8 @@ import io.github.kaiso.relmongo.lazy.LazyLoadingProxy; import io.github.kaiso.relmongo.tests.common.AbstractBaseTest; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.test.context.ContextConfiguration; import java.util.Arrays; import java.util.Optional; @@ -27,7 +24,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -@ContextConfiguration(classes = { MappedByTest.class }) + public class MappedByTest extends AbstractBaseTest { @Autowired @@ -45,21 +42,11 @@ public class MappedByTest extends AbstractBaseTest { @Autowired private DrivingLicenseRepository drivingLicenseRepository; - @Autowired - private MongoOperations mongoOperations; - - @BeforeEach - public void beforeEach() { - mongoOperations.dropCollection(DrivingLicense.class); - mongoOperations.dropCollection(Passport.class); - mongoOperations.dropCollection(Person.class); - mongoOperations.dropCollection(House.class); - mongoOperations.dropCollection(Car.class); - } + @Test public void shouldFetchManyToOneMappedBy() { - Car car = new Car(); + Car car = new Car(0); car.setColor(Color.BLUE); String manufacturer = "BMW"; car.setManufacturer(manufacturer); diff --git a/src/test/java/io/github/kaiso/relmongo/tests/OrphanRemovalTest.java b/src/test/java/io/github/kaiso/relmongo/tests/OrphanRemovalTest.java index 5a5f79f..9b218e4 100644 --- a/src/test/java/io/github/kaiso/relmongo/tests/OrphanRemovalTest.java +++ b/src/test/java/io/github/kaiso/relmongo/tests/OrphanRemovalTest.java @@ -7,11 +7,8 @@ import io.github.kaiso.relmongo.tests.common.AbstractBaseTest; import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.test.context.ContextConfiguration; import java.util.ArrayList; import java.util.Arrays; @@ -22,22 +19,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -@ContextConfiguration(classes = { OrphanRemovalTest.class }) + public class OrphanRemovalTest extends AbstractBaseTest { @Autowired private PersonRepository repository; - @Autowired - private MongoOperations mongoOperations; - - @BeforeEach - public void beforeEach() { - mongoOperations.dropCollection(Person.class); - mongoOperations.dropCollection(Address.class); - mongoOperations.dropCollection(PersonDetails.class); - } - @Test public void shouldRemoveOrphanOnOneToMany() { Address address1 = new Address(); diff --git a/src/test/java/io/github/kaiso/relmongo/tests/PersonRepositoryTest.java b/src/test/java/io/github/kaiso/relmongo/tests/PersonRepositoryTest.java index 9e7db0d..d1b61d5 100644 --- a/src/test/java/io/github/kaiso/relmongo/tests/PersonRepositoryTest.java +++ b/src/test/java/io/github/kaiso/relmongo/tests/PersonRepositoryTest.java @@ -16,11 +16,10 @@ import io.github.kaiso.relmongo.util.RelMongoConstants; import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.test.context.ContextConfiguration; +import org.springframework.dao.DuplicateKeyException; import java.util.Arrays; import java.util.Collection; @@ -33,7 +32,6 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -@ContextConfiguration(classes = { PersonRepositoryTest.class }) public class PersonRepositoryTest extends AbstractBaseTest { @Autowired @@ -51,17 +49,6 @@ public class PersonRepositoryTest extends AbstractBaseTest { @Autowired private DrivingLicenseRepository drivingLicenseRepository; - @Autowired - private MongoOperations mongoOperations; - - @BeforeEach - public void beforeEach() { - mongoOperations.dropCollection(DrivingLicense.class); - mongoOperations.dropCollection(Passport.class); - mongoOperations.dropCollection(Person.class); - mongoOperations.dropCollection(House.class); - mongoOperations.dropCollection(Car.class); - } @Test public void shouldSaveAndRetreive() { @@ -74,17 +61,34 @@ public void shouldSaveAndRetreive() { assertTrue(retreivedEmployee.isPresent()); assertEquals(retreivedEmployee.get().getId(), employee.getId()); } + + @Test() + public void shouldFailOnIndexedField() { + Assertions.assertThrows(DuplicateKeyException.class, () -> { + Car car1 = new Car(1); + car1.setColor(Color.BLUE); + car1.setManufacturer("BMW"); + Car car2 = new Car(1); + car2.setColor(Color.RED); + car2.setManufacturer("BMW"); + + carRepository.save(car1); + carRepository.save(car2); + }); + } @Test public void shouldPersistOnlyIdOnOneToManyRelation() { - Car car1 = new Car(); + Car car1 = new Car(1); car1.setColor(Color.BLUE); car1.setManufacturer("BMW"); - Car car2 = new Car(); - car2.setColor(Color.BLUE); + Car car2 = new Car(2); + car2.setColor(Color.RED); car2.setManufacturer("BMW"); + carRepository.save(car1); carRepository.save(car2); + Person person = new Person(); person.setName("Dave"); person.setEmail("dave@mail.com"); @@ -132,24 +136,24 @@ public void shouldfetchOneToOneRelation() { Optional retreivedPerson = repository.findById(person.getId().toString()); assertNotNull(retreivedPerson.get().getPassport()); assertEquals(retreivedPerson.get().getPassport().getNumber(), "12345"); - //mappedBy + // mappedBy assertEquals(retreivedPerson.get().getPassport().getOwner().getId(), retreivedPerson.get().getId()); - + } @Test public void shouldFetchOneToManyRelation() { - Car car = new Car(); + Car car = new Car(1); car.setColor(Color.BLUE); String manufacturer = "BMW"; car.setManufacturer(manufacturer); - // carRepository.save(car); - - Car car1 = new Car(); + // carRepository.save(car); + + Car car1 = new Car(2); car1.setColor(Color.RED); String manufacturer1 = "JAGUAR"; car1.setManufacturer(manufacturer1); - // carRepository.save(car1); + // carRepository.save(car1); Person person = new Person(); person.setName("Dave"); @@ -161,7 +165,7 @@ public void shouldFetchOneToManyRelation() { assertFalse(retreivedPerson.get().getCars().isEmpty()); assertTrue(retreivedPerson.get().getCars().get(0).getColor().equals(Color.BLUE)); assertTrue(retreivedPerson.get().getCars().get(0).getManufacturer().equals(manufacturer)); - //mappedBy + // mappedBy assertEquals(retreivedPerson.get().getCars().get(0).getOwner().getId(), retreivedPerson.get().getId()); } @@ -180,7 +184,7 @@ public void shouldLazyLoadCollection() { assertFalse(retreivedPerson.get().getHouses().isEmpty()); assertTrue(retreivedPerson.get().getHouses().get(0).getAddress().equals("Paris")); assertTrue(retreivedPerson.get().getHouses() instanceof LazyLoadingProxy); - //mappedBy + // mappedBy assertEquals(retreivedPerson.get().getHouses().get(0).getOwner().getId(), retreivedPerson.get().getId()); } @@ -224,13 +228,13 @@ public void shouldReplaceOneToOneRelation() { @Test public void shouldReplaceOneToManyRelation() { - Car car = new Car(); + Car car = new Car(1); car.setColor(Color.BLUE); String manufacturer = "BMW"; car.setManufacturer(manufacturer); carRepository.save(car); - Car car1 = new Car(); + Car car1 = new Car(2); car1.setColor(Color.RED); String manufacturer1 = "JAGUAR"; car1.setManufacturer(manufacturer1); @@ -284,13 +288,13 @@ public void shouldRemoveOneToOneRelation() { @Test public void shouldRemoveElementFromOneToManyRelation() { - Car car = new Car(); + Car car = new Car(1); car.setColor(Color.BLUE); String manufacturer = "BMW"; car.setManufacturer(manufacturer); carRepository.save(car); - Car car1 = new Car(); + Car car1 = new Car(2); car1.setColor(Color.RED); String manufacturer1 = "JAGUAR"; car1.setManufacturer(manufacturer1); @@ -301,9 +305,9 @@ public void shouldRemoveElementFromOneToManyRelation() { person.setEmail("dave@mail.com"); person.setCars(Arrays.asList(new Car[] { car, car1 })); repository.save(person); - + Optional retreivedPerson = repository.findById(person.getId().toString()); - + assertTrue(retreivedPerson.get().getCars().size() == 2); assertTrue(retreivedPerson.get().getCars().get(0).getColor().equals(Color.BLUE)); assertTrue(retreivedPerson.get().getCars().get(0).getManufacturer().equals(manufacturer)); @@ -318,10 +322,10 @@ public void shouldRemoveElementFromOneToManyRelation() { @Test public void shouldFetchAggreation() { - Car car1 = new Car(); + Car car1 = new Car(1); car1.setColor(Color.BLUE); car1.setManufacturer("BMW"); - Car car2 = new Car(); + Car car2 = new Car(2); car2.setColor(Color.RED); car2.setManufacturer("JAGUAR"); carRepository.save(car1); @@ -344,8 +348,6 @@ public void shouldNotLazyLoadAggregation() { House house1 = new House(); house.setAddress("Bir El Hafey"); - - Person person = new Person(); person.setName("Dave"); diff --git a/src/test/java/io/github/kaiso/relmongo/tests/common/AbstractBaseTest.java b/src/test/java/io/github/kaiso/relmongo/tests/common/AbstractBaseTest.java index b832d05..32bdb97 100644 --- a/src/test/java/io/github/kaiso/relmongo/tests/common/AbstractBaseTest.java +++ b/src/test/java/io/github/kaiso/relmongo/tests/common/AbstractBaseTest.java @@ -1,223 +1,69 @@ package io.github.kaiso.relmongo.tests.common; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; - -import de.flapdoodle.embed.mongo.MongodExecutable; -import de.flapdoodle.embed.mongo.MongodProcess; -import de.flapdoodle.embed.mongo.MongodStarter; -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 io.github.kaiso.relmongo.config.EnableRelMongo; +import io.github.kaiso.relmongo.config.TestContextConfiguration; +import io.github.kaiso.relmongo.data.model.Address; +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.PersonDetails; +import io.github.kaiso.relmongo.data.model.State; import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.Configurator; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Primary; -import org.springframework.core.convert.converter.Converter; -import org.springframework.core.type.filter.AnnotationTypeFilter; -import org.springframework.data.annotation.Persistent; -import org.springframework.data.convert.CustomConversions; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; -import org.springframework.data.mongodb.config.EnableMongoAuditing; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoCustomConversions; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -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.List; -import java.util.Set; -@EnableRelMongo -@EnableMongoAuditing -@EnableMongoRepositories(basePackages = "io.github.kaiso.relmongo.data") +@ContextConfiguration(classes = { TestContextConfiguration.class }) @ExtendWith(SpringExtension.class) -@ComponentScan(basePackages = { "io.github.kaiso.relmongo.tests" }) -public abstract class AbstractBaseTest extends AbstractMongoClientConfiguration { +public abstract class AbstractBaseTest { private static Logger logger = LoggerFactory.getLogger(AbstractBaseTest.class); + @Autowired + protected MongoOperations mongoOperations; + static { ConfigurationSource configurationSource; try { configurationSource = new ConfigurationSource( - new FileInputStream(new File(System.getProperty("user.dir") + "/src/test/resources/log4j2-test.yaml"))); + new FileInputStream(new File(System.getProperty("user.dir") + "/src/test/resources/log4j2-test.yaml"))); Configurator.initialize(ConfigurationBuilderFactory.newConfigurationBuilder().setConfigurationSource(configurationSource).build(true)); logger.info("Logger initialized !"); } catch (IOException e) { e.printStackTrace(); } - - } - - /** - * please store Starter or RuntimeConfig in a static final field - * if you want to use artifact store caching (or else disable caching) - */ - private static final MongodStarter starter = MongodStarter.getDefaultInstance(); - - private static MongodExecutable _mongodExe; - private static MongodProcess _mongod; - @Autowired - private ApplicationContext applicationContext; - private static MongoClient _mongo; - - @BeforeAll - protected static void setUp() throws Exception { - - _mongodExe = starter.prepare(new MongodConfigBuilder().version(Version.Main.PRODUCTION) - .net(new Net("localhost", 55777, Network.localhostIsIPv6())).build()); - _mongod = _mongodExe.start(); - - _mongo = MongoClients.create("mongodb://localhost:55777"); - // _mongo = new MongoClient(Arrays.asList(new ServerAddress("127.0.0.1", - // 30001),new ServerAddress("127.0.0.1", 30002),new ServerAddress("127.0.0.1", - // 30003))); - } - - @AfterAll - protected static void tearDown() throws Exception { - - _mongod.stop(); - _mongodExe.stop(); - } - - @Bean - @Override - public MongoClient mongoClient() { - return _mongo; - } - - @Bean - @Primary - @Override - public MongoMappingContext mongoMappingContext() throws ClassNotFoundException { - MongoMappingContext context = new MongoMappingContext(); - context.setInitialEntitySet(new EntityScanner(applicationContext) - .scan(Document.class, Persistent.class)); - context.setSimpleTypeHolder(customConversions().getSimpleTypeHolder()); - context.afterPropertiesSet(); - return context; - } - - @Bean - @Primary - @Override - public CustomConversions customConversions() { - return new MongoCustomConversions(Collections.singletonList(new RMLocalDateTimeToDateConverter())); - } - - @Bean - @Override - public MappingMongoConverter mappingMongoConverter() throws Exception { - DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory()); - MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext()); - converter.setCustomConversions(customConversions()); - converter.afterPropertiesSet(); - return converter; - } - - @Override - public String getDatabaseName() { - return "test"; - } - - @Override - public Collection getMappingBasePackages() { - return Collections.singleton("io.github.kaiso.relmongo.data"); - } - - public class EntityScanner { - - private final ApplicationContext context; - - /** - * Create a new {@link EntityScanner} instance. - * - * @param context - * the source application context - */ - public EntityScanner(ApplicationContext context) { - Assert.notNull(context, "Context must not be null"); - this.context = context; - } - - /** - * Scan for entities with the specified annotations. - * - * @param annotationTypes - * the annotation types used on the entities - * @return a set of entity classes - * @throws ClassNotFoundException - * if an entity class cannot be loaded - */ - @SafeVarargs - public final Set> scan(Class... annotationTypes) - throws ClassNotFoundException { - List packages = getPackages(); - if (packages.isEmpty()) { - return Collections.emptySet(); - } - ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider( - false); - scanner.setEnvironment(this.context.getEnvironment()); - scanner.setResourceLoader(this.context); - for (Class annotationType : annotationTypes) { - scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType)); - } - Set> entitySet = new HashSet<>(); - for (String basePackage : packages) { - if (StringUtils.hasText(basePackage)) { - for (BeanDefinition candidate : scanner - .findCandidateComponents(basePackage)) { - entitySet.add(ClassUtils.forName(candidate.getBeanClassName(), - this.context.getClassLoader())); - } - } - } - return entitySet; - } - - private List getPackages() { - return Collections.singletonList("io.github.kaiso.relmongo.data"); - } - } - public class RMLocalDateTimeToDateConverter implements Converter { - public Date convert(LocalDateTime source) { - return Date.from(source.atZone(ZoneId.systemDefault()).toInstant()); - } + @AfterEach + public void afterEach() { +// mongoOperations.dropCollection(DrivingLicense.class); +// mongoOperations.dropCollection(Passport.class); +// mongoOperations.dropCollection(Person.class); +// mongoOperations.dropCollection(House.class); +// mongoOperations.dropCollection(Car.class); +// mongoOperations.dropCollection(State.class); +// mongoOperations.dropCollection(Address.class); + mongoOperations.remove(DrivingLicense.class).all(); + mongoOperations.remove(Passport.class).all(); + mongoOperations.remove(Person.class).all(); + mongoOperations.remove(PersonDetails.class).all(); + mongoOperations.remove(House.class).all(); + mongoOperations.remove(Car.class).all(); + mongoOperations.remove(State.class).all(); + mongoOperations.remove(Address.class).all(); } }