Skip to content

Commit

Permalink
feat: added possibility for query hints (#155)
Browse files Browse the repository at this point in the history
* feat: added possibility for query hints

* feat: improvements and tests

* fix: format
  • Loading branch information
JordenReuter authored Apr 17, 2024
1 parent 470e5f6 commit 7979ef6
Show file tree
Hide file tree
Showing 11 changed files with 419 additions and 144 deletions.
2 changes: 1 addition & 1 deletion docs/modules/tkit-quarkus/pages/includes/attributes.adoc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
:project-version: 2.20.0
:project-version:
:quarkus-version: 3.9.3

:examples-dir: ./../examples/
274 changes: 137 additions & 137 deletions docs/modules/tkit-quarkus/pages/includes/tkit-quarkus-rest-context.adoc

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,18 @@ public PagedQuery<T> createPageQuery(CriteriaQuery<T> query, Page page) {
return new PagedQuery<>(getEntityManager(), query, page, idAttributeName);
}

/**
* Creates the page query of the DAO {@code <T>} type.
*
* @param query the criteria query
* @param page the page for the query
* @param hint query hint
* @return the new page query instance
*/
public PagedQuery<T> createPageQuery(CriteriaQuery<T> query, Page page, String hint) {
return new PagedQuery<>(getEntityManager(), query, page, idAttributeName, hint);
}

/**
* Creates the page query of the DAO {@code <T>} type.
*
Expand All @@ -138,6 +150,19 @@ public <E> PagedQuery<E> createPageQueryCustom(CriteriaQuery<E> query, Page page
return new PagedQuery<>(em, query, page, idAttributeName);
}

/**
* Creates the page query of the custom {@code <E>} type
*
* @param query the criteria query
* @param page the page for the query
* @param hint query hint
* @param <E> the entity type of the paged query.
* @return the new page query instance
*/
public <E> PagedQuery<E> createPageQueryCustom(CriteriaQuery<E> query, Page page, String hint) {
return new PagedQuery<>(em, query, page, idAttributeName, hint);
}

/**
* Finds all entities.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ public static <T> PageResult<T> empty() {
/**
* The page number.
*/
private int number;
private long number;

/**
* The page size.
*/
private int size;
private long size;

/**
* The number of pages.
Expand All @@ -52,10 +52,22 @@ public static <T> PageResult<T> empty() {
* @param page the page.
*/
public PageResult(long totalElements, Stream<T> stream, Page page) {
this(totalElements, stream, page.number(), page.size());
}

/**
* The default constructor.
*
* @param totalElements the count of all items.
* @param stream the data stream.
* @param number number of the page.
* @param size size of the page.
*/
public PageResult(long totalElements, Stream<T> stream, long number, long size) {
this.totalElements = totalElements;
this.stream = stream;
this.number = page.number();
this.size = page.size();
this.number = number;
this.size = size;
this.totalPages = (totalElements + size - 1) / size;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public class PagedQuery<T> {
*/
private Page page;

/**
* Query hint
*/
private String hint;

/**
* Default constructor.
*
Expand All @@ -51,23 +56,44 @@ public class PagedQuery<T> {
* @param page the start page.
*/
public PagedQuery(EntityManager em, CriteriaQuery<T> criteria, Page page, String idAttributeName) {
this(em, criteria, page, idAttributeName, null);
}

/**
* Constructor including query hint.
*
* @param em the entity manager.
* @param criteria the search criteria
* @param page the start page.
* @param hint custom query hint
*/
public PagedQuery(EntityManager em, CriteriaQuery<T> criteria, Page page, String idAttributeName, String hint) {
this.em = em;
this.criteria = setDefaultSorting(em, criteria, idAttributeName);
this.page = page;
this.countCriteria = createCountCriteria(em, criteria);
this.hint = hint;
}

public PageResult<T> getPageResult() {
try {
var q = em.createQuery(countCriteria);
if (hint != null) {
q.setHint(AbstractDAO.HINT_LOAD_GRAPH, em.getEntityGraph(hint));
}
// get count
Long count = em.createQuery(countCriteria).getSingleResult();
Long count = q.getSingleResult();
// return empty page for count zero
if (count == 0) {
return PageResult.empty();
}

// get stream
Stream<T> stream = em.createQuery(criteria)
var sq = em.createQuery(criteria);
if (hint != null) {
sq.setHint(AbstractDAO.HINT_LOAD_GRAPH, em.getEntityGraph(hint));
}
Stream<T> stream = sq
.setFirstResult(page.number() * page.size())
.setMaxResults(page.size())
.getResultStream();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.tkit.quarkus.jpa.test;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;

import org.tkit.quarkus.jpa.models.TraceableEntity;

@Entity
@Table(name = "TEST_CHILD")
public class Child extends TraceableEntity {
@Column(name = "MFE_ID", nullable = false)
private String mfeId;

public String getMfeId() {
return mfeId;
}

public void setMfeId(String mfeId) {
this.mfeId = mfeId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.tkit.quarkus.jpa.test;

import jakarta.enterprise.context.ApplicationScoped;

import org.tkit.quarkus.jpa.daos.AbstractDAO;

@ApplicationScoped
public class ChildDAO extends AbstractDAO<Child> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.tkit.quarkus.jpa.test;

import static jakarta.persistence.FetchType.LAZY;

import java.util.List;

import jakarta.persistence.*;

import org.tkit.quarkus.jpa.models.TraceableEntity;

@Entity
@NamedEntityGraph(name = "Parent.loadChildren", includeAllAttributes = true)
@Table(name = "TEST_PARENT")
public class Parent extends TraceableEntity {

@OneToMany(fetch = LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "PARENT_GUID")
private List<Child> children;

public List<Child> getChildren() {
return children;
}

public void setChildren(List<Child> children) {
this.children = children;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.tkit.quarkus.jpa.test;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.transaction.Transactional;

import org.tkit.quarkus.jpa.daos.AbstractDAO;
import org.tkit.quarkus.jpa.daos.Page;
import org.tkit.quarkus.jpa.daos.PageResult;

@ApplicationScoped
public class ParentDAO extends AbstractDAO<Parent> {

@Transactional(Transactional.TxType.NOT_SUPPORTED)
Parent loadById(String id) {
var cb = this.getEntityManager().getCriteriaBuilder();
var cq = cb.createQuery(Parent.class);
var root = cq.from(Parent.class);

cq.where(cb.equal(root.get("id"), id));
Parent p = this.getEntityManager().createQuery(cq)
.setHint(HINT_LOAD_GRAPH, this.getEntityManager().getEntityGraph("Parent.loadChildren")).getSingleResult();
this.getEntityManager().detach(p);
return p;
}

PageResult<Parent> loadPage() {
var cb = this.getEntityManager().getCriteriaBuilder();
var cq = cb.createQuery(Parent.class);
var root = cq.from(Parent.class);

cq.where(root.get("id").isNotNull());
return this.createPageQuery(cq, Page.of(0, 10), "Parent.loadChildren").getPageResult();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.tkit.quarkus.jpa.test;

import java.util.List;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

import org.tkit.quarkus.jpa.models.TraceableEntity;

@Path("parent")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.NOT_SUPPORTED)
@ApplicationScoped
public class ParentRestController {
@Inject
ParentDAO parentDAO;

@GET
@Path("{id}")
public Response find(@PathParam("id") String id) {
Parent tmp = parentDAO.loadById(id);
if (tmp == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
System.out.println("_____________________________________");
System.out.println(tmp.getChildren().get(0).getMfeId());
return Response.ok(tmp).build();
}

@GET
public Response create() {
Parent tmp = new Parent();
Child c1 = new Child();
c1.setMfeId("mfe1");
Child c2 = new Child();
c2.setMfeId("mfe2");
tmp.setChildren(List.of(c1, c2));
// Sibling sibling = siblingDAO.create(new Sibling());
// tmp.setSibling(sibling);
parentDAO.create(tmp);

return Response.ok(tmp).build();
}

@GET
@Path("page")
public Response getPageResult() {
return Response.ok(parentDAO.loadPage().getStream()
.map(parent -> parent.getChildren().stream().map(TraceableEntity::getId).toList()).toList()).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.tkit.quarkus.jpa.test;

import static io.restassured.RestAssured.given;

import jakarta.ws.rs.core.Response;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.quarkus.test.junit.QuarkusTest;
import io.restassured.http.ContentType;

@QuarkusTest
@DisplayName("Parent DAO tests")
public class ParentDAOTest extends AbstractTest {

private static final Logger log = LoggerFactory.getLogger(ParentDAOTest.class);

@Test
public void searchParentTest() {
Parent create = given()
.get("parent")
.then()
.log().all()
.statusCode(Response.Status.OK.getStatusCode())
.extract().body().as(Parent.class);

Parent find = given()
.contentType(ContentType.JSON)
.pathParam("id", create.getId())
.get("parent/{id}")
.then()
.log().all()
.statusCode(Response.Status.OK.getStatusCode())
.extract().body().as(Parent.class);

Assertions.assertNotNull(find);
Assertions.assertEquals(create.getId(), find.getId());
}

@Test
public void loadPageResult() {
given()
.get("parent")
.then()
.log().all()
.statusCode(Response.Status.OK.getStatusCode())
.extract().body().as(Parent.class);

var find = given()
.contentType(ContentType.JSON)
.get("parent/page")
.then()
.log().all()
.statusCode(Response.Status.OK.getStatusCode())
.contentType(ContentType.JSON)
.extract().asString();

Assertions.assertNotNull(find);
}
}

0 comments on commit 7979ef6

Please sign in to comment.