0
# Repository Pattern Operations
1
2
Repository-based persistence operations through PanacheRepository and PanacheRepositoryBase interfaces. This pattern provides clean separation of concerns by encapsulating entity operations within dedicated repository classes, following Domain-Driven Design principles.
3
4
## Capabilities
5
6
### Repository Interfaces
7
8
Foundation interfaces for the Repository pattern.
9
10
```java { .api }
11
public interface PanacheRepository<Entity> extends PanacheRepositoryBase<Entity, Long> {
12
// Repository for entities with Long ID - no additional methods
13
}
14
15
public interface PanacheRepositoryBase<Entity, Id> {
16
// All default methods for repository operations
17
18
// Entity Manager access
19
default EntityManager getEntityManager();
20
default Session getSession();
21
22
// Instance operations
23
default void persist(Entity entity);
24
default void persistAndFlush(Entity entity);
25
default void delete(Entity entity);
26
default boolean isPersistent(Entity entity);
27
default void flush();
28
}
29
```
30
31
**Usage:**
32
```java
33
@ApplicationScoped
34
public class PersonRepository implements PanacheRepository<Person> {
35
// Custom business methods
36
public Person findByEmail(String email) {
37
return find("email", email).firstResult();
38
}
39
40
public List<Person> findByStatus(PersonStatus status) {
41
return list("status", status);
42
}
43
}
44
45
// Injection and usage
46
@Inject
47
PersonRepository personRepository;
48
49
Person person = new Person();
50
personRepository.persist(person);
51
```
52
53
### Finding Entities
54
55
Repository methods for finding entities by ID or query.
56
57
```java { .api }
58
default Entity findById(Id id);
59
60
default Entity findById(Id id, LockModeType lockModeType);
61
62
default Optional<Entity> findByIdOptional(Id id);
63
64
default Optional<Entity> findByIdOptional(Id id, LockModeType lockModeType);
65
66
default PanacheQuery<Entity> find(String query, Object... params);
67
68
default PanacheQuery<Entity> find(String query, Sort sort, Object... params);
69
70
default PanacheQuery<Entity> find(String query, Map<String, Object> params);
71
72
default PanacheQuery<Entity> find(String query, Sort sort, Map<String, Object> params);
73
74
default PanacheQuery<Entity> find(String query, Parameters params);
75
76
default PanacheQuery<Entity> find(String query, Sort sort, Parameters params);
77
78
default PanacheQuery<Entity> findAll();
79
80
default PanacheQuery<Entity> findAll(Sort sort);
81
```
82
83
**Usage:**
84
```java
85
@ApplicationScoped
86
public class PersonRepository implements PanacheRepository<Person> {
87
88
public Person getPersonById(Long id) {
89
return findById(id); // Returns null if not found
90
}
91
92
public Optional<Person> findPersonById(Long id) {
93
return findByIdOptional(id); // Returns Optional
94
}
95
96
public List<Person> getActivePersons() {
97
return find("status", PersonStatus.Active).list();
98
}
99
100
public PanacheQuery<Person> findByAgeRange(int minAge, int maxAge) {
101
return find("age >= ?1 AND age <= ?2", minAge, maxAge);
102
}
103
104
public List<Person> getAllSorted() {
105
return findAll(Sort.by("name", "age")).list();
106
}
107
}
108
```
109
110
### Listing Entities
111
112
Repository methods that directly return Lists (shortcuts for find().list()).
113
114
```java { .api }
115
default List<Entity> list(String query, Object... params);
116
117
default List<Entity> list(String query, Sort sort, Object... params);
118
119
default List<Entity> list(String query, Map<String, Object> params);
120
121
default List<Entity> list(String query, Sort sort, Map<String, Object> params);
122
123
default List<Entity> list(String query, Parameters params);
124
125
default List<Entity> list(String query, Sort sort, Parameters params);
126
127
default List<Entity> listAll();
128
129
default List<Entity> listAll(Sort sort);
130
```
131
132
**Usage:**
133
```java
134
@ApplicationScoped
135
public class PersonRepository implements PanacheRepository<Person> {
136
137
public List<Person> getActivePersons() {
138
return list("status", PersonStatus.Active);
139
}
140
141
public List<Person> getPersonsByCity(String city) {
142
return list("city = :city", Parameters.with("city", city));
143
}
144
145
public List<Person> getAllPersonsSorted() {
146
return listAll(Sort.by("lastName", "firstName"));
147
}
148
149
public List<Person> searchPersons(String namePattern, PersonStatus status) {
150
Map<String, Object> params = Map.of(
151
"pattern", "%" + namePattern + "%",
152
"status", status
153
);
154
return list("name ILIKE :pattern AND status = :status", params);
155
}
156
}
157
```
158
159
### Streaming Entities
160
161
Repository methods that return Streams for memory-efficient processing (require active transaction).
162
163
```java { .api }
164
default Stream<Entity> stream(String query, Object... params);
165
166
default Stream<Entity> stream(String query, Sort sort, Object... params);
167
168
default Stream<Entity> stream(String query, Map<String, Object> params);
169
170
default Stream<Entity> stream(String query, Sort sort, Map<String, Object> params);
171
172
default Stream<Entity> stream(String query, Parameters params);
173
174
default Stream<Entity> stream(String query, Sort sort, Parameters params);
175
176
default Stream<Entity> streamAll();
177
178
default Stream<Entity> streamAll(Sort sort);
179
```
180
181
**Usage:**
182
```java
183
@ApplicationScoped
184
public class PersonRepository implements PanacheRepository<Person> {
185
186
@Transactional
187
public void processAllActivePersons() {
188
stream("status", PersonStatus.Active)
189
.forEach(this::processActivePerson);
190
}
191
192
@Transactional
193
public List<String> getActivePersonEmails() {
194
return stream("status = :status", Parameters.with("status", PersonStatus.Active))
195
.map(person -> person.email)
196
.filter(Objects::nonNull)
197
.collect(Collectors.toList());
198
}
199
200
@Transactional
201
public long countAdults() {
202
return streamAll()
203
.filter(person -> person.age >= 18)
204
.count();
205
}
206
}
207
```
208
209
### Counting Entities
210
211
Repository methods for counting entities without loading them.
212
213
```java { .api }
214
default long count();
215
216
default long count(String query, Object... params);
217
218
default long count(String query, Map<String, Object> params);
219
220
default long count(String query, Parameters params);
221
```
222
223
**Usage:**
224
```java
225
@ApplicationScoped
226
public class PersonRepository implements PanacheRepository<Person> {
227
228
public long getTotalPersonCount() {
229
return count();
230
}
231
232
public long getActivePersonCount() {
233
return count("status", PersonStatus.Active);
234
}
235
236
public long getPersonCountByCity(String city) {
237
return count("city = :city", Parameters.with("city", city));
238
}
239
240
public long getAdultCount() {
241
return count("age >= :minAge", Parameters.with("minAge", 18));
242
}
243
}
244
```
245
246
### Persisting Entities
247
248
Repository methods for saving entities to the database.
249
250
```java { .api }
251
// Single entity persistence
252
default void persist(Entity entity);
253
default void persistAndFlush(Entity entity);
254
255
// Batch persistence
256
default void persist(Iterable<Entity> entities);
257
default void persist(Stream<Entity> entities);
258
default void persist(Entity firstEntity, Entity... entities);
259
```
260
261
**Usage:**
262
```java
263
@ApplicationScoped
264
public class PersonRepository implements PanacheRepository<Person> {
265
266
public void savePerson(Person person) {
267
persist(person);
268
}
269
270
public void savePersonAndFlush(Person person) {
271
persistAndFlush(person); // Immediately flushes to DB
272
}
273
274
public void saveMultiplePersons(List<Person> persons) {
275
persist(persons); // Batch persist
276
}
277
278
public void savePersons(Person... persons) {
279
persist(persons[0], Arrays.copyOfRange(persons, 1, persons.length));
280
}
281
282
@Transactional
283
public Person createPerson(String name, String email) {
284
Person person = new Person();
285
person.name = name;
286
person.email = email;
287
person.status = PersonStatus.Active;
288
persist(person);
289
return person;
290
}
291
}
292
```
293
294
### Deleting Entities
295
296
Repository methods for removing entities from the database.
297
298
```java { .api }
299
// Single entity deletion
300
default void delete(Entity entity);
301
302
// Bulk deletion methods
303
default long deleteAll();
304
default boolean deleteById(Id id);
305
default long delete(String query, Object... params);
306
default long delete(String query, Map<String, Object> params);
307
default long delete(String query, Parameters params);
308
```
309
310
**Usage:**
311
```java
312
@ApplicationScoped
313
public class PersonRepository implements PanacheRepository<Person> {
314
315
public void removePerson(Person person) {
316
delete(person);
317
}
318
319
public boolean removePersonById(Long id) {
320
return deleteById(id); // Returns true if entity was deleted
321
}
322
323
public long removeInactivePersons() {
324
return delete("status", PersonStatus.Inactive);
325
}
326
327
public long removeOldPersons(LocalDate cutoffDate) {
328
return delete("createdDate < :cutoff",
329
Parameters.with("cutoff", cutoffDate));
330
}
331
332
public void clearAllPersons() {
333
deleteAll(); // Removes all entities
334
}
335
}
336
```
337
338
### Updating Entities
339
340
Repository methods for bulk update operations using HQL.
341
342
```java { .api }
343
default int update(String query, Object... params);
344
default int update(String query, Map<String, Object> params);
345
default int update(String query, Parameters params);
346
```
347
348
**Usage:**
349
```java
350
@ApplicationScoped
351
public class PersonRepository implements PanacheRepository<Person> {
352
353
public int activatePersonsByCity(String city) {
354
return update("status = ?1 where city = ?2",
355
PersonStatus.Active, city);
356
}
357
358
public int updatePersonStatus(PersonStatus oldStatus, PersonStatus newStatus) {
359
return update("status = :newStatus where status = :oldStatus",
360
Parameters.with("newStatus", newStatus).and("oldStatus", oldStatus));
361
}
362
363
public int markAdults() {
364
return update("status = :adult where age >= :minAge",
365
Parameters.with("adult", PersonStatus.Adult).and("minAge", 18));
366
}
367
}
368
```
369
370
### Entity Manager Access
371
372
Direct access to EntityManager and Session for advanced operations.
373
374
```java { .api }
375
default EntityManager getEntityManager();
376
default Session getSession();
377
default void flush();
378
```
379
380
**Usage:**
381
```java
382
@ApplicationScoped
383
public class PersonRepository implements PanacheRepository<Person> {
384
385
public List<Person> findWithCriteria(String namePattern) {
386
EntityManager em = getEntityManager();
387
CriteriaBuilder cb = em.getCriteriaBuilder();
388
CriteriaQuery<Person> cq = cb.createQuery(Person.class);
389
Root<Person> root = cq.from(Person.class);
390
391
cq.select(root)
392
.where(cb.like(root.get("name"), namePattern));
393
394
return em.createQuery(cq).getResultList();
395
}
396
397
public List<Person> executeNativeQuery(String sql, Object... params) {
398
Session session = getSession();
399
Query<Person> query = session.createNativeQuery(sql, Person.class);
400
for (int i = 0; i < params.length; i++) {
401
query.setParameter(i + 1, params[i]);
402
}
403
return query.getResultList();
404
}
405
406
public void forceFlush() {
407
flush(); // Forces pending changes to database
408
}
409
}
410
```
411
412
### Entity State Management
413
414
Repository methods for checking and managing entity persistence state.
415
416
```java { .api }
417
default boolean isPersistent(Entity entity);
418
```
419
420
**Usage:**
421
```java
422
@ApplicationScoped
423
public class PersonRepository implements PanacheRepository<Person> {
424
425
public void saveIfNotPersistent(Person person) {
426
if (!isPersistent(person)) {
427
persist(person);
428
}
429
}
430
431
public String getPersonState(Person person) {
432
return isPersistent(person) ? "MANAGED" : "DETACHED";
433
}
434
435
@Transactional
436
public Person saveOrUpdate(Person person) {
437
if (isPersistent(person)) {
438
// Already managed, changes will be auto-saved
439
return person;
440
} else {
441
// Detached or new, need to persist
442
persist(person);
443
return person;
444
}
445
}
446
}
447
```
448
449
## Repository Pattern Best Practices
450
451
### Dependency Injection
452
```java
453
// Inject repositories in service classes
454
@ApplicationScoped
455
public class PersonService {
456
457
@Inject
458
PersonRepository personRepository;
459
460
@Inject
461
AddressRepository addressRepository;
462
463
@Transactional
464
public Person createPersonWithAddress(PersonDto dto) {
465
Person person = new Person();
466
person.name = dto.name;
467
person.email = dto.email;
468
personRepository.persist(person);
469
470
Address address = new Address();
471
address.person = person;
472
address.street = dto.street;
473
addressRepository.persist(address);
474
475
return person;
476
}
477
}
478
```
479
480
### Custom Repository Methods
481
```java
482
@ApplicationScoped
483
public class PersonRepository implements PanacheRepository<Person> {
484
485
// Business-specific finder methods
486
public Optional<Person> findByEmail(String email) {
487
return find("email", email).firstResultOptional();
488
}
489
490
public List<Person> findAdultsByCity(String city) {
491
return list("age >= 18 AND city = ?1", city);
492
}
493
494
// Complex queries with custom logic
495
public List<Person> findRecentlyActive(int days) {
496
LocalDateTime cutoff = LocalDateTime.now().minusDays(days);
497
return list("lastLoginDate >= ?1", cutoff);
498
}
499
500
// Validation and business rules
501
@Transactional
502
public Person createVerifiedPerson(String name, String email) {
503
if (findByEmail(email).isPresent()) {
504
throw new IllegalArgumentException("Email already exists");
505
}
506
507
Person person = new Person();
508
person.name = name;
509
person.email = email;
510
person.status = PersonStatus.Verified;
511
person.createdDate = LocalDateTime.now();
512
513
persist(person);
514
return person;
515
}
516
}
517
```