@@ -447,10 +447,14 @@ We might start with something like this, a mix of UI and persistence logic:
447
447
448
448
[source,java]
449
449
----
450
- @Path("/") @Produces("application/json")
450
+ @Path("/")
451
+ @Produces("application/json")
451
452
public class BookResource {
452
453
453
- @GET @Path("book/{isbn}")
454
+ private final SessionFactory sessionfactory = .... ;
455
+
456
+ @GET
457
+ @Path("book/{isbn}")
454
458
public Book getBook(String isbn) {
455
459
var book = sessionFactory.fromTransaction(session -> session.find(Book.class, isbn));
456
460
return book == null ? Response.status(404).build() : book;
@@ -473,18 +477,25 @@ Let's now consider a slightly more complicated case.
473
477
474
478
[source,java]
475
479
----
476
- @Path("/") @Produces("application/json")
480
+ @Path("/")
481
+ @Produces("application/json")
477
482
public class BookResource {
478
483
private static final int RESULTS_PER_PAGE = 20;
479
484
480
- @GET @Path("books/{titlePattern}/{page:\\d+}")
481
- public List<Book> findBooks(String titlePattern, int page) {
482
- var books = sessionFactory.fromTransaction(session -> {
483
- return session.createSelectionQuery("from Book where title like ?1 order by title", Book.class)
484
- .setParameter(1, titlePattern)
485
- .setPage(Page.page(RESULTS_PER_PAGE, page))
486
- .getResultList();
487
- });
485
+ private final SessionFactory sessionfactory = .... ;
486
+
487
+ @GET
488
+ @Path("books/{titlePattern}/{pageNumber:\\d+}")
489
+ public List<Book> findBooks(String titlePattern, int pageNumber) {
490
+ var page = Page.page(RESULTS_PER_PAGE, pageNumber);
491
+ var books =
492
+ sessionFactory.fromTransaction(session -> {
493
+ var findBooksByTitle = "from Book where title like ?1 order by title";
494
+ return session.createSelectionQuery(findBooksByTitle, Book.class)
495
+ .setParameter(1, titlePattern)
496
+ .setPage(page)
497
+ .getResultList();
498
+ });
488
499
return books.isEmpty() ? Response.status(404).build() : books;
489
500
}
490
501
@@ -498,9 +509,9 @@ Let's hit the code with our favorite thing, the Extract Method refactoring. We o
498
509
499
510
[source,java]
500
511
----
501
- static List<Book> findBooksTitled(Session session,
502
- String titlePattern, Page page) {
503
- return session.createSelectionQuery("from Book where title like ?1 order by title" , Book.class)
512
+ static List<Book> findBooksTitled(Session session, String titlePattern, Page page) {
513
+ var findBooksByTitle = "from Book where title like ?1 order by title";
514
+ return session.createSelectionQuery(findBooksByTitle , Book.class)
504
515
.setParameter(1, titlePattern)
505
516
.setPage(page)
506
517
.getResultList();
@@ -522,13 +533,13 @@ We need a place to put the annotation, so let's move our query method to a new c
522
533
query = "from Book where title like :title order by title")
523
534
class Queries {
524
535
525
- static List<Book> findBooksTitled(Session session,
526
- String titlePattern, Page page) {
536
+ static List<Book> findBooksTitled(Session session, String titlePattern, Page page) {
527
537
return session.createQuery(Queries_._findBooksByTitle_) //type safe reference to the named query
528
538
.setParameter("title", titlePattern)
529
539
.setPage(page)
530
540
.getResultList();
531
541
}
542
+
532
543
}
533
544
----
534
545
@@ -546,11 +557,13 @@ Whatever the case, the code which orchestrates a unit of work usually just calls
546
557
[source,java]
547
558
----
548
559
@GET
549
- @Path("books/{titlePattern}")
550
- public List<Book> findBooks(String titlePattern) {
551
- var books = sessionFactory.fromTransaction(session ->
552
- Queries.findBooksTitled(session, titlePattern,
553
- Page.page(RESULTS_PER_PAGE, page)));
560
+ @Path("books/{titlePattern}/{pageNumber:\\d+}")
561
+ public List<Book> findBooks(String titlePattern, int pageNumber) {
562
+ var page = Page.page(RESULTS_PER_PAGE, pageNumber);
563
+ var books =
564
+ sessionFactory.fromTransaction(session ->
565
+ // call handwritten query method
566
+ Queries.findBooksTitled(session, titlePattern, page));
554
567
return books.isEmpty() ? Response.status(404).build() : books;
555
568
}
556
569
----
@@ -567,7 +580,9 @@ Suppose we simplify `Queries` to just the following:
567
580
568
581
[source,java]
569
582
----
583
+ // a sort of proto-repository, this interface is never implemented
570
584
interface Queries {
585
+ // a HQL query method with a generated static "implementation"
571
586
@HQL("where title like :title order by title")
572
587
List<Book> findBooksTitled(String title, Page page);
573
588
}
@@ -579,11 +594,13 @@ We can call it just like we were previously calling our handwritten version:
579
594
[source,java]
580
595
----
581
596
@GET
582
- @Path("books/{titlePattern}")
583
- public List<Book> findBooks(String titlePattern) {
584
- var books = sessionFactory.fromTransaction(session ->
585
- Queries_.findBooksTitled(session, titlePattern,
586
- Page.page(RESULTS_PER_PAGE, page)));
597
+ @Path("books/{titlePattern}/{pageNumber:\\d+}")
598
+ public List<Book> findBooks(String titlePattern, int pageNumber) {
599
+ var page = Page.page(RESULTS_PER_PAGE, pageNumber);
600
+ var books =
601
+ sessionFactory.fromTransaction(session ->
602
+ // call the generated query method "implementation"
603
+ Queries_.findBooksTitled(session, titlePattern, page));
587
604
return books.isEmpty() ? Response.status(404).build() : books;
588
605
}
589
606
----
@@ -592,32 +609,37 @@ In this case, the quantity of code eliminated is pretty trivial.
592
609
The real value is in improved type safety.
593
610
We now find out about errors in assignments of arguments to query parameters at compile time.
594
611
595
- 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.
612
+ 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` .
596
613
Well, indeed we can.
597
614
What we need to do is indicate the kind of session the `Queries` interface depends on, by adding a method to retrieve the session.
615
+ Observe, again, that we're _still_ not attempting to hide the `Session` from the client code.
598
616
599
617
[source,java]
600
618
----
619
+ // a true repository interface with generated implementation
601
620
interface Queries {
602
- EntityManager entityManager();
621
+ // declare the kind of session backing this repository
622
+ Session session();
603
623
624
+ // a HQL query method with a generated implementation
604
625
@HQL("where title like :title order by title")
605
626
List<Book> findBooksTitled(String title, Page page);
606
627
}
607
628
----
608
629
609
- The `Queries` interface is now considered a _repository_, and we may use CDI to inject the repository implementation generated by Hibernate Processor:
630
+ The `Queries` interface is now considered a _repository_, and we may use CDI to inject the repository implementation generated by Hibernate Processor.
631
+ Also, since I guess we're now working in some sort of container environment, we'll let the container manage transactions for us.
610
632
611
633
[source,java]
612
634
----
613
- @Inject Queries queries;
635
+ @Inject Queries queries; // inject the repository
614
636
615
637
@GET
616
- @Path("books/{titlePattern}")
638
+ @Path("books/{titlePattern}/{pageNumber:\\d+} ")
617
639
@Transactional
618
- public List<Book> findBooks(String titlePattern) {
619
- var books = queries.findBooksTitled(session, titlePattern,
620
- Page.page(RESULTS_PER_PAGE, page));
640
+ public List<Book> findBooks(String titlePattern, int pageNumber ) {
641
+ var page = Page.page(RESULTS_PER_PAGE, pageNumber);
642
+ var books = queries.findBooksTitled(session, titlePattern, page); // call the repository method
621
643
return books.isEmpty() ? Response.status(404).build() : books;
622
644
}
623
645
----
0 commit comments