Skip to content

Commit

Permalink
Merge pull request #15818 from DavideD/15814-Lazy-fetch
Browse files Browse the repository at this point in the history
Lazy fetch associations with Hibernate Reactive
  • Loading branch information
Sanne authored Mar 17, 2021
2 parents 83a90bd + bf9e86d commit 280967a
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public FastBootReactiveEntityManagerFactoryBuilder(PrevalidatedQuarkusMetadata m
@Override
public EntityManagerFactory build() {
final SessionFactoryOptionsBuilder optionsBuilder = metadata.buildSessionFactoryOptionsBuilder();
optionsBuilder.enableCollectionInDefaultFetchGroup(true);
populate(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, optionsBuilder, standardServiceRegistry,
multiTenancyStrategy);
SessionFactoryOptions options = optionsBuilder.buildOptions();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package io.quarkus.it.hibernate.reactive.postgresql;

import java.util.Collection;
import java.util.concurrent.CompletionStage;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;

import org.hibernate.reactive.mutiny.Mutiny;
import org.hibernate.reactive.stage.Stage;

import io.quarkus.it.hibernate.reactive.postgresql.lazy.Author;
import io.quarkus.it.hibernate.reactive.postgresql.lazy.Book;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.pgclient.PgPool;
import io.vertx.mutiny.sqlclient.Tuple;

@Path("/hr-fetch")
public class HibernateReactiveTestEndpointFetchLazy {

@Inject
Mutiny.Session mutinySession;

// I'm using the factory because there are some issues with an injected Stage.Session
// See issue https://github.com/quarkusio/quarkus/issues/14812
@Inject
Stage.SessionFactory stageSessionFactory;

// Injecting a Vert.x Pool is not required, It's used to
// independently validate the contents of the database for the test
@Inject
PgPool pgPool;

@GET
@Path("/findBooksWithMutiny/{authorId}")
public Uni<Collection<Book>> findBooksWithMutiny(@PathParam("authorId") Integer authorId) {
return mutinySession.find(Author.class, authorId)
.chain(author -> Mutiny.fetch(author.getBooks()));
}

@GET
@Path("/findBooksWithStage/{authorId}")
public CompletionStage<Collection<Book>> findBooksWithStage(@PathParam("authorId") Integer authorId) {
return stageSessionFactory.withTransaction((session, tx) -> session
.find(Author.class, authorId)
.thenCompose(author -> Stage.fetch(author.getBooks())));
}

@POST
@Path("/prepareDb")
public Uni<String> prepareDb() {
final Author author = new Author(567, "Neal Stephenson");
final Book book1 = new Book("0-380-97346-4", "Cryptonomicon", author);
final Book book2 = new Book("0-553-08853-X", "Snow Crash", author);
author.getBooks().add(book1);
author.getBooks().add(book2);

return mutinySession.createQuery(" delete from Book").executeUpdate()
.call(() -> mutinySession.createQuery("delete from Author").executeUpdate())
.call(() -> mutinySession.persist(author))
.chain(mutinySession::flush)
.chain(() -> selectNameFromId(author.getId()));
}

private Uni<String> selectNameFromId(Integer id) {
return pgPool.preparedQuery("SELECT name FROM Author WHERE id = $1").execute(Tuple.of(id)).map(rowSet -> {
if (rowSet.size() == 1) {
return rowSet.iterator().next().getString(0);
} else if (rowSet.size() > 1) {
throw new AssertionError("More than one result returned: " + rowSet.size());
} else {
return null; // Size 0
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package io.quarkus.it.hibernate.reactive.postgresql.lazy;

import static javax.persistence.CascadeType.PERSIST;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table
public class Author {
@Id
private Integer id;

private String name;

@OneToMany(mappedBy = "author", cascade = PERSIST)
private Collection<Book> books = new ArrayList<>();

public Author() {
}

public Author(Integer id, String name) {
this.id = id;
this.name = name;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Collection<Book> getBooks() {
return books;
}

public void setBooks(Collection<Book> books) {
this.books = books;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Author author = (Author) o;
return Objects.equals(name, author.name);
}

@Override
public int hashCode() {
return Objects.hash(name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package io.quarkus.it.hibernate.reactive.postgresql.lazy;

import static javax.persistence.FetchType.LAZY;

import java.util.Objects;

import javax.json.bind.annotation.JsonbTransient;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table
public class Book {
@Id
@GeneratedValue
private Integer id;

private String isbn;

private String title;

@JsonbTransient
@ManyToOne(fetch = LAZY)
private Author author;

public Book(String isbn, String title, Author author) {
this.title = title;
this.isbn = isbn;
this.author = author;
}

public Book() {
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getIsbn() {
return isbn;
}

public void setIsbn(String isbn) {
this.isbn = isbn;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public Author getAuthor() {
return author;
}

public void setAuthor(Author author) {
this.author = author;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Book book = (Book) o;
return Objects.equals(isbn, book.isbn)
&& Objects.equals(title, book.title);
}

@Override
public int hashCode() {
return Objects.hash(isbn, title);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.quarkus.it.hibernate.reactive.postgresql;

import io.quarkus.test.junit.NativeImageTest;

@NativeImageTest
public class HibernateReactiveFetchLazyInGraalIT extends HibernateReactiveFetchLazyTest {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package io.quarkus.it.hibernate.reactive.postgresql;

import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertIterableEquals;

import java.util.List;

import org.junit.jupiter.api.Test;

import io.quarkus.test.common.http.TestHTTPEndpoint;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;
import io.restassured.response.Response;

@QuarkusTest
@TestHTTPEndpoint(HibernateReactiveTestEndpointFetchLazy.class)
public class HibernateReactiveFetchLazyTest {

@Test
public void fetchAfterFindWithMutiny() {
RestAssured.when()
.post("/prepareDb")
.then()
.body(is("Neal Stephenson"));

Response response = RestAssured.when()
.get("/findBooksWithMutiny/567")
.then()
.extract().response();
assertTitles(response, "Cryptonomicon", "Snow Crash");
}

@Test
public void fetchAfterFindWithWithStage() {
RestAssured.when()
.post("/prepareDb")
.then()
.body(is("Neal Stephenson"));

Response response = RestAssured.when()
.get("/findBooksWithStage/567")
.then()
.extract().response();
assertTitles(response, "Cryptonomicon", "Snow Crash");
}

private void assertTitles(Response response, String... expectedTitles) {
List<Object> titles = response.jsonPath().getList("title").stream().sorted().collect(toList());
assertIterableEquals(asList(expectedTitles), titles);
}
}

0 comments on commit 280967a

Please sign in to comment.