From 43c2c8859fdbdc39bf52b856a941d8f376581661 Mon Sep 17 00:00:00 2001 From: Radovan Radic Date: Fri, 26 Apr 2024 15:41:58 +0200 Subject: [PATCH] Add test and reflection metadata for Hibernate Reactive ManyToMany and ElementCollection (#2919) --- .../reflect-config.json | 15 ++++ .../src/main/java/example/Course.java | 78 +++++++++++++++++++ .../main/java/example/CourseRepository.java | 16 ++++ .../src/main/java/example/Student.java | 68 ++++++++++++++++ .../main/java/example/StudentRepository.java | 16 ++++ .../java/example/StudentRepositorySpec.java | 73 +++++++++++++++++ 6 files changed, 266 insertions(+) create mode 100644 data-hibernate-reactive/src/main/resources/META-INF/native-image/io.micronaut.data/data-hibernate-reactive-graal/reflect-config.json create mode 100644 doc-examples/hibernate-reactive-example-java/src/main/java/example/Course.java create mode 100644 doc-examples/hibernate-reactive-example-java/src/main/java/example/CourseRepository.java create mode 100644 doc-examples/hibernate-reactive-example-java/src/main/java/example/Student.java create mode 100644 doc-examples/hibernate-reactive-example-java/src/main/java/example/StudentRepository.java create mode 100644 doc-examples/hibernate-reactive-example-java/src/test/java/example/StudentRepositorySpec.java diff --git a/data-hibernate-reactive/src/main/resources/META-INF/native-image/io.micronaut.data/data-hibernate-reactive-graal/reflect-config.json b/data-hibernate-reactive/src/main/resources/META-INF/native-image/io.micronaut.data/data-hibernate-reactive-graal/reflect-config.json new file mode 100644 index 0000000000..e1c8cad0cc --- /dev/null +++ b/data-hibernate-reactive/src/main/resources/META-INF/native-image/io.micronaut.data/data-hibernate-reactive-graal/reflect-config.json @@ -0,0 +1,15 @@ +[ + { + "name": "org.hibernate.reactive.persister.collection.impl.ReactiveBasicCollectionPersister", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.hibernate.mapping.Collection", + "org.hibernate.cache.spi.access.CollectionDataAccess", + "org.hibernate.metamodel.spi.RuntimeModelCreationContext" + ] + } + ] + } +] \ No newline at end of file diff --git a/doc-examples/hibernate-reactive-example-java/src/main/java/example/Course.java b/doc-examples/hibernate-reactive-example-java/src/main/java/example/Course.java new file mode 100644 index 0000000000..43e26948a5 --- /dev/null +++ b/doc-examples/hibernate-reactive-example-java/src/main/java/example/Course.java @@ -0,0 +1,78 @@ +package example; + +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.Table; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Entity +@Table(name = "course") +public class Course { + + @Id + @GeneratedValue + private Long id; + + private String title; + + @ManyToMany(mappedBy = "courses", fetch = FetchType.EAGER) + private Set students = new HashSet<>(); + + @ElementCollection(fetch = FetchType.EAGER) + private List notes; + + public Course() { + } + + public Course(String title) { + this(title, Set.of()); + } + + public Course(String title, Set students) { + this(title, students, List.of()); + } + + public Course(String title, Set students, List notes) { + this.title = title; + this.students = students; + this.notes = notes; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Set getStudents() { + return students; + } + public void setStudents(Set students) { + this.students = students; + } + + public List getNotes() { + return notes; + } + + public void setNotes(List notes) { + this.notes = notes; + } +} diff --git a/doc-examples/hibernate-reactive-example-java/src/main/java/example/CourseRepository.java b/doc-examples/hibernate-reactive-example-java/src/main/java/example/CourseRepository.java new file mode 100644 index 0000000000..77fd220a54 --- /dev/null +++ b/doc-examples/hibernate-reactive-example-java/src/main/java/example/CourseRepository.java @@ -0,0 +1,16 @@ +package example; + +import io.micronaut.core.annotation.NonNull; +import io.micronaut.data.annotation.Join; +import io.micronaut.data.annotation.Repository; +import io.micronaut.data.repository.reactive.ReactorCrudRepository; +import reactor.core.publisher.Mono; + +@Repository +public interface CourseRepository extends ReactorCrudRepository { + + @NonNull + @Override + @Join(value = "students", type = Join.Type.LEFT) + Mono findById(@NonNull Long id); +} diff --git a/doc-examples/hibernate-reactive-example-java/src/main/java/example/Student.java b/doc-examples/hibernate-reactive-example-java/src/main/java/example/Student.java new file mode 100644 index 0000000000..31d498952a --- /dev/null +++ b/doc-examples/hibernate-reactive-example-java/src/main/java/example/Student.java @@ -0,0 +1,68 @@ +package example; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.Table; + +import java.util.HashSet; +import java.util.Set; + +@Entity +@Table(name = "student") +public class Student { + + @Id + @GeneratedValue + private Long id; + + private String name; + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable(name = "student_course", + joinColumns = @JoinColumn(name = "student_id"), + inverseJoinColumns = @JoinColumn(name = "course_id") + ) + private Set courses = new HashSet<>(); + + public Student() { + } + + public Student(String name) { + this(name, Set.of()); + } + + public Student(String name, Set courses) { + this.name = name; + this.courses = courses; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getCourses() { + return courses; + } + + public void setCourses(Set courses) { + this.courses = courses; + } + +} diff --git a/doc-examples/hibernate-reactive-example-java/src/main/java/example/StudentRepository.java b/doc-examples/hibernate-reactive-example-java/src/main/java/example/StudentRepository.java new file mode 100644 index 0000000000..3deefbba11 --- /dev/null +++ b/doc-examples/hibernate-reactive-example-java/src/main/java/example/StudentRepository.java @@ -0,0 +1,16 @@ +package example; + +import io.micronaut.core.annotation.NonNull; +import io.micronaut.data.annotation.Join; +import io.micronaut.data.annotation.Repository; +import io.micronaut.data.repository.reactive.ReactorCrudRepository; +import reactor.core.publisher.Mono; + +@Repository +public interface StudentRepository extends ReactorCrudRepository { + + @NonNull + @Override + @Join(value = "courses", type = Join.Type.LEFT) + Mono findById(@NonNull Long id); +} diff --git a/doc-examples/hibernate-reactive-example-java/src/test/java/example/StudentRepositorySpec.java b/doc-examples/hibernate-reactive-example-java/src/test/java/example/StudentRepositorySpec.java new file mode 100644 index 0000000000..a2338320aa --- /dev/null +++ b/doc-examples/hibernate-reactive-example-java/src/test/java/example/StudentRepositorySpec.java @@ -0,0 +1,73 @@ +package example; + +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +@MicronautTest(transactional = false) +class StudentRepositorySpec { + + @Inject + StudentRepository studentRepository; + + @Inject + CourseRepository courseRepository; + + @AfterEach + public void cleanup() { + studentRepository.deleteAll().block(); + } + + /** + * Tests @{@link jakarta.persistence.ManyToMany} relations with Hibernate Reactive. + */ + @Test + void testCrud() { + Course languageCourse = new Course("English"); + languageCourse.setNotes(List.of("Starting in December")); + courseRepository.save(languageCourse).block(); + Course mathCourse = new Course("Mathematics"); + courseRepository.save(mathCourse).block(); + + languageCourse = courseRepository.findById(languageCourse.getId()).block(); + assertNotNull(languageCourse); + assertEquals(0, languageCourse.getStudents().size()); + assertEquals(1, languageCourse.getNotes().size()); + + mathCourse = courseRepository.findById(mathCourse.getId()).block(); + assertNotNull(mathCourse); + assertEquals(0, mathCourse.getStudents().size()); + assertEquals(0, mathCourse.getNotes().size()); + + Student student = new Student("Peter", Set.of(mathCourse)); + studentRepository.save(student).block(); + Long id = student.getId(); + assertNotNull(id); + + student = studentRepository.findById(id).block(); + assertNotNull(student); + assertEquals("Peter", student.getName()); + assertEquals(1, student.getCourses().size()); + + assertEquals(1, studentRepository.count().block()); + assertTrue(studentRepository.findAll().toIterable().iterator().hasNext()); + + languageCourse = courseRepository.findById(languageCourse.getId()).block(); + assertNotNull(languageCourse); + assertEquals(0, languageCourse.getStudents().size()); + + mathCourse = courseRepository.findById(mathCourse.getId()).block(); + assertNotNull(mathCourse); + assertEquals(1, mathCourse.getStudents().size()); + + studentRepository.deleteById(id).block(); + assertEquals(0, studentRepository.count().block()); + } + +}