or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

utilities.mddocs/

0

# Utility Operations

1

2

Utility classes for transaction management, EntityManager access, parameter building, and sorting configuration. These utilities provide essential supporting functionality for Panache operations and query building.

3

4

## Capabilities

5

6

### Panache Utility Class

7

8

The Panache utility class provides global operations for EntityManager access, transaction management, and database operations.

9

10

```java { .api }

11

public class Panache {

12

// EntityManager access

13

public static EntityManager getEntityManager();

14

public static Session getSession();

15

public static EntityManager getEntityManager(Class<?> clazz);

16

public static Session getSession(Class<?> clazz);

17

public static EntityManager getEntityManager(String persistenceUnit);

18

public static Session getSession(String persistenceUnit);

19

20

// Transaction management

21

public static TransactionManager getTransactionManager();

22

public static void setRollbackOnly();

23

24

// Database operations

25

public static int executeUpdate(String query, Object... params);

26

public static int executeUpdate(String query, Map<String, Object> params);

27

public static int executeUpdate(String query, Parameters params);

28

29

// Flushing

30

public static void flush();

31

public static void flush(Class<?> clazz);

32

public static void flush(String persistenceUnit);

33

}

34

```

35

36

**Usage:**

37

```java

38

import io.quarkus.hibernate.orm.panache.Panache;

39

40

// Access default EntityManager/Session

41

EntityManager em = Panache.getEntityManager();

42

Session session = Panache.getSession();

43

44

// Access EntityManager for specific entity class

45

EntityManager personEm = Panache.getEntityManager(Person.class);

46

Session personSession = Panache.getSession(Person.class);

47

48

// Access EntityManager for specific persistence unit

49

EntityManager customEm = Panache.getEntityManager("custom-pu");

50

51

// Transaction management

52

TransactionManager tm = Panache.getTransactionManager();

53

Panache.setRollbackOnly(); // Mark transaction for rollback

54

55

// Execute update queries

56

int updated = Panache.executeUpdate("UPDATE Person SET status = ?1 WHERE age > ?2", PersonStatus.Adult, 18);

57

58

// Execute with named parameters

59

int deleted = Panache.executeUpdate("DELETE FROM Person WHERE status = :status",

60

Parameters.with("status", PersonStatus.Inactive));

61

62

// Manual flush operations

63

Panache.flush(); // Flush default EntityManager

64

Panache.flush(Person.class); // Flush EntityManager for Person

65

Panache.flush("custom-pu"); // Flush specific persistence unit

66

```

67

68

### Parameters Builder

69

70

Utility class for building named parameter maps in a fluent manner.

71

72

```java { .api }

73

public class Parameters {

74

// Static factory method

75

public static Parameters with(String name, Object value);

76

77

// Builder methods

78

public Parameters and(String name, Object value);

79

80

// Convert to Map

81

public Map<String, Object> map();

82

}

83

```

84

85

**Usage:**

86

```java

87

import io.quarkus.panache.common.Parameters;

88

89

// Simple parameter building

90

Parameters params = Parameters.with("status", PersonStatus.Active)

91

.and("city", "NYC")

92

.and("minAge", 18);

93

94

// Use in queries

95

List<Person> persons = Person.list("status = :status AND city = :city AND age >= :minAge", params);

96

97

// Convert to Map if needed

98

Map<String, Object> paramMap = params.map();

99

100

// Complex parameter building

101

Parameters complexParams = Parameters.with("namePattern", "%John%")

102

.and("statuses", Arrays.asList(PersonStatus.Active, PersonStatus.Pending))

103

.and("startDate", LocalDate.now().minusYears(1))

104

.and("endDate", LocalDate.now());

105

106

List<Person> results = Person.list(

107

"name ILIKE :namePattern AND status IN :statuses AND createdDate BETWEEN :startDate AND :endDate",

108

complexParams

109

);

110

```

111

112

### Page Class

113

114

Immutable utility class for representing pagination information.

115

116

```java { .api }

117

public class Page {

118

// Fields

119

public final int index; // 0-based page index

120

public final int size; // Page size

121

122

// Constructors

123

public Page(int size);

124

public Page(int index, int size);

125

126

// Static factory methods

127

public static Page of(int index, int size);

128

public static Page ofSize(int size);

129

130

// Navigation methods

131

public Page next();

132

public Page previous();

133

public Page first();

134

public Page index(int newIndex);

135

}

136

```

137

138

**Usage:**

139

```java

140

import io.quarkus.panache.common.Page;

141

142

// Create page with default index 0

143

Page firstPage = new Page(20); // 20 items per page, page 0

144

Page firstPageAlt = Page.ofSize(20); // Same as above

145

146

// Create specific page

147

Page secondPage = new Page(1, 20); // Page 1 (second page), 20 items

148

Page secondPageAlt = Page.of(1, 20); // Same as above

149

150

// Navigation

151

Page page = Page.of(0, 10);

152

Page nextPage = page.next(); // Page(1, 10)

153

Page prevPage = nextPage.previous(); // Page(0, 10)

154

Page firstPage2 = page.first(); // Page(0, 10)

155

Page specificPage = page.index(5); // Page(5, 10)

156

157

// Use with queries

158

List<Person> persons = Person.findAll()

159

.page(Page.of(0, 25))

160

.list();

161

162

// Pagination loop

163

Page currentPage = Page.ofSize(100);

164

PanacheQuery<Person> query = Person.findAll();

165

166

while (true) {

167

List<Person> batch = query.page(currentPage).list();

168

if (batch.isEmpty()) break;

169

170

processBatch(batch);

171

currentPage = currentPage.next();

172

}

173

```

