diff --git a/doc-examples/jdbc-example-records-groovy/build.gradle b/doc-examples/jdbc-example-records-groovy/build.gradle new file mode 100644 index 0000000000..41385f2886 --- /dev/null +++ b/doc-examples/jdbc-example-records-groovy/build.gradle @@ -0,0 +1,28 @@ +plugins { + id "groovy" + id "io.micronaut.build.internal.data-example" +} + +application { + mainClass = "example.Application" +} + +micronaut { + version libs.versions.micronaut.platform.get() + runtime "netty" + testRuntime "spock" +} + +dependencies { + compileOnly projects.micronautDataProcessor + + implementation projects.micronautDataJdbc + implementation mnSerde.micronaut.serde.support + implementation mn.micronaut.http.client + implementation mnValidation.micronaut.validation + implementation(libs.managed.jakarta.persistence.api) + + runtimeOnly mnSql.micronaut.jdbc.tomcat + runtimeOnly mnLogging.logback.classic + runtimeOnly mnSql.h2 +} diff --git a/doc-examples/jdbc-example-records-groovy/gradle.properties b/doc-examples/jdbc-example-records-groovy/gradle.properties new file mode 100644 index 0000000000..2f7c48213f --- /dev/null +++ b/doc-examples/jdbc-example-records-groovy/gradle.properties @@ -0,0 +1 @@ +skipDocumentation=true diff --git a/doc-examples/jdbc-example-records-groovy/src/main/groovy/example/Book.groovy b/doc-examples/jdbc-example-records-groovy/src/main/groovy/example/Book.groovy new file mode 100644 index 0000000000..88664db49f --- /dev/null +++ b/doc-examples/jdbc-example-records-groovy/src/main/groovy/example/Book.groovy @@ -0,0 +1,29 @@ + +package example + +import io.micronaut.core.annotation.Nullable +import io.micronaut.data.annotation.DateCreated +import io.micronaut.data.annotation.MappedEntity +import jakarta.persistence.* + +@MappedEntity // <1> +class Book { + @Id @GeneratedValue Long id // <2> + @DateCreated @Nullable Date dateCreated + + private String title + private int pages + + Book(String title, int pages) { + this.title = title + this.pages = pages + } + + String getTitle() { + return title + } + + int getPages() { + return pages + } +} diff --git a/doc-examples/jdbc-example-records-groovy/src/main/groovy/example/BookDTO.groovy b/doc-examples/jdbc-example-records-groovy/src/main/groovy/example/BookDTO.groovy new file mode 100644 index 0000000000..9b1087c0a4 --- /dev/null +++ b/doc-examples/jdbc-example-records-groovy/src/main/groovy/example/BookDTO.groovy @@ -0,0 +1,11 @@ + +package example + +import io.micronaut.serde.annotation.Serdeable + +@Serdeable +class BookDTO { + + String title + int pages +} diff --git a/doc-examples/jdbc-example-records-groovy/src/main/groovy/example/BookRepository.groovy b/doc-examples/jdbc-example-records-groovy/src/main/groovy/example/BookRepository.groovy new file mode 100644 index 0000000000..6aebd45bf0 --- /dev/null +++ b/doc-examples/jdbc-example-records-groovy/src/main/groovy/example/BookRepository.groovy @@ -0,0 +1,114 @@ + +// tag::repository[] +package example + +import io.micronaut.core.annotation.NonNull +import io.micronaut.data.annotation.* +import io.micronaut.data.annotation.sql.Procedure +import io.micronaut.data.jdbc.annotation.JdbcRepository +import io.micronaut.data.model.* +import io.micronaut.data.model.query.builder.sql.Dialect +import io.micronaut.data.repository.CrudRepository +import java.util.List + + +@JdbcRepository(dialect = Dialect.H2) // <1> +interface BookRepository extends CrudRepository { // <2> +// end::repository[] + + // tag::simple[] + Book findByTitle(String title); + + Book getByTitle(String title); + + Book retrieveByTitle(String title); + // end::simple[] + + // tag::greaterthan[] + List findByPagesGreaterThan(int pageCount); + // end::greaterthan[] + + // tag::logical[] + List findByPagesGreaterThanOrTitleLike(int pageCount, String title); + // end::logical[] + + // tag::simple-alt[] + // tag::repository[] + Book find(String title); + // end::simple-alt[] + // end::repository[] + + // tag::pageable[] + List findByPagesGreaterThan(int pageCount, Pageable pageable); + + Page findByTitleLike(String title, Pageable pageable); + + Slice list(Pageable pageable); + // end::pageable[] + + // tag::simple-projection[] + List findTitleByPagesGreaterThan(int pageCount); + // end::simple-projection[] + + // tag::top-projection[] + List findTop3ByTitleLike(String title); + // end::top-projection[] + + // tag::ordering[] + List listOrderByTitle(); + + List listOrderByTitleDesc(); + // end::ordering[] + + // tag::explicit[] + @Query("SELECT * FROM Book AS b WHERE b.title = :t ORDER BY b.title") + List listBooks(String t); + // end::explicit[] + + // tag::save[] + Book persist(Book entity); + // end::save[] + + // tag::save2[] + Book persist(String title, int pages); + // end::save2[] + + // tag::update[] + void update(@Id Long id, int pages); + + void update(@Id Long id, String title); + // end::update[] + + // tag::update2[] + void updateByTitle(String title, int pages); + // end::update2[] + + // tag::deleteall[] + void deleteAll(); + // end::deleteall[] + + // tag::deleteone[] + void delete(String title); + // end::deleteone[] + + // tag::deleteby[] + void deleteByTitleLike(String title); + // end::deleteby[] + + // tag::dto[] + BookDTO findOne(String title); + // end::dto[] + + // tag::native[] + @Query("select * from book b where b.title like :title limit 5") + List findBooks(String title); + // end::native[] + + // tag::procedure[] + @Procedure + Long calculateSum(@NonNull Long bookId); + // end::procedure[] + +// tag::repository[] +} +// end::repository[] diff --git a/doc-examples/jdbc-example-records-groovy/src/main/resources/application.yml b/doc-examples/jdbc-example-records-groovy/src/main/resources/application.yml new file mode 100644 index 0000000000..a38a6f0050 --- /dev/null +++ b/doc-examples/jdbc-example-records-groovy/src/main/resources/application.yml @@ -0,0 +1,8 @@ +datasources: + default: + url: jdbc:h2:mem:devDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE + driverClassName: org.h2.Driver + username: sa + password: '' + schema-generate: CREATE_DROP + dialect: H2 diff --git a/doc-examples/jdbc-example-records-groovy/src/test/groovy/example/BookRepositorySpec.groovy b/doc-examples/jdbc-example-records-groovy/src/test/groovy/example/BookRepositorySpec.groovy new file mode 100644 index 0000000000..7c7e1e26dc --- /dev/null +++ b/doc-examples/jdbc-example-records-groovy/src/test/groovy/example/BookRepositorySpec.groovy @@ -0,0 +1,59 @@ +package example + +import io.micronaut.context.BeanContext +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import spock.lang.Shared +import spock.lang.Specification + +import jakarta.inject.Inject + +@MicronautTest +class BookRepositorySpec extends Specification { + + @Inject @Shared BookRepository bookRepository + + @Inject + BeanContext beanContext + + void 'test CRUD operations'() { + + when: "Create: Save a new book" + + // tag::save[] + Book book = new Book("The Stand", 1000) + bookRepository.save(book) + // end::save[] + Long id = book.id + + then: "An ID was assigned" + id != null + + when: "Read a book from the database" + // tag::read[] + book = bookRepository.findById(id).orElse(null) + // end::read[] + + then:"The book was read" + book != null + book.title == 'The Stand' + + // Check the count + bookRepository.count() == 1 + bookRepository.findAll().iterator().hasNext() + + when: "The book is updated" + // tag::update[] + bookRepository.update(book.getId(), "Changed") + // end::update[] + book = bookRepository.findById(id).orElse(null) + then: "The title was changed" + book.title == 'Changed' + + when: "The book is deleted" + // tag::delete[] + bookRepository.deleteById(id) + // end::delete[] + then:"It is gone" + bookRepository.count() == 0 + } +} diff --git a/doc-examples/jdbc-example-records-groovy/src/test/resources/logback.xml b/doc-examples/jdbc-example-records-groovy/src/test/resources/logback.xml new file mode 100644 index 0000000000..abf9f39e7e --- /dev/null +++ b/doc-examples/jdbc-example-records-groovy/src/test/resources/logback.xml @@ -0,0 +1,12 @@ + + + + + %cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n + + + + + + + diff --git a/doc-examples/jdbc-example-records-java/build.gradle b/doc-examples/jdbc-example-records-java/build.gradle index 812e9ec341..78c83ed55f 100644 --- a/doc-examples/jdbc-example-records-java/build.gradle +++ b/doc-examples/jdbc-example-records-java/build.gradle @@ -17,6 +17,7 @@ dependencies { annotationProcessor mnValidation.micronaut.validation implementation projects.micronautDataJdbc + implementation mnSerde.micronaut.serde.support implementation mn.micronaut.http.client implementation mnValidation.micronaut.validation implementation(libs.managed.jakarta.persistence.api) diff --git a/doc-examples/jdbc-example-records-java/src/main/java/example/BookDTO.java b/doc-examples/jdbc-example-records-java/src/main/java/example/BookDTO.java index a24e8c86a7..ea11a13158 100644 --- a/doc-examples/jdbc-example-records-java/src/main/java/example/BookDTO.java +++ b/doc-examples/jdbc-example-records-java/src/main/java/example/BookDTO.java @@ -1,9 +1,9 @@ package example; -import io.micronaut.core.annotation.Introspected; +import io.micronaut.serde.annotation.Serdeable; -@Introspected +@Serdeable public class BookDTO { private String title; diff --git a/doc-examples/jdbc-example-records-kotlin/build.gradle b/doc-examples/jdbc-example-records-kotlin/build.gradle new file mode 100644 index 0000000000..37a4648830 --- /dev/null +++ b/doc-examples/jdbc-example-records-kotlin/build.gradle @@ -0,0 +1,34 @@ +plugins { + id "org.jetbrains.kotlin.jvm" + id "org.jetbrains.kotlin.kapt" + id "org.jetbrains.kotlin.plugin.allopen" + id "io.micronaut.build.internal.data-kotlin-example" +} + +application { + mainClass = "example.ApplicationKt" +} + +micronaut { + version libs.versions.micronaut.platform.get() + runtime "netty" + testRuntime "junit5" +} + +dependencies { + kapt projects.micronautDataProcessor + kapt mnValidation.micronaut.validation + + implementation mnSerde.micronaut.serde.support + implementation projects.micronautDataJdbc + implementation mn.micronaut.http.client + implementation mnValidation.micronaut.validation + implementation libs.managed.jakarta.persistence.api + + implementation libs.kotlin.coroutines + implementation libs.kotlin.coroutines.reactive + + runtimeOnly mnSql.micronaut.jdbc.tomcat + runtimeOnly mnLogging.logback.classic + runtimeOnly mnSql.h2 +} diff --git a/doc-examples/jdbc-example-records-kotlin/gradle.properties b/doc-examples/jdbc-example-records-kotlin/gradle.properties new file mode 100644 index 0000000000..2f7c48213f --- /dev/null +++ b/doc-examples/jdbc-example-records-kotlin/gradle.properties @@ -0,0 +1 @@ +skipDocumentation=true diff --git a/doc-examples/jdbc-example-records-kotlin/src/main/kotlin/example/Book.kt b/doc-examples/jdbc-example-records-kotlin/src/main/kotlin/example/Book.kt new file mode 100644 index 0000000000..dbf9efa622 --- /dev/null +++ b/doc-examples/jdbc-example-records-kotlin/src/main/kotlin/example/Book.kt @@ -0,0 +1,14 @@ +package example + +import io.micronaut.core.annotation.Nullable +import io.micronaut.data.annotation.* +import java.util.* + +@MappedEntity // (1) +data class Book(@Id + @field:Id @GeneratedValue var id: Long?, // (2) + @DateCreated @Nullable var dateCreated: Date? = null, + var title: String, + var pages: Int = 0) { + constructor(title: String, pages: Int) : this(null, null, title, pages) +} diff --git a/doc-examples/jdbc-example-records-kotlin/src/main/kotlin/example/BookDTO.kt b/doc-examples/jdbc-example-records-kotlin/src/main/kotlin/example/BookDTO.kt new file mode 100644 index 0000000000..a19827e245 --- /dev/null +++ b/doc-examples/jdbc-example-records-kotlin/src/main/kotlin/example/BookDTO.kt @@ -0,0 +1,9 @@ +package example + +import io.micronaut.serde.annotation.Serdeable + +@Serdeable +data class BookDTO( + var title: String, + var pages: Int +) diff --git a/doc-examples/jdbc-example-records-kotlin/src/main/kotlin/example/BookRepository.kt b/doc-examples/jdbc-example-records-kotlin/src/main/kotlin/example/BookRepository.kt new file mode 100644 index 0000000000..8e5e9f646d --- /dev/null +++ b/doc-examples/jdbc-example-records-kotlin/src/main/kotlin/example/BookRepository.kt @@ -0,0 +1,123 @@ + +// tag::repository[] +package example + +import io.micronaut.context.annotation.Executable +import io.micronaut.core.annotation.NonNull +import io.micronaut.data.annotation.Id +import io.micronaut.data.annotation.Query +import io.micronaut.data.annotation.sql.Procedure +import io.micronaut.data.jdbc.annotation.JdbcRepository +import io.micronaut.data.model.Page +import io.micronaut.data.model.Pageable +import io.micronaut.data.model.Slice +import io.micronaut.data.model.query.builder.sql.Dialect +import io.micronaut.data.repository.CrudRepository +import jakarta.transaction.Transactional + +@JdbcRepository(dialect = Dialect.H2) // <1> +interface BookRepository : CrudRepository { // <2> +// end::repository[] + + // tag::simple[] + fun findByTitle(title: String): Book + + fun getByTitle(title: String): Book + + fun retrieveByTitle(title: String): Book + // end::simple[] + + // tag::simple-alt[] + // tag::repository[] + @Executable + fun find(title: String): Book + // end::simple-alt[] + // end::repository[] + + // tag::greaterthan[] + fun findByPagesGreaterThan(pageCount: Int): List + // end::greaterthan[] + + // tag::logical[] + fun findByPagesGreaterThanOrTitleLike(pageCount: Int, title: String): List + // end::logical[] + + // tag::pageable[] + fun findByPagesGreaterThan(pageCount: Int, pageable: Pageable): List + + fun findByTitleLike(title: String, pageable: Pageable): Page + + fun list(pageable: Pageable): Slice + // end::pageable[] + + // tag::simple-projection[] + fun findTitleByPagesGreaterThan(pageCount: Int): List + // end::simple-projection[] + + // tag::top-projection[] + fun findTop3ByTitleLike(title: String): List + // end::top-projection[] + + // tag::ordering[] + fun listOrderByTitle(): List + + fun listOrderByTitleDesc(): List + // end::ordering[] + + // tag::explicit[] + @Query("SELECT * FROM book as b WHERE b.title = :t ORDER BY b.title") + fun listBooks(t: String): List + // end::explicit[] + + // tag::save[] + fun persist(entity: Book): Book + // end::save[] + + // tag::save2[] + fun persist(title: String, pages: Int): Book + // end::save2[] + + // tag::update[] + fun update(@Id id: Long?, pages: Int) + + fun update(@Id id: Long?, title: String) + // end::update[] + + // tag::update2[] + fun updateByTitle(title: String, pages: Int) + // end::update2[] + + // tag::deleteall[] + override fun deleteAll() + // end::deleteall[] + + @Transactional(Transactional.TxType.MANDATORY) + fun deleteAllRequiresTx() { + deleteAll() + } + + // tag::deleteone[] + fun delete(title: String) + // end::deleteone[] + + // tag::deleteby[] + fun deleteByTitleLike(title: String) + // end::deleteby[] + + // tag::dto[] + fun findOne(title: String): BookDTO + // end::dto[] + + // tag::native[] + @Query("select * from book b where b.title like :title limit 5") + fun findBooks(title: String): List + // end::native[] + + // tag::procedure[] + @Procedure + fun calculateSum(bookId: @NonNull Long): Long + // end::procedure[] + +// tag::repository[] +} +// end::repository[] diff --git a/doc-examples/jdbc-example-records-kotlin/src/main/resources/application.yml b/doc-examples/jdbc-example-records-kotlin/src/main/resources/application.yml new file mode 100644 index 0000000000..a38a6f0050 --- /dev/null +++ b/doc-examples/jdbc-example-records-kotlin/src/main/resources/application.yml @@ -0,0 +1,8 @@ +datasources: + default: + url: jdbc:h2:mem:devDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE + driverClassName: org.h2.Driver + username: sa + password: '' + schema-generate: CREATE_DROP + dialect: H2 diff --git a/doc-examples/jdbc-example-records-kotlin/src/test/kotlin/example/BookRepositorySpec.kt b/doc-examples/jdbc-example-records-kotlin/src/test/kotlin/example/BookRepositorySpec.kt new file mode 100644 index 0000000000..192c0d6091 --- /dev/null +++ b/doc-examples/jdbc-example-records-kotlin/src/test/kotlin/example/BookRepositorySpec.kt @@ -0,0 +1,132 @@ +package example + +import io.micronaut.context.BeanContext +import io.micronaut.data.annotation.Query +import io.micronaut.data.model.Pageable +import io.micronaut.test.extensions.junit5.annotation.MicronautTest +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import java.util.* +import jakarta.inject.Inject +import org.junit.jupiter.api.BeforeEach + +@MicronautTest +class BookRepositorySpec { + + // tag::inject[] + @Inject + lateinit var bookRepository: BookRepository + // end::inject[] + + // tag::metadata[] + @Inject + lateinit var beanContext: BeanContext + + @BeforeEach + fun cleanup() { + bookRepository.deleteAll() + } + + @Test + fun testAnnotationMetadata() { + val query = beanContext.getBeanDefinition(BookRepository::class.java) // <1> + .getRequiredMethod("find", String::class.java) // <2> + .annotationMetadata + .stringValue(Query::class.java) // <3> + .orElse(null) + + + assertEquals( // <4> + "SELECT book_.`id`,book_.`date_created`,book_.`title`,book_.`pages` FROM `book` book_ WHERE (book_.`title` = ?)", + query + ) + + } + // end::metadata[] + + @Test + fun testCrud() { + assertNotNull(bookRepository) + + // Create: Save a new book + // tag::save[] + var book = Book(0, null, "The Stand", 1000) + bookRepository.save(book) + // end::save[] + + val id = book.id + assertNotNull(id) + + // Read: Read a book from the database + // tag::read[] + book = bookRepository.findById(id).orElse(null) + // end::read[] + assertNotNull(book) + assertEquals("The Stand", book.title) + + // Check the count + assertEquals(1, bookRepository.count()) + assertTrue(bookRepository.findAll().iterator().hasNext()) + + // Update: Update the book and save it again + // tag::update[] + bookRepository.update(book.id, "Changed") + // end::update[] + book = bookRepository.findById(id).orElse(null) + assertEquals("Changed", book.title) + + // Delete: Delete the book + // tag::delete[] + bookRepository.deleteById(id) + // end::delete[] + assertEquals(0, bookRepository.count()) + } + + @Test + fun testPageable() { + // tag::saveall[] + bookRepository.saveAll(Arrays.asList( + Book(0, null,"The Stand", 1000), + Book(0, null,"The Shining", 600), + Book(0, null,"The Power of the Dog", 500), + Book(0, null,"The Border", 700), + Book(0, null,"Along Came a Spider", 300), + Book(0, null,"Pet Cemetery", 400), + Book(0, null,"A Game of Thrones", 900), + Book(0, null,"A Clash of Kings", 1100) + )) + // end::saveall[] + + // tag::pageable[] + val slice = bookRepository.list(Pageable.from(0, 3)) + val resultList = bookRepository.findByPagesGreaterThan(500, Pageable.from(0, 3)) + val page = bookRepository.findByTitleLike("The%", Pageable.from(0, 3)) + // end::pageable[] + + assertEquals( + 3, + slice.numberOfElements + ) + assertEquals( + 3, + resultList.size + ) + assertEquals( + 3, + page.numberOfElements + ) + assertEquals( + 4, + page.totalSize + ) + + } + + @Test + fun testDto() { + bookRepository.save(Book(0, null, "The Shining", 400)) + val bookDTO = bookRepository.findOne("The Shining") + + assertEquals("The Shining", bookDTO.title) + } +} diff --git a/doc-examples/jdbc-example-records-kotlin/src/test/resources/logback.xml b/doc-examples/jdbc-example-records-kotlin/src/test/resources/logback.xml new file mode 100644 index 0000000000..abf9f39e7e --- /dev/null +++ b/doc-examples/jdbc-example-records-kotlin/src/test/resources/logback.xml @@ -0,0 +1,12 @@ + + + + + %cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n + + + + + + + diff --git a/settings.gradle b/settings.gradle index b15908736e..569aff82cd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -91,6 +91,8 @@ include 'doc-examples:jdbc-example-java' include 'doc-examples:jdbc-example-groovy' include 'doc-examples:jdbc-example-kotlin' include 'doc-examples:jdbc-example-records-java' +include 'doc-examples:jdbc-example-records-groovy' +include 'doc-examples:jdbc-example-records-kotlin' include 'doc-examples:jdbc-multitenancy-datasource-example-java' include 'doc-examples:jdbc-multitenancy-schema-example-java' diff --git a/src/main/docs/guide/dbc/javaRecords.adoc b/src/main/docs/guide/dbc/javaRecords.adoc deleted file mode 100644 index c5fcb692bf..0000000000 --- a/src/main/docs/guide/dbc/javaRecords.adoc +++ /dev/null @@ -1,15 +0,0 @@ -Since 2.3.0, Micronaut Data JDBC / R2DBC has support for using Java 16 records to model entities. - -The following record class demonstrates this capability: - -snippet::example.Book[project-base="doc-examples/jdbc-example-records", source="main"] - -<1> The ann:data.annotation.MappedEntity[] annotation is used on the record -<2> The database identifier is annotated with ann:data.annotation.Id[] and ann:data.annotation.GeneratedValue[] plus marked as `@Nullable` - -Since records are immutable constructor arguments that are generated values need to be marked as `@Nullable` and you should pass `null` for those arguments. The following presents an example: - -snippet::example.BookRepositorySpec[project-base="doc-examples/jdbc-example-records", tags="save", indent="0"] - -It is important to note that the returned instance is not the same as the instance passed to the `save` method. When a write operation is performed Micronaut Data will use a copy-constructor approach to populate the database identifier and return a new instance from the `save` method. - diff --git a/src/main/docs/guide/dbc/javaRecordsKotlinDataClasses.adoc b/src/main/docs/guide/dbc/javaRecordsKotlinDataClasses.adoc new file mode 100644 index 0000000000..8139375789 --- /dev/null +++ b/src/main/docs/guide/dbc/javaRecordsKotlinDataClasses.adoc @@ -0,0 +1,15 @@ +Since 2.3.0, Micronaut Data JDBC / R2DBC has support for using Java 16 records to model entities. For Kotlin projects Micronaut Data JDBC / R2DBC supports using immutable data classes as model entities. With Groovy POGOs there is not a need for using Java records. + +The following example illustrates demonstrates these capabilities: + +snippet::example.Book[project-base="doc-examples/jdbc-example-records", source="main"] + +<1> The ann:data.annotation.MappedEntity[] annotation is used on the Java record. For Kotlin a data class is used instead, and for Groovy a standard POGO. +<2> The database identifier is annotated with ann:data.annotation.Id[] and ann:data.annotation.GeneratedValue[] plus marked as `@Nullable` + +Since records are immutable constructor arguments that are generated values need to be marked as `@Nullable` and you should pass `null` for those arguments. Kotlin data classes are implemented similarly: to modify an entity a copy-constructor is used and every modification means a new entity instance. Groovy uses an idiomatic POGO. The following example illustrates this: + +snippet::example.BookRepositorySpec[project-base="doc-examples/jdbc-example-records", tags="save", indent="0"] + +NOTE: It is important to keep in mind that for Java records and Kotlin data classes returned instances are not the same objects passed to the `save` method. When a write operation is performed Micronaut Data uses a copy-constructor approach to populate the database identifier and return a new instance from the `save` method. + diff --git a/src/main/docs/guide/dbc/kotlinData.adoc b/src/main/docs/guide/dbc/kotlinData.adoc deleted file mode 100644 index a809939f62..0000000000 --- a/src/main/docs/guide/dbc/kotlinData.adoc +++ /dev/null @@ -1,28 +0,0 @@ -Micronaut Data JDBC / R2DBC supports using immutable Kotlin data classes as model entities. -The implementation is the same as for Java 16 records: to modify an entity a copy-constructor will be used and every modification means a new entity instance. - -[source, kotlin] -.src/main/kotlin/example/Student.kt ----- -package example - -import io.micronaut.data.annotation.GeneratedValue -import io.micronaut.data.annotation.Id -import io.micronaut.data.annotation.MappedEntity -import io.micronaut.data.annotation.Relation - -@MappedEntity -data class Student( - @field:Id @GeneratedValue - val id: Long?, - val name: String, - @Relation(value = Relation.Kind.MANY_TO_MANY, cascade = [Relation.Cascade.PERSIST]) - val courses: List, - @Relation(value = Relation.Kind.ONE_TO_MANY, mappedBy = "student") - val ratings: List -) { - constructor(name: String, items: List) : this(null, name, items, emptyList()) -} ----- - -NOTE: Generated values and relations that cannot be created during the entity initialization should be declared as nullable. diff --git a/src/main/docs/guide/toc.yml b/src/main/docs/guide/toc.yml index 6108eccca6..43ea2a8be6 100644 --- a/src/main/docs/guide/toc.yml +++ b/src/main/docs/guide/toc.yml @@ -87,8 +87,7 @@ dbc: mappedPropertyAlias: Using @MappedProperty alias sqlJsonType: JSON Column Support sqlJsonView: JSON View - javaRecords: Support for Java 16 Records - kotlinData: Support for Kotlin immutable data classes + javaRecordsKotlinDataClasses: Support for Java Records and Kotlin data classes dbcDataTypes: Data Types dbcAttributeConverter: Using Attribute Converter dbcJoinQueries: Join Queries