{"id":23884978,"url":"https://github.com/lecousin/lc-spring-data-r2dbc","last_synced_at":"2025-04-09T23:41:24.065Z","repository":{"id":41503803,"uuid":"315025538","full_name":"lecousin/lc-spring-data-r2dbc","owner":"lecousin","description":"An extension of spring-data-r2dbc to provide features such as relationships, joins, cascading save/delete, lazy loading, sequence, schema generation, composite id","archived":false,"fork":false,"pushed_at":"2024-01-22T06:25:44.000Z","size":581,"stargazers_count":48,"open_issues_count":16,"forks_count":12,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-24T01:35:17.443Z","etag":null,"topics":["cascade","lazy-loading","orm","r2dbc","relationships","schema","spring","spring-data","spring-data-r2dbc"],"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/lecousin.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,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-22T11:52:48.000Z","updated_at":"2024-08-11T14:05:25.000Z","dependencies_parsed_at":"2025-01-04T04:23:36.317Z","dependency_job_id":"356bbc04-1a85-4b0d-b74b-d534ed12de8a","html_url":"https://github.com/lecousin/lc-spring-data-r2dbc","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lecousin%2Flc-spring-data-r2dbc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lecousin%2Flc-spring-data-r2dbc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lecousin%2Flc-spring-data-r2dbc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lecousin%2Flc-spring-data-r2dbc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lecousin","download_url":"https://codeload.github.com/lecousin/lc-spring-data-r2dbc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248131467,"owners_count":21052819,"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":["cascade","lazy-loading","orm","r2dbc","relationships","schema","spring","spring-data","spring-data-r2dbc"],"created_at":"2025-01-04T04:22:32.089Z","updated_at":"2025-04-09T23:41:24.043Z","avatar_url":"https://github.com/lecousin.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lc-spring-data-r2dbc\n\n\u003ca href=\"https://search.maven.org/artifact/net.lecousin.reactive-data-relational/core\"\u003e\u003cimg src=\"https://img.shields.io/maven-central/v/net.lecousin.reactive-data-relational/core.svg\"\u003e\u003c/a\u003e \u0026nbsp;\n\u003ca href=\"https://www.javadoc.io/doc/net.lecousin.reactive-data-relational/core/0.10.2\"\u003e\u003cimg src=\"https://img.shields.io/badge/javadoc-0.10.2-brightgreen.svg\"\u003e\u003c/a\u003e \u0026nbsp;\n\u003ca href=\"https://github.com/lecousin/lc-spring-data-r2dbc/actions/workflows/maven.yml?query=branch%3Amaster\"\u003e\u003cimg src=\"https://github.com/lecousin/lc-spring-data-r2dbc/actions/workflows/maven.yml/badge.svg?branch=master\"\u003e\u003c/a\u003e\n\u003cbr/\u003e\n\u003ca href=\"https://codecov.io/gh/lecousin/lc-spring-data-r2dbc/branch/master\"\u003e\u003cimg src=\"https://codecov.io/gh/lecousin/lc-spring-data-r2dbc/branch/master/graph/badge.svg\"\u003e\u003c/a\u003e \u0026nbsp;\n\u003ca href=\"https://sonarcloud.io/project/overview?id=lecousin_lc-spring-data-r2dbc\"\u003e\u003cimg src=\"https://sonarcloud.io/api/project_badges/measure?project=lecousin_lc-spring-data-r2dbc\u0026metric=coverage\"\u003e\u003c/a\u003e \u0026nbsp;\n\u003ca href=\"https://sonarcloud.io/project/overview?id=lecousin_lc-spring-data-r2dbc\"\u003e\u003cimg src=\"https://sonarcloud.io/api/project_badges/measure?project=lecousin_lc-spring-data-r2dbc\u0026metric=bugs\"\u003e\u003c/a\u003e \u0026nbsp;\n\u003ca href=\"https://sonarcloud.io/project/overview?id=lecousin_lc-spring-data-r2dbc\"\u003e\u003cimg src=\"https://sonarcloud.io/api/project_badges/measure?project=lecousin_lc-spring-data-r2dbc\u0026metric=vulnerabilities\"\u003e\u003c/a\u003e \u0026nbsp;\n\u003ca href=\"https://sonarcloud.io/project/overview?id=lecousin_lc-spring-data-r2dbc\"\u003e\u003cimg src=\"https://sonarcloud.io/api/project_badges/measure?project=lecousin_lc-spring-data-r2dbc\u0026metric=code_smells\"\u003e\u003c/a\u003e\n\n\nThe goal this library is to provide basic ORM features not covered by Spring Data R2DBC (now part of [Spring Data Relational](https://github.com/spring-projects/spring-data-relational) project).\n\n## Features\n\n - Lazy loading\n - Linked entities (1 to 1, 1 to n, n to 1, n to n)\n - Select statement with joins\n - Save (insert/update) with cascade\n - Delete with cascade\n - Composite Id\n - Sequence\n - Insert multiple rows in a single INSERT request (except for MySql)\n - Schema generation, with indexes, foreign key constraints, sequences\n - Array columns (only with Postgresql)\n\nFeatures are detailed with examples in the [wiki section](https://github.com/lecousin/lc-spring-data-r2dbc/wiki)\n\n## Supported databases\n\n - H2\n - Postgres\n - MySql \n \n## Dependencies version\n\n\u003ctable\u003e\n\t\u003ctr\u003e\n\t\t\u003cth\u003eDependency\u003c/th\u003e\n\t\t\u003cth\u003egroupId\u003c/th\u003e\n\t\t\u003cth\u003eartifactId\u003c/th\u003e\n\t\t\u003cth\u003eversion\u003c/th\u003e\n\t\t\u003cth\u003eLatest version\u003c/th\u003e\n\t\u003c/tr\u003e\n\t\u003ctr\u003e\n\t\t\u003ctd\u003eSpring Boot\u003c/td\u003e\n\t\t\u003ctd\u003eorg.springframework.boot\u003c/td\u003e\n\t\t\u003ctd\u003espring-boot-starter-data-r2dbc\u003c/td\u003e\n\t\t\u003ctd\u003e2.6.7\u003c/td\u003e\n\t\t\u003ctd\u003e\u003ca href=\"https://search.maven.org/artifact/org.springframework.boot/spring-boot-starter-data-r2dbc\"\u003e\u003cimg src=\"https://img.shields.io/maven-central/v/org.springframework.boot/spring-boot-starter-data-r2dbc.svg\"\u003c/a\u003e\u003c/td\u003e\n\t\u003c/tr\u003e\n\t\u003ctr\u003e\n\t\t\u003ctd\u003eAppache Commons Lang\u003c/td\u003e\n\t\t\u003ctd\u003eorg.apache.commons\u003c/td\u003e\n\t\t\u003ctd\u003ecommons-lang3\u003c/td\u003e\n\t\t\u003ctd\u003e3.12.0\u003c/td\u003e\n\t\t\u003ctd\u003e\u003ca href=\"https://search.maven.org/artifact/org.apache.commons/commons-lang3\"\u003e\u003cimg src=\"https://img.shields.io/maven-central/v/org.apache.commons/commons-lang3.svg\"\u003c/a\u003e\u003c/td\u003e\n\t\u003c/tr\u003e\n\t\u003ctr\u003e\n\t\t\u003ctd\u003eJavassist\u003c/td\u003e\n\t\t\u003ctd\u003eorg.javassist\u003c/td\u003e\n\t\t\u003ctd\u003ejavassist\u003c/td\u003e\n\t\t\u003ctd\u003e3.28.0-GA\u003c/td\u003e\n\t\t\u003ctd\u003e\u003ca href=\"https://search.maven.org/artifact/org.javassist/javassist\"\u003e\u003cimg src=\"https://img.shields.io/maven-central/v/org.javassist/javassist.svg\"\u003c/a\u003e\u003c/td\u003e\n\t\u003c/tr\u003e\n\t\u003ctr\u003e\n\t\t\u003ctd\u003eH2 driver\u003c/td\u003e\n\t\t\u003ctd\u003eio.r2dbc\u003c/td\u003e\n\t\t\u003ctd\u003er2dbc-h2\u003c/td\u003e\n\t\t\u003ctd\u003e0.9.1.RELEASE\u003c/td\u003e\n\t\t\u003ctd\u003e\u003ca href=\"https://search.maven.org/artifact/io.r2dbc/r2dbc-h2\"\u003e\u003cimg src=\"https://img.shields.io/maven-central/v/io.r2dbc/r2dbc-h2.svg\"\u003c/a\u003e\u003c/td\u003e\n\t\u003c/tr\u003e\n\t\u003ctr\u003e\n\t\t\u003ctd\u003eMySql driver\u003c/td\u003e\n\t\t\u003ctd\u003edev.miku\u003c/td\u003e\n\t\t\u003ctd\u003er2dbc-mysql\u003c/td\u003e\n\t\t\u003ctd\u003e0.8.2.RELEASE\u003c/td\u003e\n\t\t\u003ctd\u003e\u003ca href=\"https://search.maven.org/artifact/dev.miku/r2dbc-mysql\"\u003e\u003cimg src=\"https://img.shields.io/maven-central/v/dev.miku/r2dbc-mysql.svg\"\u003c/a\u003e\u003c/td\u003e\n\t\u003c/tr\u003e\n\t\u003ctr\u003e\n\t\t\u003ctd\u003ePostgreSQL driver\u003c/td\u003e\n\t\t\u003ctd\u003eorg.postgresql\u003c/td\u003e\n\t\t\u003ctd\u003er2dbc-postgresql\u003c/td\u003e\n\t\t\u003ctd\u003e0.9.1.RELEASE\u003c/td\u003e\n\t\t\u003ctd\u003e\u003ca href=\"https://search.maven.org/artifact/org.postgresql/r2dbc-postgresql\"\u003e\u003cimg src=\"https://img.shields.io/maven-central/v/org.postgresql/r2dbc-postgresql.svg\"\u003c/a\u003e\u003c/td\u003e\n\t\u003c/tr\u003e\n\u003c/table\u003e\n\n# Configuration\n\n## Dependency configuration\n\nAdd the dependency to your project, depending on your database (you may add several if you are using multiple databases in your project):\n\n### H2\n\nMaven\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003enet.lecousin.reactive-data-relational\u003c/groupId\u003e\n  \u003cartifactId\u003eh2\u003c/artifactId\u003e\n  \u003cversion\u003e0.10.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nGradle\n```groovy\nimplementation group: 'net.lecousin.reactive-data-relational', name: 'h2', version: '0.10.2'\n```\n\n### Postgres\n\nMaven\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003enet.lecousin.reactive-data-relational\u003c/groupId\u003e\n  \u003cartifactId\u003epostgres\u003c/artifactId\u003e\n  \u003cversion\u003e0.10.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nGradle\n```groovy\nimplementation group: 'net.lecousin.reactive-data-relational', name: 'postgres', version: '0.10.2'\n```\n\n### MySql\n\nMaven\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003enet.lecousin.reactive-data-relational\u003c/groupId\u003e\n  \u003cartifactId\u003emysql\u003c/artifactId\u003e\n  \u003cversion\u003e0.10.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nGradle\n```groovy\nimplementation group: 'net.lecousin.reactive-data-relational', name: 'mysql', version: '0.10.2'\n```\n\n## Spring Boot configuration\n\nIn your Spring Boot application class, you need to:\n- add `@EnableR2dbcRepositories(repositoryFactoryBeanClass = LcR2dbcRepositoryFactoryBean.class)`\n- launch the initializer `LcReactiveDataRelationalInitializer.init()` that will add functionalities to your entity classes, before your application starts. This step **MUST** be done before Spring starts to ensure no entity class is loaded yet in the JVM.\n- configure your database\n\nExample:\n\n```java\n@SpringBootApplication\n@EnableR2dbcRepositories(repositoryFactoryBeanClass = LcR2dbcRepositoryFactoryBean.class)\n@Import(PostgresConfiguration.class) // here you can change depending on the database you are using\npublic class MyApp {\n\n\tpublic static void main(String[] args) {\n\t\tLcReactiveDataRelationalInitializer.init();\n\t\tSpringApplication.run(MyApp.class, args);\n\t}\n\n}\n```\n\nThe `@Import` annotation is used when using a single database connection, and your connection is configured through application properties (`application.properties` or `application.yml`). Depending on your database, you can use one of this configuration class:\n - `net.lecousin.reactive.data.relational.h2.H2Configuration`\n - `net.lecousin.reactive.data.relational.mysql.MySqlConfiguration`\n - `net.lecousin.reactive.data.relational.postgres.PostgresConfiguration`\n\nFinally, configure how to connect to the database using Spring R2DBC normal configuration, here is an example of application.yml file:\n\n```yaml\nspring:\n  r2dbc:\n    username: sa\n    url: r2dbc:h2:mem:///testdb;DB_CLOSE_DELAY=-1;\n```\n\n### Using custom connection factory\n\nIf you want to configure the connection programmatically instead of using application properties,\ninstead of using `@Import` annotation like in the previous example, you can extend the configuration class to provide your own `ConnectionFactory`:\n \n```java\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport io.r2dbc.h2.H2ConnectionConfiguration;\nimport io.r2dbc.h2.H2ConnectionFactory;\nimport net.lecousin.reactive.data.relational.h2.H2Configuration;\n\n@Configuration\npublic class H2TestConfiguration extends H2Configuration {\n\n\t@Override\n\t@Bean\n\tpublic H2ConnectionFactory connectionFactory() {\n\t\treturn new H2ConnectionFactory(\n\t\t\tH2ConnectionConfiguration.builder()\n\t\t\t.url(\"mem:testdb;DB_CLOSE_DELAY=-1;\")\n\t\t\t.username(\"sa\")\n\t\t\t.build()\n\t\t);\n\t}\n\t\n}\n```\n\n### Multiple databases\n\nIf you need to connect to multiple databases, the configuration is different. You need to create a `@Configuration` class for each database connection, extending class `LcR2dbcEntityOperationsBuilder`. Instead of declaring `@EnableR2dbcRepositories` directly on your application class, you will declare it to each configuration class.\n\nHere is an example of such a configuration class:\n\n```java\n@Configuration\n@EnableR2dbcRepositories(repositoryFactoryBeanClass = LcR2dbcRepositoryFactoryBean.class, basePackages = \"com.example.book.dao.repository\", entityOperationsRef = \"bookOperations\")\npublic class BookConfig extends LcR2dbcEntityOperationsBuilder {\n\n\t/** Connection factory. */\n\t@Bean\n\t@Qualifier(\"bookDatabaseConnectionFactory\")\n\tpublic ConnectionFactory bookDatabaseConnectionFactory(@Value(\"${database.book}\") String databaseUrl) {\n\t\treturn ConnectionFactories.get(databaseUrl);\n\t}\n\t\n\t/** Entity operations bean. */\n\t@Bean\n\t@Qualifier(\"bookOperations\")\n\tpublic LcR2dbcEntityTemplate bookOperations(@Qualifier(\"bookDatabaseConnectionFactory\") ConnectionFactory connectionFactory) {\n\t\treturn buildEntityOperations(connectionFactory);\n\t}\n\n}\n```\n\n- define a bean to create a `ConnectionFactory` (here we get a url from the application configuration, but you can create it in another way)\n- define a bean `LcR2dbcEntityTemplate` with the connection factory as argument\n- add the annotation `@EnableR2dbcRepositories` with the packages containing the repositories that will use this database, and the attribute `entityOperationsRef` set to the qualifier of the `LcR2dbcEntityTemplate` bean\n\nA complete example illustrating a Spring Boot application connecting to different databases is available in the repository [lc-spring-data-r2dbc-sample](https://github.com/lecousin/lc-spring-data-r2dbc-sample).\n\n\n### Application startup time\n\nBy default, when calling `LcReactiveDataRelationalInitializer.init()`, all classes present in the class path are analyzed to find entity classes.\nThis can take some time especially if you have many libraries in your class path.\n\nThis behavior allows no additional configuration, which make it easier during development. However for production, if it is important for your application to startup faster\n(for example a microservice), you can declare the list of entity classes in a YAML resource file `lc-reactive-data-relational.yaml` (similar to file `persistence.xml` for JPA).\nThe classes are declared under the name `entities`, each level can declare a package, then leaf names are classes. For example:\n\n```yaml\nentities:\n  - net.lecousin.reactive.data.relational.test:\n    - simplemodel:\n      - BooleanTypes\n      - CharacterTypes\n    - onetoonemodel:\n      - MyEntity1\n      - MySubEntity1\n    - onetomanymodel:\n      - RootEntity\n      - SubEntity\n      - SubEntity2\n      - SubEntity3\n```\n\nThe presence of this file will disable class path analysis. Note that you may have several resource files `lc-reactive-data-relational.yaml` in your class path (for example if several modules are providing entities), and all will be processed.\n\n## JUnit 5\n\nFor your tests, using JUnit 5, you can use the annotation `@DataR2dbcTest` provided by Spring, and add the annotation `@EnableR2dbcRepositories(repositoryFactoryBeanClass = LcR2dbcRepositoryFactoryBean.class)`.\n\nIn order to make sure the initializer is launched before any test class is loaded, add the following maven dependency, which will automatically call `LcReactiveDataRelationalInitializer.init()` during JUnit startup:\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003enet.lecousin.reactive-data-relational\u003c/groupId\u003e\n  \u003cartifactId\u003etest-junit-5\u003c/artifactId\u003e\n  \u003cversion\u003e0.10.2\u003c/version\u003e\n  \u003cscope\u003etest\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\n# Usage\n\nA more complete guide is available in the [wiki section](https://github.com/lecousin/lc-spring-data-r2dbc/wiki), here is an overview.\n\n## Entity class\n\nYour entity classes are declared using the Spring Data R2DBC annotations `@Table` and `@Column`:\n\n```java\n@Table\npublic class MyEntity {\n\t@Column\n\tprivate String myText;\n}\n```\n\nIn addition, here are the following supported annotations:\n- `org.springframework.data.annotation.Id` indicates a primary key\n- `net.lecousin.reactive.data.relational.annotations.CompositeId` indicates properties to use as a unique key (cannot be used together with @Id)\n- `net.lecousin.reactive.data.relational.annotations.GeneratedValue` indicates a value that should be generated by the database (auto increment, using a sequence, or generating a random UUID)\n- `org.springframework.data.annotation.Version` is used for optimistic lock\n- `net.lecousin.reactive.data.relational.annotations.ForeignKey` indicates a link with a foreign key stored in the table. The cascade behavior can be specified using the attributes `optional`, `onForeignDeleted`, and `cascadeDelete`. The type of the attribute in the class will be the linked entity class (not the foreign key itself). A foreign key cannot be used on a collection type.\n- `net.lecousin.reactive.data.relational.annotations.ForeignTable` is the other side of the foreign key. It does not store anything in the table\nbut indicates the link to another class to use with joins or lazy loading. A foreign table can be used on a collection type for a one to many link.\n- `net.lecousin.reactive.data.relational.annotations.JoinTable` can be used for a many to many (n-n) relationship when no additional field is required\non the join table. The join table will be automatically created with the 2 foreign keys. This allows to join directly between 2 tables with many to many relationship in a\ntransparent manner.\n- `org.springframework.data.annotation.CreatedDate` and `org.springframework.data.annotation.LastModifiedDate` can be used to automatically store respectively the creation date and modification date. It can be used with a column of type `Long`, `Instant`, `LocalDate`, `LocalTime` or `LocalDateTime`. `OffsetTime` and `ZonedDateTime` can be used except for MySql that does not support columns with timezone information.\n- `net.lecousin.reactive.data.relational.annotations.ColumnDefinition` allows to specify constraints for schema generation.\n\nAdditional methods may be declared in an Entity class to handle lazy loading, documented in the [dedicated section](#lazy-loading).\n\nExample:\n\n```java\n@Table\npublic class MyEntity {\n\t@Id @GeneratedValue\n\tprivate Long id;\n\t\n\t@ColumnDefinition(max = 100, nullable = true)\n\tprivate String someOptionalText;\n\t\n\t@ForeignKey(optional = false)\n\tprivate MyOtherEntity linkedEntity;\n\t\n\t[...]\n}\n```\n\n### Important note for Java \u003e 11\n\nIf you use a Java version \u003e 11, the JVM does not allow to modify classes in another package, and this library won't be able to enhance your classes to provide all the functionalities. As a workaround for now, you must explicitly allow it by placing an empty interface named `AllowEnhancer` in **every package containing entities**.\n\nYou can just create it like this:\n\n```java\npublic interface AllowEnhancer {\n}\n```\n\n## Spring Repository\n\nYou can use Spring repositories as usual.\nThe methods to save and delete (such as `save`, `saveAll`, `delete`, `deleteAll`) will automatically be done with cascade.\nThe select methods (findXXX) do not perform any join by default, but allow lazy loading. However joins can be done using `SelectQuery`. This is described in the 2 following sections.\n\n## Lazy loading\n\nSpring Repository methods to find entities (`find...`) do not perform any join. If your class has links to other classes, they won't be loaded, however this library supports\n_lazy loading_ \nwhich will load the linked entities on demand.\n\nLazy loading is done by declaring additional methods on your entity class, with a default body. **The body will be automatically replaced with the correct code**.\n\nHere is an example of an entity enabling lazy loading:\n\n```java\n@Table\npublic class MyEntity {\n\n\t@Id @GeneratedValue\n\tprivate Long id;\n\n\t@ForeignKey(optional = false)\n\tprivate MyOtherEntity other;\n\n\t@ForeignTable(joinKey = \"myEntity\")\n\tprivate List\u003cJoinEntity\u003e links;\n\n\t/* The usual getters and setters. */\n\n\tpublic MyOtherEntity getOther() {\n\t\treturn other;\n\t}\n\t\n\tpublic void setOther(MyOtherEntity other) {\n\t\tthis.other = other;\n\t}\n\t\n\tpublic List\u003cJoinEntity\u003e getLinks() {\n\t\treturn links;\n\t}\n\n\tpublic void setLinks(List\u003cJoinEntity\u003e links) {\n\t\tthis.links = links;\n\t}\n\t\n\t/* Lazy loading methods. */\n\n\tpublic Mono\u003cMyOtherEntity\u003e lazyGetOther() {\n\t\treturn null; // you can just return null, the correct code will be automatically generated.\n\t}\n\n\tpublic Flux\u003cJoinEntity\u003e lazyGetLinks() {\n\t\treturn null; // you can just return null, the correct code will be automatically generated.\n\t}\n\t\n\t/* Lazy loading of this MyEntity (this class). */\n\t\n\t/** @return true if this MyEntity is loaded from database, false if only the @Id is available. */\n\tpublic boolean entityLoaded() {\n\t\treturn false; // you can just return false, the correct code will be automatically generated.\n\t}\n\t\n\t/** Ensure this MyEntity is loaded from database before to use. */\n\tpublic Mono\u003cMyEntity\u003e loadEntity() {\n\t\treturn null; // you can just return null, the correct code will be automatically generated.\n\t}\n\t\n}\n```\n\nAs illustrated by this example, you can define the following methods to handle lazy loading on an entity class:\n- methods `public Mono\u003cT\u003e lazyGetXXX() { return null; }` to get a loaded entity on a @ForeignKey or @ForeignTable field XXX.\n- For collections, the same method can be declared with a `Flux`: `public Flux\u003cT\u003e lazyGetXXX() { return null; }` to get entities from a collection with @ForeignTable\n- a method `public boolean entityLoaded() { return false; }` to know if the entity instance is fully loaded or not.\n- a method `public Mono\u003cT\u003e loadEntity() { return null; }` where T is your entity class, to load the instance from the database. If the entity is already loaded, `Mono.just(this)` is returned (no redundant database query on each call).\n\nThe body of those methods will be automatically generated during enhancement (when calling `LcReactiveDataRelationalInitializer.init()` at startup), that's why you can just return null or false in your code.\n\nUsing methods `lazyGetXXX` or `loadEntity` allow to ensure the entity or attributes are loaded. If already loaded, nothing is done, else a database request is done. That's why\na `Mono` or `Flux` is returned, so you can perform your actions in a non-blocking mode even if a database request needs to be executed.\n\nIn case an entity or attribute is not loaded, and you are not using those methods, here is the behavior you can expect:\n- On a @ForeignKey attribute, a class will be instantiated with the foreign key as id, all other attributes are not set. That means your attribute is not null (using classical getter)\nbut all its attributes are null except its primary key which is pre-filled. In other words you can get the foreign key value without needing to load the full entity.\n- On a @ForeignTable attribute, the attribute will be null. The reason is that the database table of your entity does not store any information about this link (unlike when having a foreign key).\n\nNote that all those methods are completely optional. If you define some of those methods, the enhancer will generate the corresponding code, else the method will just not be available.\n\n## Select with joins\n\nLazy loading is often not a good solution in term of performance, and we may want to load linked entities in a single database request using joins.\n\nIn JPA, entity graphs are used to specify the attributes to fetch automatically. This library does not provide similar way to indicate which links need to be loaded,\nbut you can use the class `SelectQuery` to perform more complex searches and load linked entities.\n\nThe `SelectQuery` can be used anywhere, including in your Spring Data repositories as default method (note that default methods in Spring Data repositories is not supported with Kotlin).\n\nFor example:\n\n```java\npublic interface RootEntityRepository extends LcR2dbcRepository\u003cRootEntity, Long\u003e {\n\n\t/** Search RootEntity having the given value, and fetch sub entities 'list'. */\n\tdefault Flux\u003cRootEntity\u003e findByValue(String value) {\n\t\treturn SelectQuery\n\t\t.from(RootEntity.class, \"entity\")                      // SELECT entity FROM RootEntity AS entity\n\t\t.where(Criteria.property(\"entity\", \"value\").is(value)) // WHERE entity.value = :value\n\t\t.join(\"entity\", \"list\", \"sub\")                         // JOIN entity.list AS sub\n\t\t.execute(getLcClient());\n\t}\n\t\n\t/** Search RootEntity with a sub-entity having the given value, and fetch sub entities. */\n\tdefault Flux\u003cRootEntity\u003e findBySubValue(String value) {\n\t\treturn SelectQuery\n\t\t.from(RootEntity.class, \"entity\")                      // SELECT entity FROM RootEntity AS entity\n\t\t.join(\"entity\", \"list\", \"sub\")                         // JOIN entity.list AS sub\n\t\t.where(Criteria.property(\"sub\", \"subValue\").is(value)) // WHERE sub.subValue = :value\n\t\t.execute(getLcClient());\n\t}\n\t\n\t/** Count RootEntity with a sub-entity having the given value. */\n\tdefault Mono\u003cLong\u003e findBySubValue(String value) {\n\t\treturn SelectQuery\n\t\t.from(RootEntity.class, \"entity\")                      // SELECT entity FROM RootEntity AS entity\n\t\t.join(\"entity\", \"list\", \"sub\")                         // JOIN entity.list AS sub\n\t\t.where(Criteria.property(\"sub\", \"subValue\").is(value)) // WHERE sub.subValue = :value\n\t\t.executeCount(getLcClient());\n\t}\n\n\t/** Search RootEntity having the same value as one of its sub-entities. */\t\n\tdefault Flux\u003cRootEntity\u003e havingSubValueEqualsToValue() {\n\t\treturn SelectQuery\n\t\t.from(RootEntity.class, \"entity\")                      // SELECT entity FROM RootEntity AS entity\n\t\t.join(\"entity\", \"list\", \"sub\")                         // JOIN entity.list AS sub\n\t\t.where(Criteria.property(\"sub\", \"subValue\").is(\"entity\", \"value\")) // WHERE sub.subValue = entity.value\n\t\t.execute(getLcClient());\n\t}\n\t\n\t/** Get all RootEntity and fetch links 'list', 'list2' and 'list3'. */\n\tdefault Flux\u003cRootEntity\u003e findAllFull() {\n\t\treturn getLcClient().execute(\n\t\t\tSelectQuery.from(RootEntity.class, \"root\")         // SELECT root FROM RootEntity as root\n\t\t\t.join(\"root\", \"list\", \"sub1\")                      // JOIN root.list as sub1\n\t\t\t.join(\"root\", \"list2\", \"sub2\")                     // JOIN root.list2 as sub2\n\t\t\t.join(\"root\", \"list3\", \"sub3\")                     // JOIN root.list3 as sub3\n\t\t);\n\t}\n\n}\n```\n\nYou can note the method `getLcClient()` needed to execute requests, which is automatically available if your repository extends `LcR2dbcRepository`.\nIf you don't need it, your repository can just extend the `R2dbcRepository` base interface of Spring.\n\n`SelectQuery` can be used anywhere, not only in a repository, in this case you will need the client instance that you can inject in your class:\n\n```java\n\t@Autowired\n\tprivate LcReactiveDataRelationalClient lcClient;\n```\n\nIn case you have multiple database connections, you can inject the entity template configured for a specific database connection, then get the client using `template.getLcClient()`:\n\n```java\n\t@Autowired\n\t@Qualifier(\"bookOperations\")\n\tprivate LcR2dbcEntityTemplate template;\n```\n\n# Resources\n\n- [Spring Data R2DBC documentation](https://spring.io/projects/spring-data-r2dbc) \n\n- [Wiki](https://github.com/lecousin/lc-spring-data-r2dbc/wiki)\n\n- Example application: A complete example illustrating a Spring Boot application connecting to different databases is available in the repository [lc-spring-data-r2dbc-sample](https://github.com/lecousin/lc-spring-data-r2dbc-sample), and comes with an Angular GUI to test it.\n\n# License\n\nlc-spring-data-r2dbc is Open Source software released under the [Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0.html).\n\n# Contributing\n\nAny contribution is welcome !\n\nDo not hesitate to start a [discussion](https://github.com/lecousin/lc-spring-data-r2dbc/discussions) about new features you would like to contribute to, or to open [an issue](https://github.com/lecousin/lc-spring-data-r2dbc/issues) if you encounter a bug.\n\nContributions to improve the documentation are also welcome.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flecousin%2Flc-spring-data-r2dbc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flecousin%2Flc-spring-data-r2dbc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flecousin%2Flc-spring-data-r2dbc/lists"}