or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

active-record.mdindex.mdquery-pagination.mdrepository-pattern.mdutilities.md

repository-pattern.mddocs/

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

```