174

175

### Sort Class

176

177

Utility class for building SQL sorting specifications with direction and null precedence control.

178

179

```java { .api }

180

public class Sort {

181

// Static factory methods

182

public static Sort by(String column);

183

public static Sort by(String column, Direction direction);

184

public static Sort by(String column, NullPrecedence nullPrecedence);

185

public static Sort by(String column, Direction direction, NullPrecedence nullPrecedence);

186

public static Sort by(String... columns);

187

public static Sort ascending(String... columns);

188

public static Sort descending(String... columns);

189

public static Sort empty();

190

191

// Builder methods

192

public Sort and(String name);

193

public Sort and(String name, Direction direction);

194

public Sort and(String name, NullPrecedence nullPrecedence);

195

public Sort and(String name, Direction direction, NullPrecedence nullPrecedence);

196

197

// Direction control

198

public Sort descending();

199

public Sort ascending();

200

public Sort direction(Direction direction);

201

202

// Configuration

203

public Sort disableEscaping();

204

205

// Access methods

206

public List<Column> getColumns();

207

public boolean isEscapingEnabled();

208

209

// Nested types

210

public enum Direction { Ascending, Descending }

211

public enum NullPrecedence { NULLS_FIRST, NULLS_LAST }

212

213

public static class Column {

214

public String getName();

215

public Direction getDirection();

216

public NullPrecedence getNullPrecedence();

217

}

218

}

219

```

220

221

**Usage:**

222

```java

223

import io.quarkus.panache.common.Sort;

224

225

// Simple sorting

226

Sort nameSort = Sort.by("name");

227

Sort nameDescSort = Sort.by("name", Sort.Direction.Descending);

228

229

// Multiple columns

230

Sort multiSort = Sort.by("lastName", "firstName");

231

Sort mixedSort = Sort.by("lastName").and("firstName", Sort.Direction.Descending);

232

233

// Convenience methods

234

Sort ascSort = Sort.ascending("name", "age");

235

Sort descSort = Sort.descending("createdDate", "id");

236

237

// Null precedence

238

Sort nullsFirstSort = Sort.by("name", Sort.NullPrecedence.NULLS_FIRST);

239

Sort complexSort = Sort.by("status", Sort.Direction.Ascending, Sort.NullPrecedence.NULLS_LAST)

240

.and("name");

241

242

// Direction changes

243

Sort sort = Sort.by("name", "age").descending(); // Makes both columns descending

244

Sort mixed = Sort.by("name").and("age").direction(Sort.Direction.Descending);

245

246

// Use with queries

247

List<Person> sorted = Person.listAll(Sort.by("lastName", "firstName"));

248

249

List<Person> complexSorted = Person.list("status = :status",

250

Parameters.with("status", PersonStatus.Active),

251

Sort.by("priority", Sort.Direction.Descending)

252

.and("createdDate", Sort.Direction.Ascending)

253

);

254

255

// Disable column escaping if needed

256

Sort unescaped = Sort.by("custom_column").disableEscaping();

257

```

258

259

### Advanced Utility Usage Examples

260

261

Combining utilities for complex operations:

262

263

