Skip to content

Commit df7ca37

Browse files
authored
Merge pull request #31364 from chiroito/guide_hibernate_orm_panache
Add more detail code and procedures on hibernate-orm-panache doc
2 parents 8df1111 + 1e51c0a commit df7ca37

File tree

1 file changed

+186
-0
lines changed

1 file changed

+186
-0
lines changed

docs/src/main/asciidoc/hibernate-orm-panache.adoc

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,22 @@ What we're doing in Panache is to allow you to write your Hibernate ORM entities
1919

2020
[source,java]
2121
----
22+
package org.acme;
23+
2224
public enum Status {
2325
Alive,
2426
Deceased
2527
}
28+
----
29+
30+
[source,java]
31+
----
32+
package org.acme;
33+
34+
import java.time.LocalDate;
35+
import java.util.List;
36+
import jakarta.persistence.Entity;
37+
import io.quarkus.hibernate.orm.panache.PanacheEntity;
2638
2739
@Entity
2840
public class Person extends PanacheEntity {
@@ -156,6 +168,13 @@ columns as public fields:
156168

157169
[source,java]
158170
----
171+
package org.acme;
172+
173+
import java.time.LocalDate;
174+
import java.util.List;
175+
import jakarta.persistence.Entity;
176+
import io.quarkus.hibernate.orm.panache.PanacheEntity;
177+
159178
@Entity
160179
public class Person extends PanacheEntity {
161180
public String name;
@@ -169,6 +188,13 @@ You can put all your JPA column annotations on the public fields. If you need a
169188

170189
[source,java]
171190
----
191+
package org.acme;
192+
193+
import java.time.LocalDate;
194+
import java.util.List;
195+
import jakarta.persistence.Entity;
196+
import io.quarkus.hibernate.orm.panache.PanacheEntity;
197+
172198
@Entity
173199
public class Person extends PanacheEntity {
174200
public String name;
@@ -197,6 +223,11 @@ Once you have written your entity, here are the most common operations you will
197223

198224
[source,java]
199225
----
226+
import java.time.LocalDate;
227+
import java.time.Month;
228+
import java.util.List;
229+
import java.util.Optional;
230+
200231
// creating a person
201232
Person person = new Person();
202233
person.name = "Stef";
@@ -252,6 +283,10 @@ All `list` methods have equivalent `stream` versions.
252283

253284
[source,java]
254285
----
286+
import java.util.List;
287+
import java.util.stream.Collectors;
288+
import java.util.stream.Stream;
289+
255290
try (Stream<Person> persons = Person.streamAll()) {
256291
List<String> namesButEmmanuels = persons
257292
.map(p -> p.name.toLowerCase() )
@@ -272,6 +307,13 @@ Adding them as static methods in your entity class is the Panache Active Record
272307

273308
[source,java]
274309
----
310+
package org.acme;
311+
312+
import java.time.LocalDate;
313+
import java.util.List;
314+
import jakarta.persistence.Entity;
315+
import io.quarkus.hibernate.orm.panache.PanacheEntity;
316+
275317
@Entity
276318
public class Person extends PanacheEntity {
277319
public String name;
@@ -301,6 +343,12 @@ When using the repository pattern, you can define your entities as regular JPA e
301343

302344
[source,java]
303345
----
346+
package org.acme;
347+
348+
import jakarta.persistence.GeneratedValue;
349+
import jakarta.persistence.Id;
350+
import java.time.LocalDate;
351+
304352
@Entity
305353
public class Person {
306354
@Id @GeneratedValue private Long id;
@@ -345,6 +393,13 @@ by making them implements `PanacheRepository`:
345393

346394
[source,java]
347395
----
396+
package org.acme;
397+
398+
import io.quarkus.hibernate.orm.panache.PanacheRepository;
399+
400+
import jakarta.enterprise.context.ApplicationScoped;
401+
import java.util.List;
402+
348403
@ApplicationScoped
349404
public class PersonRepository implements PanacheRepository<Person> {
350405
@@ -369,6 +424,8 @@ is exactly the same as using the active record pattern, except you need to injec
369424

370425
[source,java]
371426
----
427+
import jakarta.inject.Inject;
428+
372429
@Inject
373430
PersonRepository personRepository;
374431
@@ -384,6 +441,11 @@ Once you have written your repository, here are the most common operations you w
384441

385442
[source,java]
386443
----
444+
import java.time.LocalDate;
445+
import java.time.Month;
446+
import java.util.List;
447+
import java.util.Optional;
448+
387449
// creating a person
388450
Person person = new Person();
389451
person.setName("Stef");
@@ -439,6 +501,10 @@ All `list` methods have equivalent `stream` versions.
439501

440502
[source,java]
441503
----
504+
import java.util.List;
505+
import java.util.stream.Collectors;
506+
import java.util.stream.Stream;
507+
442508
Stream<Person> persons = personRepository.streamAll();
443509
List<String> namesButEmmanuels = persons
444510
.map(p -> p.name.toLowerCase() )
@@ -460,6 +526,22 @@ Then, you can create the following resource to create/read/update/delete your Pe
460526

461527
[source,java]
462528
----
529+
package org.acme;
530+
531+
import java.net.URI;
532+
import java.util.List;
533+
import jakarta.transaction.Transactional;
534+
import jakarta.ws.rs.Consumes;
535+
import jakarta.ws.rs.DELETE;
536+
import jakarta.ws.rs.GET;
537+
import jakarta.ws.rs.NotFoundException;
538+
import jakarta.ws.rs.POST;
539+
import jakarta.ws.rs.PUT;
540+
import jakarta.ws.rs.Path;
541+
import jakarta.ws.rs.Produces;
542+
import jakarta.ws.rs.core.MediaType;
543+
import jakarta.ws.rs.core.Response;
544+
463545
@Path("/persons")
464546
@Produces(MediaType.APPLICATION_JSON)
465547
@Consumes(MediaType.APPLICATION_JSON)
@@ -526,6 +608,39 @@ public class PersonResource {
526608
NOTE: Be careful to use the `@Transactional` annotation on the operations that modify the database,
527609
you can add the annotation at the class level for simplicity purpose.
528610

611+
To make it easier to showcase some capabilities of Hibernate ORM with Panache on Quarkus with Dev mode, some test data should be inserted into the database by adding the following content to a new file named src/main/resources/import.sql:
612+
613+
[source,sql]
614+
----
615+
INSERT INTO person (id, birth, name, status) VALUES (nextval('hibernate_sequence'), '1995-09-12', 'Emily Brown', 0);
616+
----
617+
618+
NOTE: If you would like to initialize the DB when you start the Quarkus app in your production environment, add `quarkus.hibernate-orm.database.generation=drop-and-create` to the Quarkus startup options in addition to `import.sql`.
619+
620+
After that, you can see the people list and add new person as followings:
621+
622+
[source,shell]
623+
----
624+
$ curl -w "\n" http://localhost:8080/persons
625+
[{"id":1,"name":"Emily Brown","birth":"1995-09-12","status":"Alive"}]
626+
627+
$ curl -X POST -H "Content-Type: application/json" -d '{"name" : "William Davis" , "birth" : "1988-07-04", "status" : "Alive"}' http://localhost:8080/persons
628+
629+
$ curl -w "\n" http://localhost:8080/persons
630+
[{"id":1,"name":"Emily Brown","birth":"1995-09-12","status":"Alive"}, {"id":2,"name":"William Davis","birth":"1988-07-04","status":"Alive"}]
631+
----
632+
633+
NOTE: If you see the Person object as Person<1>, then the object has not been converted. In this case, add the dependency `quarkus-resteasy-reactive-jackson` in `pom.xml`.
634+
635+
[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
636+
.pom.xml
637+
----
638+
<dependency>
639+
<groupId>io.quarkus</groupId>
640+
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
641+
</dependency>
642+
----
643+
529644
== Advanced Query
530645

531646
=== Paging
@@ -535,6 +650,10 @@ sets you can use the `find` method equivalents, which return a `PanacheQuery` on
535650

536651
[source,java]
537652
----
653+
import io.quarkus.hibernate.orm.panache.PanacheQuery;
654+
import io.quarkus.panache.common.Page;
655+
import java.util.List;
656+
538657
// create a query for all living persons
539658
PanacheQuery<Person> livingPersons = Person.find("status", Status.Alive);
540659
@@ -571,6 +690,9 @@ The `PanacheQuery` type has many other methods to deal with paging and returning
571690

572691
[source,java]
573692
----
693+
import io.quarkus.hibernate.orm.panache.PanacheQuery;
694+
import java.util.List;
695+
574696
// create a query for all living persons
575697
PanacheQuery<Person> livingPersons = Person.find("status", Status.Alive);
576698
@@ -603,6 +725,8 @@ But these methods also accept an optional `Sort` parameter, which allows you to
603725

604726
[source,java]
605727
----
728+
import io.quarkus.panache.common.Sort;
729+
606730
List<Person> persons = Person.list(Sort.by("name").and("birth"));
607731
608732
// and with more restrictions
@@ -652,6 +776,16 @@ You can reference a named query instead of a (simplified) HQL query by prefixing
652776

653777
[source,java]
654778
----
779+
package org.acme;
780+
781+
import java.time.LocalDate;
782+
import jakarta.persistence.Entity;
783+
import jakarta.persistence.NamedQueries;
784+
import jakarta.persistence.NamedQuery;
785+
786+
import io.quarkus.hibernate.orm.panache.PanacheEntity;
787+
import io.quarkus.panache.common.Parameters;
788+
655789
@Entity
656790
@NamedQueries({
657791
@NamedQuery(name = "Person.getByName", query = "from Person where name = ?1"),
@@ -702,6 +836,9 @@ Or by name using a `Map`:
702836

703837
[source,java]
704838
----
839+
import java.util.HashMap;
840+
import java.util.Map;
841+
705842
Map<String, Object> params = new HashMap<>();
706843
params.put("name", "stef");
707844
params.put("status", Status.Alive);
@@ -740,6 +877,7 @@ instantiate the projection DTO instead of using the entity class. This class mus
740877
[source,java]
741878
----
742879
import io.quarkus.runtime.annotations.RegisterForReflection;
880+
import io.quarkus.hibernate.orm.panache.PanacheQuery;
743881
744882
@RegisterForReflection // <1>
745883
public class PersonName {
@@ -771,6 +909,9 @@ If in the DTO projection object you have a field from a referenced entity, you c
771909

772910
[source,java]
773911
----
912+
import jakarta.persistence.ManyToOne;
913+
import io.quarkus.hibernate.orm.panache.common.ProjectedFieldName;
914+
774915
@Entity
775916
public class Dog extends PanacheEntity {
776917
public String name;
@@ -858,6 +999,8 @@ And your transaction still has to be committed.
858999
Here is an example of the usage of the flush method to allow making a specific action in case of `PersistenceException`:
8591000
[source,java]
8601001
----
1002+
import jakarta.persistence.PersistenceException;
1003+
8611004
@Transactional
8621005
public void create(Parameter parameter){
8631006
try {
@@ -882,6 +1025,10 @@ The following examples are for the active record pattern, but the same can be us
8821025

8831026
[source,java]
8841027
----
1028+
import jakarta.persistence.LockModeType;
1029+
import jakarta.transaction.Transactional;
1030+
import jakarta.ws.rs.GET;
1031+
8851032
public class PersonEndpoint {
8861033
8871034
@GET
@@ -899,6 +1046,10 @@ public class PersonEndpoint {
8991046

9001047
[source,java]
9011048
----
1049+
import jakarta.persistence.LockModeType;
1050+
import jakarta.transaction.Transactional;
1051+
import jakarta.ws.rs.GET;
1052+
9021053
public class PersonEndpoint {
9031054
9041055
@GET
@@ -924,6 +1075,13 @@ you just declare whatever ID you want as a public field:
9241075

9251076
[source,java]
9261077
----
1078+
import jakarta.persistence.Entity;
1079+
import jakarta.persistence.GeneratedValue;
1080+
import jakarta.persistence.GenerationType;
1081+
import jakarta.persistence.Id;
1082+
import jakarta.persistence.SequenceGenerator;
1083+
import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
1084+
9271085
@Entity
9281086
public class Person extends PanacheEntityBase {
9291087
@@ -945,6 +1103,9 @@ and specify your ID type as an extra type parameter:
9451103

9461104
[source,java]
9471105
----
1106+
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
1107+
import jakarta.enterprise.context.ApplicationScoped;
1108+
9481109
@ApplicationScoped
9491110
public class PersonRepository implements PanacheRepositoryBase<Person,Integer> {
9501111
//...
@@ -990,6 +1151,14 @@ You can write your mocking test like this:
9901151

9911152
[source,java]
9921153
----
1154+
import io.quarkus.panache.mock.PanacheMock;
1155+
import io.quarkus.test.junit.QuarkusTest;
1156+
import org.junit.jupiter.api.Assertions;
1157+
import org.junit.jupiter.api.Test;
1158+
import org.mockito.Mockito;
1159+
import jakarta.ws.rs.WebApplicationException;
1160+
import java.util.Collections;
1161+
9931162
@QuarkusTest
9941163
public class PanacheFunctionalityTest {
9951164
@@ -1070,6 +1239,15 @@ If you need to mock entity instance methods, such as `persist()` you can do it b
10701239

10711240
[source,java]
10721241
----
1242+
import io.quarkus.test.junit.QuarkusTest;
1243+
import io.quarkus.test.junit.mockito.InjectMock;
1244+
import org.hibernate.Session;
1245+
import org.hibernate.query.Query;
1246+
import org.junit.jupiter.api.Assertions;
1247+
import org.junit.jupiter.api.BeforeEach;
1248+
import org.junit.jupiter.api.Test;
1249+
import org.mockito.Mockito;
1250+
10731251
@QuarkusTest
10741252
public class PanacheMockingTest {
10751253
@@ -1141,6 +1319,14 @@ You can write your mocking test like this:
11411319

11421320
[source,java]
11431321
----
1322+
import io.quarkus.test.junit.QuarkusTest;
1323+
import io.quarkus.test.junit.mockito.InjectMock;
1324+
import org.junit.jupiter.api.Assertions;
1325+
import org.junit.jupiter.api.Test;
1326+
import org.mockito.Mockito;
1327+
import jakarta.ws.rs.WebApplicationException;
1328+
import java.util.Collections;
1329+
11441330
@QuarkusTest
11451331
public class PanacheFunctionalityTest {
11461332
@InjectMock

0 commit comments

Comments
 (0)