{"id":21131157,"url":"https://github.com/wnameless/spring-boot-up-data-mongodb","last_synced_at":"2025-03-14T12:16:01.848Z","repository":{"id":191326740,"uuid":"684417317","full_name":"wnameless/spring-boot-up-data-mongodb","owner":"wnameless","description":"MongoDB enhancement of Cascade and Event brought by spring-boot-up","archived":false,"fork":false,"pushed_at":"2023-09-16T05:47:40.000Z","size":82,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-21T06:11:48.991Z","etag":null,"topics":["java","spring-boot-data-mongodb"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wnameless.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-08-29T04:51:27.000Z","updated_at":"2023-11-14T10:29:53.000Z","dependencies_parsed_at":null,"dependency_job_id":"5e544c40-3e57-4d47-9810-15e2d5cbbdda","html_url":"https://github.com/wnameless/spring-boot-up-data-mongodb","commit_stats":null,"previous_names":["wnameless/spring-boot-up-data-mongodb"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wnameless%2Fspring-boot-up-data-mongodb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wnameless%2Fspring-boot-up-data-mongodb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wnameless%2Fspring-boot-up-data-mongodb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wnameless%2Fspring-boot-up-data-mongodb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wnameless","download_url":"https://codeload.github.com/wnameless/spring-boot-up-data-mongodb/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243573495,"owners_count":20312883,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","spring-boot-data-mongodb"],"created_at":"2024-11-20T05:49:54.160Z","updated_at":"2025-03-14T12:16:01.779Z","avatar_url":"https://github.com/wnameless.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.wnameless.spring.boot.up/spring-boot-up-data-mongodb/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.wnameless.spring.boot.up/spring-boot-up-data-mongodb)\n[![codecov](https://codecov.io/gh/wnameless/spring-boot-up-data-mongodb/branch/main/graph/badge.svg)](https://codecov.io/gh/wnameless/spring-boot-up-data-mongodb)\n\nspring-boot-up-data-mongodb\n=============\nMongoDB enhancement of Cascade and Event brought by spring-boot-up.\n\n## _Goal_ - introducing `Cascade` and more features into Spring MongoDB\n## _Purpose_ - reducing boilerplate codes followed by Spring `@DBRef`($ref)\nEntity relationship model:\n```mermaid\ngraph TD;\n    Car-- \"$ref: gasTank\" --\u003eGasTank;\n    GasTank-. \"$ref: car\" .-\u003eCar;\n    Car-- \"$ref: engine\" --\u003eEngine;\n    Engine-. \"$ref: car\" .-\u003eCar;\n    Engine--\u003eMotor;\n    Motor-. \"$ref: engine\" .-\u003eEngine;\n    Motor-. \"$ref: car\" .-\u003eCar;\n    Car-- \"$ref: wheels\" --\u003eWheel;\n    Wheel-. \"$ref: car\" .-\u003eCar;\n    Car-- \"$ref: subGasTank\" --\u003eSubGasTank\n    SubGasTank-. \"$ref: car\" .-\u003eCar;\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eEntity Initialization\u003c/summary\u003e\n\n```java\nvar car  = new Car();\nvar gasTank =  new GasTank();\nvar engine  = new Engine();\nvar motor = new Motor();\nvar frontRightWheel = new Wheel();\nvar frontLeftWheel = new Wheel();\nvar rareRightWheel = new Wheel();\nvar rareLeftWheel = new Wheel();\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ci\u003eBoilerplate codes\u003c/i\u003e before \u003cb\u003espring-boot-up-data-mongodb\u003c/b\u003e was introduced\u003c/summary\u003e\n\n```java\n// Must save all documents before assigning @DBRef fields\n\n// Create Car\ncarRepository.save(car);\n\n// Create GasTank with Car ref\ngasTank.set(car);\ngasTankRepository.save(gasTank);\n\n// Create Engine with Car ref\nengine.setCar(car);\nengineRepository.save(engine);\n\n// Create Motor with Engine and Car ref\nmotor.setEngine(engine);\nmotor.setCar(car);\nmotorRepository.save(motor);\n\n// Update Engine with Motor ref\nengine.setMotor(motor);\nengineRepository.save(engine);\n\n// Create Wheel(s) with Car ref\nfrontRightWheel.setCar(car);\nfrontLeftWheel.setCar(car);\nrareRightWheel.setCar(car);\nrareLeftWheel.setCar(car);\nwheelRepository.save(wheels);\n\n// Update Car with GasTank, Engine and Wheel(s) ref\ncar.setGasTank(gasTank);\ncar.setEngine(engine);\ncar.setWheels(Arrays.asList(frontRightWheel, frontLeftWheel, rareRightWheel, rareLeftWheel));\ncarRepository.save(car);\n```\n\u003c/details\u003e\n\n\u003cdetails open\u003e\n\u003csummary\u003e\u003ci\u003eCompact codes\u003c/i\u003e after utilizing \u003cb\u003espring-boot-up-data-mongodb\u003c/b\u003e\u003c/summary\u003e\n\n```java\n// Only need to focus on setting relationships between documents\ncar.setGasTank(gasTank);\ncar.setEngine(engine);\nengine.setMotor(motor);\ncar.setWheels(wheels);\n\ncarRepository.save(car);\n```\n\u003c/details\u003e\n\n# Maven Repo\n```xml\n\u003cdependency\u003e\n\t\u003cgroupId\u003ecom.github.wnameless.spring.boot.up\u003c/groupId\u003e\n\t\u003cartifactId\u003espring-boot-up-data-mongodb\u003c/artifactId\u003e\n\t\u003cversion\u003e${newestVersion}\u003c/version\u003e\n\t\u003c!-- Newest version shows in the maven-central badge above --\u003e\n\u003c/dependency\u003e\n```\nThis lib uses Semantic Versioning: `{MAJOR.MINOR.PATCH}`.\u003cbr\u003e\nHowever, the MAJOR version is always matched the Spring Boot MAJOR version.\n```diff\n! Maven dependency spring-boot-starter-data-mongodb is required\n```\n\n# Quick Start\n```java\n@EnableSpringBootUpMongo(allowAnnotationDrivenEvent = true) // Default value is false\n@Configuration\npublic class MyConfiguration {}\n```\n\n```java\n@Repository\npublic interface CarRepository extends MongoRepository\u003cCar, String\u003e, MongoProjectionRepository\u003cCar\u003e {}\n// With projection feature\n```\n\u003cdetails\u003e\n\u003csummary\u003eRepository without projection feature\u003c/summary\u003e\n\n```java\n@Repository\npublic interface CarRepository extends MongoRepository\u003cCar, String\u003e {}\n```\n\u003c/details\u003e\n\n# Feature List\u003ca id='top'\u003e\u003c/a\u003e\n| Name | Option | Description | Since |\n| --- | --- | --- | --- |\n| [Cascade(@CascadeRef)](#3.0.0-1) | --- | Cascade feature for Spring Data MongoDB entities | v3.0.0 |\n| | [CascadeType.CREATE](#3.0.0-1.1) | Cascade CREATE | v3.0.0 |\n| | [CascadeType.UPDATE](#3.0.0-1.2) | Cascade UPDATE | v3.0.0 |\n| | [CascadeType.DELETE](#3.0.0-1.3) | Cascade DELETE | v3.0.0 |\n| | CascadeType.ALL | A combining of CREATE, UPDATE and DELETE  | v3.0.0 |\n| [@ParentRef](#3.0.0-2) | --- | Automatically set the cascade event publisher object into `@ParentRef` annotated fields of the cascade event receiver | v3.0.0 |\n| | [Default Usage](#3.0.0-2.1) | No additional configuration  | v3.0.0 |\n| | [Advanced Usage](#3.0.0-2.2) | Providing a field name of parent object | v3.0.0 |\n| [Annotation Driven Event](#3.0.0-3) | --- | Annotation Driven Event feature for `MongoEvent` | v3.0.0 |\n| | [No arguments](#3.0.0-3.1) | Annotated methods with no arguments | v3.0.0 |\n| | [SourceAndDocument](#3.0.0-3.2) | Annotated methods with single `SourceAndDocument` argument | v3.0.0 |\n| [Projection](#3.0.0-4) | --- | Projection feature for Spring Data MongoDB entities | v3.0.0 |\n| | [Dot notation](#3.0.0-4.1) | String path with dot operator(.) | v3.0.0 |\n| | [Path](#3.0.0-4.2) | QueryDSL Path | v3.0.0 |\n| | [Projection Class](#3.0.0-4.3) | Java Class | v3.0.0 |\n| [Custom Conversions](#3.0.0-5) | --- | A collection of MongoCustomConversions | v3.0.0 |\n| | [JavaTime](#3.0.0-5.1) | MongoCustomConversions for Java 8 Date/Time | v3.0.0 |\n\n### [:top:](#top) Cascade(@CascadeRef)\u003ca id='3.0.0-1'\u003e\u003c/a\u003e\n```diff\n+ @CascadeRef must annotate alongside @DBRef\n```\nEntity classes:\n\u003cdetails open\u003e\n\u003csummary\u003eCar\u003c/summary\u003e\n\n```java\n@EqualsAndHashCode(of = \"id\")\n@Data\n@Document\npublic class Car {\n  @Id\n  String id;\n\n  @CascadeRef({CascadeType.CREATE, CascadeType.DELETE})\n  @DBRef\n  Engine engine;\n\n  @CascadeRef(CascadeType.CREATE)\n  @DBRef\n  GasTank gasTank;\n\n  @CascadeRef // Equivalent to @CascadeRef(CascadeType.ALL)\n  @DBRef\n  List\u003cWheel\u003e wheels = new ArrayList\u003c\u003e();\n\n  @CascadeRef({CascadeType.UPDATE, CascadeType.DELETE})\n  @DBRef\n  GasTank subGasTank;\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eGasTank\u003c/summary\u003e\n\n```java\n@EqualsAndHashCode(of = \"id\")\n@Data\n@Document\npublic class GasTank {\n  @Id\n  String id;\n\n  @ParentRef\n  @DBRef\n  Car car;\n\n  double capacity = 100;\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eEngine\u003c/summary\u003e\n\n```java\n@EqualsAndHashCode(of = \"id\")\n@Data\n@Document\npublic class Engine {\n  @Id\n  String id;\n\n  @ParentRef\n  @DBRef\n  Car car;\n\n  double horsePower = 500;\n\n  @CascadeRef\n  @DBRef\n  Motor motor;\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eMotor\u003c/summary\u003e\n\n```java\n@EqualsAndHashCode(of = \"id\")\n@Data\n@Document\npublic class Motor {\n  @Id\n  String id;\n\n  @ParentRef\n  @DBRef\n  Engine engine;\n\n  @ParentRef(\"car\")\n  @DBRef\n  Car car;\n\n  double rpm = 60000;\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWheel\u003c/summary\u003e\n\n```java\n@EqualsAndHashCode(of = \"id\")\n@Data\n@Document\npublic class Wheel {\n  @Id\n  String id;\n\n  @ParentRef\n  @DBRef\n  Car car;\n\n  String tireBrand = \"MAXXIS\";\n}\n```\n\u003c/details\u003e\n\n---\nJUnit `BeaforeEach`\n```java\nmongoTemplate.getDb().drop(); // reset DB before each test\n\ncar.setGasTank(gasTank);\ncar.setEngine(engine);\nengine.setMotor(motor);\ncar.setWheels(Arrays.asList(frontRightWheel, frontLeftWheel, rareRightWheel, rareLeftWheel));\ncarRepository.save(car);\n```\n\n#### [:top:](#top) CascadeType.CREATE\u003ca id='3.0.0-1.1'\u003e\u003c/a\u003e\n```java\n// JUnit\nassertEquals(1, carRepository.count());\nassertEquals(1, gasTankRepository.count());\nassertEquals(1, engineRepository.count());\nassertEquals(1, motorRepository.count());\nassertEquals(4, wheelRepository.count());\n```\n\n#### [:top:](#top) CascadeType.UPDATE\u003ca id='3.0.0-1.2'\u003e\u003c/a\u003e\n```java\n// JUnit\ncar = new Car();\nvar subGasTank = new GasTank();\ncar.setSubGasTank(subGasTank);\n// Because this car object hasn't been saved, so the CascadeType.UPDATE about the subGasTank object won't be performed\nassertThrows(RuntimeException.class, () -\u003e {\n  carRepository.save(car);\n});\n\ncar = new Car();\ncarRepository.save(car);\nvar subGasTank = new GasTank();\ncar.setSubGasTank(subGasTank);\ncarRepository.save(car);\n// Because this car object has been saved, so the CascadeType.UPDATE is performed\nassertSame(subGasTank, car.getSubGasTank());\n```\nThe main diffrence between `CascadeType.UPDATE` and plain `@DBREf` is that\u003cbr\u003e\n`CascadeType.UPDATE` allows unsaved documents to be set in `@DBREf` fields but plain `@DBREf` won't.\n```diff\n@@ Once @DBRef has been established, CascadeType.UPDATE won't change anything in @DBRef's nature @@\n```\n\n#### [:top:](#top) CascadeType.DELETE\u003ca id='3.0.0-1.3'\u003e\u003c/a\u003e\n```java\n// JUnit\ncarRepository.deleteAll();\nassertEquals(0, carRepository.count());\nassertEquals(1, engineRepository.count());\nassertEquals(1, motorRepository.count());\nassertEquals(1, gasTankRepository.count());\nassertEquals(4, wheelRepository.count());\n```\n```diff\n- Cascade is NOT working on bulk operations(ex: CrudRepository#deleteAll)\n```\n```java\n// JUnit\ncarRepository.deleteAll(carRepository.findAll()); \nassertEquals(0, carRepository.count());\nassertEquals(0, engineRepository.count());\nassertEquals(0, motorRepository.count());\nassertEquals(1, gasTankRepository.count());\n// gasTank won't be deleted because it's only annotated with @CascadeRef(CascadeType.CREATE)\nassertEquals(0, wheelRepository.count());\n```\n```diff\n+ Using CrudRepository#deleteAll(Iterable) instead of CrudRepository#deleteAll can perform cascade normally in most circumstances\n```\n\n### [:top:](#top) @ParentRef\u003ca id='3.0.0-2'\u003e\u003c/a\u003e\n#### [:top:](#top) Default Usage\u003ca id='3.0.0-2.1'\u003e\u003c/a\u003e\nCar is treated as a _parent_ of GasTank, because it is an event publisher to GasTank.\n\u003cdetails\u003e\n\u003csummary\u003eCar\u003c/summary\u003e\n\n```java\n@EqualsAndHashCode(of = \"id\")\n@Data\n@Document\npublic class Car {\n  @Id\n  String id;\n\n  @CascadeRef({CascadeType.CREATE, CascadeType.DELETE})\n  @DBRef\n  Engine engine;\n\n  @CascadeRef(CascadeType.CREATE)\n  @DBRef\n  GasTank gasTank;\n\n  @CascadeRef // Equivalent to @CascadeRef(CascadeType.ALL)\n  @DBRef\n  List\u003cWheel\u003e wheels = new ArrayList\u003c\u003e();\n\n  @CascadeRef({CascadeType.UPDATE, CascadeType.DELETE})\n  @DBRef\n  GasTank subGasTank;\n}\n```\n\u003c/details\u003e\n\nTherefore, the `@ParentRef` annotated field of a GasTank will be set by Car automatically.\n\u003cdetails open\u003e\n\u003csummary\u003eGasTank\u003c/summary\u003e\n\n```java\n@EqualsAndHashCode(of = \"id\")\n@Data\n@Document\npublic class GasTank {\n  @Id\n  String id;\n\n  @ParentRef\n  @DBRef\n  Car car;\n\n  double capacity = 100;\n}\n```\n\u003c/details\u003e\n\n#### [:top:](#top) Advanced Usage\u003ca id='3.0.0-2.2'\u003e\u003c/a\u003e\nEngine is treated as a _parent_ of Motor, because it is an event publisher to Motor.\n\u003cdetails\u003e\n\u003csummary\u003eEngine\u003c/summary\u003e\n\n```java\n@EqualsAndHashCode(of = \"id\")\n@Data\n@Document\npublic class Engine {\n  @Id\n  String id;\n\n  @ParentRef\n  @DBRef\n  Car car;\n\n  double horsePower = 500;\n\n  @CascadeRef\n  @DBRef\n  Motor motor;\n}\n```\n\u003c/details\u003e\n\nTherefore, the `@ParentRef(\"car\")` field of Motor is set by the _car_ field of Engine automatically.\n\u003cdetails open\u003e\n\u003csummary\u003eMotor\u003c/summary\u003e\n\n```java\n@EqualsAndHashCode(of = \"id\")\n@Data\n@Document\npublic class Motor {\n  @Id\n  String id;\n\n  @ParentRef\n  @DBRef\n  Engine engine;\n\n  @ParentRef(\"car\")\n  @DBRef\n  Car car;\n\n  double rpm = 60000;\n}\n```\n\u003c/details\u003e\n\nTest `@ParentRef`\n```java\n// Default usage\nassertSame(car, gasTank.getCar());\n// Advanced usage\nassertSame(car, engine.getCar());\nassertSame(engine, motor.getEngine());\nassertSame(car, motor.getCar());\n```\n\n### [:top:](#top) Annotation Driven Event\u003ca id='3.0.0-3'\u003e\u003c/a\u003e\n6 types of annotation driven events are supported:\n\n* BeforeConvertToMongo\n* BeforeSaveToMongo\n* AfterSaveToMongo\n* AfterConvertFromMongo\n* BeforeDeleteFromMongo\n* AfterDeleteFromMongo\n\nAll annotated methods will be triggered in corresponding MongoDB lifecycle events.\n\nAnnotated methods can accept only empty or single `SourceAndDocument` as argument.\n\u003cdetails\u003e\n\u003csummary\u003eSourceAndDocument\u003c/summary\u003e\n\n```java\npublic final class SourceAndDocument {\n\n  private final Object source;\n  private final Document document;\n\n  public SourceAndDocument(Object source, Document document) {\n    this.source = source;\n    this.document = document;\n  }\n\n  public Object getSource() {\n    return source;\n  }\n\n  public Document getDocument() {\n    return document;\n  }\n\n  public boolean hasSource(Class\u003c?\u003e type) {\n    return type.isAssignableFrom(source.getClass());\n  }\n\n  @SuppressWarnings(\"unchecked\")\n  public \u003cT\u003e T getSource(Class\u003cT\u003e type) {\n    return (T) source;\n  }\n\n  // #hashCode, #equals, #toString\n}\n```\n\u003c/details\u003e\n\n`SourceAndDocument` stores both event source object and event BSON Document at that point.\n```diff\n- Annotation Driven Event won't be triggered under Mongo bulk operations\n```\n\n#### [:top:](#top) No arguments\u003ca id='3.0.0-3.1'\u003e\u003c/a\u003e\n\n```java\n@Document\npublic class Car {\n  @Id\n  String id;\n\n  @BeforeConvertToMongo\n  void beforeConvert() {\n    System.out.println(\"beforeConvertToMongo\");\n  }\n\n  @BeforeSaveToMongo\n  void beforeSave() {\n    System.out.println(\"beforeSaveToMongo\");\n  }\n\n  @AfterSaveToMongo\n  void afterSave() {\n    System.out.println(\"afterSaveToMongo\");\n  }\n\n  @AfterConvertFromMongo\n  void afterConvert() {\n    System.out.println(\"afterConvertFromMongo\");\n  }\n\n  @BeforeDeleteFromMongo\n  void beforeDeleteFromMongo() {\n    System.out.println(\"beforeDeleteFromMongo\");\n  }\n\n  @AfterDeleteFromMongo\n  void afterDeleteFromMongo() {\n    System.out.println(\"afterDeleteFromMongo\");\n  }\n}\n```\n\n#### [:top:](#top) SourceAndDocument\u003ca id='3.0.0-3.2'\u003e\u003c/a\u003e\n```java\n@Document\npublic class Car {\n  @Id\n  String id;\n\n  @BeforeConvertToMongo\n  void beforeConvertArg(SourceAndDocument sad) {\n    var car = sad.getSource(Car.class);\n  }\n\n  @BeforeSaveToMongo\n  void beforeSaveArg(SourceAndDocument sad) {\n    var car = sad.getSource(Car.class);\n  }\n\n  @AfterSaveToMongo\n  void afterSaveArg(SourceAndDocument sad) {\n    var car = sad.getSource(Car.class);\n  }\n\n  @AfterConvertFromMongo\n  void afterConvertArg(SourceAndDocument sad) {\n    var car = sad.getSource(Car.class);\n  }\n\n  @BeforeDeleteFromMongo\n  void beforeDeleteFromMongoArg(SourceAndDocument sad) {\n    var car = sad.getSource(Car.class);\n  }\n\n  @AfterDeleteFromMongo\n  void afterDeleteFromMongoArg(SourceAndDocument sad) {\n    var car = sad.getSource(Car.class);\n  }\n}\n```\n\n### [:top:](#top) Projection\u003ca id='3.0.0-4'\u003e\u003c/a\u003e\nEntity classes:\n```java\n@EqualsAndHashCode(of = \"id\")\n@Data\n@Document\npublic class ComplexModel {\n  @Id\n  String id;\n\n  String str;\n\n  Integer i;\n\n  Double d;\n\n  Boolean b;\n\n  NestedModel nested;\n}\n```\n```java\n@Data\npublic class NestedModel {\n  Float f;\n\n  Short s;\n}\n```\n```java\n@Data\npublic class ProjectModel {\n  String str;\n}\n```\n\nInit:\n```java\nvar model = new ComplexModel();\nmodel.setStr(\"str\");\nmodel.setI(123);\nmodel.setD(45.6);\nmodel.setB(true);\nvar nested = new NestedModel();\nnested.setF(7.8f);\nnested.setS((short) 9);\nmodel.setNested(nested);\ncomplexModelRepository.save(model);\n```\n\n### Projection can be performed in 3 ways:\n\n#### [:top:](#top) Approach 1: Dot notation\u003ca id='3.0.0-4.1'\u003e\u003c/a\u003e\n```java\nvar projected = complexModelRepository.findProjectedBy(\"str\");\n// Use dot operator(.) to represent nested projection object\nvar nestedProjected = complexModelRepository.findProjectedBy(\"nested.f\");\n```\n\u003cdetails\u003e\n\u003csummary\u003eResult\u003c/summary\u003e\n\n```java\n// JUnit\nassertEquals(\"str\", projected.getStr());\nassertNull(projected.getI());\nassertNull(projected.getD());\nassertNull(projected.getB());\nassertNull(projected.getNested());\n\nassertNull(nestedProjected.getStr());\nassertNull(nestedProjected.getI());\nassertNull(nestedProjected.getD());\nassertNull(nestedProjected.getB());\nassertEquals(7.8f, nestedProjected.getNested().getF());\n```\n\u003c/details\u003e\n\n#### [:top:](#top) Approach 2: QueryDSL Path\u003ca id='3.0.0-4.2'\u003e\u003c/a\u003e\n```java\n// QueryDSL PathBuilder\nPathBuilder\u003cCar\u003e entityPath = new PathBuilder\u003c\u003e(ComplexModel.class, \"entity\");\nvar projected = carRepository.findProjectedBy(entityPath.getString(\"str\"));\n```\n\u003cdetails\u003e\n\u003csummary\u003eResult\u003c/summary\u003e\n\n```java\n// JUnit\nassertEquals(\"str\", projected.getStr());\nassertNull(projected.getI());\nassertNull(projected.getD());\nassertNull(projected.getB());\nassertNull(projected.getNested());\n```\n\u003c/details\u003e\n\n#### [:top:](#top) Approach 3: Java Class\u003ca id='3.0.0-4.3'\u003e\u003c/a\u003e\n```java\n// By projection Class\nvar projected = carRepository.findProjectedBy(ProjectModel.class);\n```\n\u003cdetails\u003e\n\u003csummary\u003eResult\u003c/summary\u003e\n\n```java\n// JUnit\nassertEquals(\"str\", projected.getStr());\nassertNull(projected.getI());\nassertNull(projected.getD());\nassertNull(projected.getB());\nassertNull(projected.getNested());\n```\n\u003c/details\u003e\n\n### [:top:](#top) Custom Conversions\u003ca id='3.0.0-5'\u003e\u003c/a\u003e\n#### [:top:](#top) JavaTime\u003ca id='3.0.0-5.1'\u003e\u003c/a\u003e\nMongoDB doesn't natively support Java 8 Date/Time(Ex: `LocalDateTime`), so here is a convenient solution.\n```java\n@Configuration\npublic class MongoConfig extends AbstractMongoClientConfiguration {\n  @Override\n  public MongoCustomConversions customConversions() {\n    // MongoConverters.javaTimeConversions() includes all types of Java 8 Date/Time converters\n    return MongoConverters.javaTimeConversions();\n  }\n}\n```\nAll Java 8 Date/Time types(excluding DayOfWeek and Month Enums) are converted to `String`, and vice versa.\n\n## MISC\n| Note| Since |\n| --- | --- |\n| Java 17 required. | v3.0.0 |\n| Spring Boot 3.0.0+ required. | v3.0.0 |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwnameless%2Fspring-boot-up-data-mongodb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwnameless%2Fspring-boot-up-data-mongodb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwnameless%2Fspring-boot-up-data-mongodb/lists"}