```java

264

public class PersonService {

265

266

@Transactional

267

public PagedResult<Person> searchPersonsPaginated(

268

String namePattern,

269

PersonStatus status,

270

String sortBy,

271

String sortDirection,

272

int page,

273

int size) {

274

275

// Build parameters

276

Parameters params = Parameters.with("namePattern", "%" + namePattern + "%")

277

.and("status", status);

278

279

// Build sort

280

Sort.Direction direction = "desc".equalsIgnoreCase(sortDirection)

281

? Sort.Direction.Descending

282

: Sort.Direction.Ascending;

283

Sort sort = Sort.by(sortBy, direction);

284

285

// Build page

286

Page pageObj = Page.of(page, size);

287

288

// Execute query

289

PanacheQuery<Person> query = Person.find("name ILIKE :namePattern AND status = :status", sort, params);

290

long totalCount = query.count();

291

List<Person> results = query.page(pageObj).list();

292

293

return new PagedResult<>(results, page, size, totalCount);

294

}

295

296

@Transactional

297

public void bulkUpdatePersonStatus(PersonStatus oldStatus, PersonStatus newStatus) {

298

// Use Panache utility for bulk update

299

int updated = Panache.executeUpdate(

300

"UPDATE Person SET status = :newStatus, updatedDate = :now WHERE status = :oldStatus",

301

Parameters.with("newStatus", newStatus)

302

.and("oldStatus", oldStatus)

303

.and("now", LocalDateTime.now())

304

);

305

306

// Force flush to ensure immediate update

307

Panache.flush();

308

309

log.info("Updated {} persons from {} to {}", updated, oldStatus, newStatus);

310

}

311

312

@Transactional

313

public void cleanupInactivePersons(int daysInactive) {

314

LocalDateTime cutoffDate = LocalDateTime.now().minusDays(daysInactive);

315

316

try {

317

// Get transaction manager for manual control

318

TransactionManager tm = Panache.getTransactionManager();

319

320

// Execute cleanup

321

int deleted = Panache.executeUpdate(

322

"DELETE FROM Person WHERE status = :status AND lastLoginDate < :cutoff",

323

Parameters.with("status", PersonStatus.Inactive)

324

.and("cutoff", cutoffDate)

325

);

326

327

if (deleted > 1000) {

328

// Too many deletions, rollback for safety

329

Panache.setRollbackOnly();

330

throw new IllegalStateException("Cleanup would delete too many records: " + deleted);

331

}

332

333

log.info("Cleaned up {} inactive persons", deleted);

334

335

} catch (Exception e) {

336

Panache.setRollbackOnly();

337

throw new RuntimeException("Cleanup failed", e);

338

}

339

}

340

341

public List<Person> getTopPersonsByActivity(int limit) {

342

// Complex sorting with null handling

343

Sort activitySort = Sort.by("lastLoginDate", Sort.Direction.Descending, Sort.NullPrecedence.NULLS_LAST)

344

.and("createdDate", Sort.Direction.Descending);

345

346

return Person.find("status = :status", Parameters.with("status", PersonStatus.Active))

347

.page(0, limit)

348

.list();

349

}

350

351

@Transactional

352

public void processPersonsBatch(PersonStatus status, int batchSize) {

353

// Use multiple persistence units if needed

354

EntityManager mainEm = Panache.getEntityManager();

355

EntityManager reportEm = Panache.getEntityManager("reporting");

356

357

Page currentPage = Page.ofSize(batchSize);

358

PanacheQuery<Person> query = Person.find("status", status);

359

360

int processedCount = 0;

361

while (true) {

362

List<Person> batch = query.page(currentPage).list();

363

if (batch.isEmpty()) break;

364

365

// Process batch

366

for (Person person : batch) {

367

processActivePerson(person);

368

processedCount++;

369

}

370

371

// Flush every batch

372

Panache.flush();

373

374

// Move to next page

375

currentPage = currentPage.next();

376

}

377

378

log.info("Processed {} persons with status {}", processedCount, status);

379

}

380

}

381

```

382

383

### Error Handling and Best Practices

384

385

Common patterns for using utilities safely:

386

387

```java

388

public class UtilityBestPractices {

389

390

// Safe parameter building

391

public Parameters buildSafeParameters(Map<String, Object> input) {

392

Parameters params = null;

393

for (Map.Entry<String, Object> entry : input.entrySet()) {

394

if (entry.getValue() != null) { // Skip null values

395

if (params == null) {

396

params = Parameters.with(entry.getKey(), entry.getValue());

397

} else {

398

params = params.and(entry.getKey(), entry.getValue());

399

}

400

}

401

}

402

return params != null ? params : Parameters.with("dummy", "dummy"); // Ensure not null

403

}

404

405

// Safe sort building

406

public Sort buildSafeSort(String sortBy, String direction) {

407

if (sortBy == null || sortBy.trim().isEmpty()) {

408

return Sort.by("id"); // Default sort

409

}

410

411

// Validate sort column to prevent SQL injection

412

if (!isValidColumnName(sortBy)) {

413

throw new IllegalArgumentException("Invalid sort column: " + sortBy);

414

}

415

416

Sort.Direction dir = "desc".equalsIgnoreCase(direction)

417

? Sort.Direction.Descending

418

: Sort.Direction.Ascending;

419

420

return Sort.by(sortBy, dir);

421

}

422

423

// Safe pagination

424

public Page buildSafePage(Integer page, Integer size) {

425

int safePage = (page != null && page >= 0) ? page : 0;

426

int safeSize = (size != null && size > 0 && size <= 1000) ? size : 20; // Max 1000

427

return Page.of(safePage, safeSize);

428

}

429

430

// Transaction-safe operations

431

@Transactional

432

public void safeExecuteUpdate(String query, Parameters params) {

433

try {

434

int updated = Panache.executeUpdate(query, params);

435

if (updated == 0) {

436

log.warn("No rows updated by query: {}", query);

437

} else {

438

log.info("Updated {} rows", updated);

439

}

440

} catch (Exception e) {

441

Panache.setRollbackOnly();

442

log.error("Update failed, transaction marked for rollback", e);

443

throw e;

444

}

445

}

446

447

private boolean isValidColumnName(String columnName) {

448

// Simple validation - extend as needed

449

return columnName.matches("^[a-zA-Z_][a-zA-Z0-9_]*$");

450

}

451

}

452

```