fix broken code examples, and make 'em more readable
Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
parent
7002ee8d0d
commit
7bf6004ef8
|
@ -447,10 +447,14 @@ We might start with something like this, a mix of UI and persistence logic:
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
@Path("/") @Produces("application/json")
|
@Path("/")
|
||||||
|
@Produces("application/json")
|
||||||
public class BookResource {
|
public class BookResource {
|
||||||
|
|
||||||
@GET @Path("book/{isbn}")
|
private final SessionFactory sessionfactory = .... ;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("book/{isbn}")
|
||||||
public Book getBook(String isbn) {
|
public Book getBook(String isbn) {
|
||||||
var book = sessionFactory.fromTransaction(session -> session.find(Book.class, isbn));
|
var book = sessionFactory.fromTransaction(session -> session.find(Book.class, isbn));
|
||||||
return book == null ? Response.status(404).build() : book;
|
return book == null ? Response.status(404).build() : book;
|
||||||
|
@ -473,16 +477,23 @@ Let's now consider a slightly more complicated case.
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
@Path("/") @Produces("application/json")
|
@Path("/")
|
||||||
|
@Produces("application/json")
|
||||||
public class BookResource {
|
public class BookResource {
|
||||||
private static final int RESULTS_PER_PAGE = 20;
|
private static final int RESULTS_PER_PAGE = 20;
|
||||||
|
|
||||||
@GET @Path("books/{titlePattern}/{page:\\d+}")
|
private final SessionFactory sessionfactory = .... ;
|
||||||
public List<Book> findBooks(String titlePattern, int page) {
|
|
||||||
var books = sessionFactory.fromTransaction(session -> {
|
@GET
|
||||||
return session.createSelectionQuery("from Book where title like ?1 order by title", Book.class)
|
@Path("books/{titlePattern}/{pageNumber:\\d+}")
|
||||||
|
public List<Book> findBooks(String titlePattern, int pageNumber) {
|
||||||
|
var page = Page.page(RESULTS_PER_PAGE, pageNumber);
|
||||||
|
var books =
|
||||||
|
sessionFactory.fromTransaction(session -> {
|
||||||
|
var findBooksByTitle = "from Book where title like ?1 order by title";
|
||||||
|
return session.createSelectionQuery(findBooksByTitle, Book.class)
|
||||||
.setParameter(1, titlePattern)
|
.setParameter(1, titlePattern)
|
||||||
.setPage(Page.page(RESULTS_PER_PAGE, page))
|
.setPage(page)
|
||||||
.getResultList();
|
.getResultList();
|
||||||
});
|
});
|
||||||
return books.isEmpty() ? Response.status(404).build() : books;
|
return books.isEmpty() ? Response.status(404).build() : books;
|
||||||
|
@ -498,9 +509,9 @@ Let's hit the code with our favorite thing, the Extract Method refactoring. We o
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
static List<Book> findBooksTitled(Session session,
|
static List<Book> findBooksTitled(Session session, String titlePattern, Page page) {
|
||||||
String titlePattern, Page page) {
|
var findBooksByTitle = "from Book where title like ?1 order by title";
|
||||||
return session.createSelectionQuery("from Book where title like ?1 order by title", Book.class)
|
return session.createSelectionQuery(findBooksByTitle, Book.class)
|
||||||
.setParameter(1, titlePattern)
|
.setParameter(1, titlePattern)
|
||||||
.setPage(page)
|
.setPage(page)
|
||||||
.getResultList();
|
.getResultList();
|
||||||
|
@ -522,13 +533,13 @@ We need a place to put the annotation, so let's move our query method to a new c
|
||||||
query = "from Book where title like :title order by title")
|
query = "from Book where title like :title order by title")
|
||||||
class Queries {
|
class Queries {
|
||||||
|
|
||||||
static List<Book> findBooksTitled(Session session,
|
static List<Book> findBooksTitled(Session session, String titlePattern, Page page) {
|
||||||
String titlePattern, Page page) {
|
|
||||||
return session.createQuery(Queries_._findBooksByTitle_) //type safe reference to the named query
|
return session.createQuery(Queries_._findBooksByTitle_) //type safe reference to the named query
|
||||||
.setParameter("title", titlePattern)
|
.setParameter("title", titlePattern)
|
||||||
.setPage(page)
|
.setPage(page)
|
||||||
.getResultList();
|
.getResultList();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -546,11 +557,13 @@ Whatever the case, the code which orchestrates a unit of work usually just calls
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
@GET
|
@GET
|
||||||
@Path("books/{titlePattern}")
|
@Path("books/{titlePattern}/{pageNumber:\\d+}")
|
||||||
public List<Book> findBooks(String titlePattern) {
|
public List<Book> findBooks(String titlePattern, int pageNumber) {
|
||||||
var books = sessionFactory.fromTransaction(session ->
|
var page = Page.page(RESULTS_PER_PAGE, pageNumber);
|
||||||
Queries.findBooksTitled(session, titlePattern,
|
var books =
|
||||||
Page.page(RESULTS_PER_PAGE, page)));
|
sessionFactory.fromTransaction(session ->
|
||||||
|
// call handwritten query method
|
||||||
|
Queries.findBooksTitled(session, titlePattern, page));
|
||||||
return books.isEmpty() ? Response.status(404).build() : books;
|
return books.isEmpty() ? Response.status(404).build() : books;
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
@ -567,7 +580,9 @@ Suppose we simplify `Queries` to just the following:
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
|
// a sort of proto-repository, this interface is never implemented
|
||||||
interface Queries {
|
interface Queries {
|
||||||
|
// a HQL query method with a generated static "implementation"
|
||||||
@HQL("where title like :title order by title")
|
@HQL("where title like :title order by title")
|
||||||
List<Book> findBooksTitled(String title, Page page);
|
List<Book> findBooksTitled(String title, Page page);
|
||||||
}
|
}
|
||||||
|
@ -579,11 +594,13 @@ We can call it just like we were previously calling our handwritten version:
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
@GET
|
@GET
|
||||||
@Path("books/{titlePattern}")
|
@Path("books/{titlePattern}/{pageNumber:\\d+}")
|
||||||
public List<Book> findBooks(String titlePattern) {
|
public List<Book> findBooks(String titlePattern, int pageNumber) {
|
||||||
var books = sessionFactory.fromTransaction(session ->
|
var page = Page.page(RESULTS_PER_PAGE, pageNumber);
|
||||||
Queries_.findBooksTitled(session, titlePattern,
|
var books =
|
||||||
Page.page(RESULTS_PER_PAGE, page)));
|
sessionFactory.fromTransaction(session ->
|
||||||
|
// call the generated query method "implementation"
|
||||||
|
Queries_.findBooksTitled(session, titlePattern, page));
|
||||||
return books.isEmpty() ? Response.status(404).build() : books;
|
return books.isEmpty() ? Response.status(404).build() : books;
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
@ -592,32 +609,37 @@ In this case, the quantity of code eliminated is pretty trivial.
|
||||||
The real value is in improved type safety.
|
The real value is in improved type safety.
|
||||||
We now find out about errors in assignments of arguments to query parameters at compile time.
|
We now find out about errors in assignments of arguments to query parameters at compile time.
|
||||||
|
|
||||||
This is all quite nice so far, but at this point you're probably wondering whether we could use dependency injection to obtain an _instance_ of the `Queries` interface.
|
This is all quite nice so far, but at this point you're probably wondering whether we could use dependency injection to obtain an _instance_ of the `Queries` interface, and have this object take care of obtaining its own `Session`.
|
||||||
Well, indeed we can.
|
Well, indeed we can.
|
||||||
What we need to do is indicate the kind of session the `Queries` interface depends on, by adding a method to retrieve the session.
|
What we need to do is indicate the kind of session the `Queries` interface depends on, by adding a method to retrieve the session.
|
||||||
|
Observe, again, that we're _still_ not attempting to hide the `Session` from the client code.
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
|
// a true repository interface with generated implementation
|
||||||
interface Queries {
|
interface Queries {
|
||||||
EntityManager entityManager();
|
// declare the kind of session backing this repository
|
||||||
|
Session session();
|
||||||
|
|
||||||
|
// a HQL query method with a generated implementation
|
||||||
@HQL("where title like :title order by title")
|
@HQL("where title like :title order by title")
|
||||||
List<Book> findBooksTitled(String title, Page page);
|
List<Book> findBooksTitled(String title, Page page);
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
The `Queries` interface is now considered a _repository_, and we may use CDI to inject the repository implementation generated by Hibernate Processor:
|
The `Queries` interface is now considered a _repository_, and we may use CDI to inject the repository implementation generated by Hibernate Processor.
|
||||||
|
Also, since I guess we're now working in some sort of container environment, we'll let the container manage transactions for us.
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
----
|
----
|
||||||
@Inject Queries queries;
|
@Inject Queries queries; // inject the repository
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("books/{titlePattern}")
|
@Path("books/{titlePattern}/{pageNumber:\\d+}")
|
||||||
@Transactional
|
@Transactional
|
||||||
public List<Book> findBooks(String titlePattern) {
|
public List<Book> findBooks(String titlePattern, int pageNumber) {
|
||||||
var books = queries.findBooksTitled(session, titlePattern,
|
var page = Page.page(RESULTS_PER_PAGE, pageNumber);
|
||||||
Page.page(RESULTS_PER_PAGE, page));
|
var books = queries.findBooksTitled(session, titlePattern, page); // call the repository method
|
||||||
return books.isEmpty() ? Response.status(404).build() : books;
|
return books.isEmpty() ? Response.status(404).build() : books;
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
|
@ -424,8 +424,8 @@ What if we would like to inject a `Queries` object instead of calling its constr
|
||||||
[%unbreakable]
|
[%unbreakable]
|
||||||
[TIP]
|
[TIP]
|
||||||
====
|
====
|
||||||
As you <<architecture,recall>>, we don't think these things really need to be container-managed objects.
|
As you <<organizing-persistence,recall>>, we don't think these things really need to be container-managed objects.
|
||||||
But if you _want_ them to be—if you're allergic to calling constructors, for some reason—then:
|
But if you _want_ them to be--if you're allergic to calling constructors, for some reason--then:
|
||||||
|
|
||||||
- placing `jakarta.inject` on the build path will cause an `@Inject` annotation to be added to the constructor of `Queries_`, and
|
- placing `jakarta.inject` on the build path will cause an `@Inject` annotation to be added to the constructor of `Queries_`, and
|
||||||
- placing `jakarta.enterprise.context` on the build path will cause a `@Dependent` annotation to be added to the `Queries_` class.
|
- placing `jakarta.enterprise.context` on the build path will cause a `@Dependent` annotation to be added to the `Queries_` class.
|
||||||
|
|
Loading…
Reference in New Issue