or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

aop.mdcore-container.mddata-access.mdindex.mdintegration.mdmessaging.mdreactive-web.mdtesting.mdweb-framework.md

data-access.mddocs/

0

# Data Access & Transaction Management

1

2

Spring provides comprehensive data access support including JDBC abstraction, transaction management, and integration with ORM frameworks like Hibernate and JPA. This includes spring-jdbc, spring-tx, spring-orm, and spring-r2dbc modules.

3

4

## Maven Dependencies

5

6

```xml

7

<!-- Spring JDBC -->

8

<dependency>

9

<groupId>org.springframework</groupId>

10

<artifactId>spring-jdbc</artifactId>

11

<version>5.3.39</version>

12

</dependency>

13

14

<!-- Spring Transaction Management -->

15

<dependency>

16

<groupId>org.springframework</groupId>

17

<artifactId>spring-tx</artifactId>

18

<version>5.3.39</version>

19

</dependency>

20

21

<!-- Spring ORM -->

22

<dependency>

23

<groupId>org.springframework</groupId>

24

<artifactId>spring-orm</artifactId>

25

<version>5.3.39</version>

26

</dependency>

27

28

<!-- Spring R2DBC (Reactive) -->

29

<dependency>

30

<groupId>org.springframework</groupId>

31

<artifactId>spring-r2dbc</artifactId>

32

<version>5.3.39</version>

33

</dependency>

34

35

<!-- Database driver (example: H2) -->

36

<dependency>

37

<groupId>com.h2database</groupId>

38

<artifactId>h2</artifactId>

39

<version>2.1.214</version>

40

</dependency>

41

```

42

43

## Core Imports

44

45

```java { .api }

46

// JDBC Core

47

import org.springframework.jdbc.core.JdbcTemplate;

48

import org.springframework.jdbc.core.NamedParameterJdbcTemplate;

49

import org.springframework.jdbc.core.RowMapper;

50

import org.springframework.jdbc.core.ResultSetExtractor;

51

import org.springframework.jdbc.core.PreparedStatementCreator;

52

import org.springframework.jdbc.core.PreparedStatementSetter;

53

54

// Data Source

55

import javax.sql.DataSource;

56

import org.springframework.jdbc.datasource.DriverManagerDataSource;

57

import org.springframework.jdbc.datasource.DataSourceTransactionManager;

58

59

// Transaction Management

60

import org.springframework.transaction.PlatformTransactionManager;

61

import org.springframework.transaction.TransactionDefinition;

62

import org.springframework.transaction.TransactionStatus;

63

import org.springframework.transaction.support.TransactionTemplate;

64

import org.springframework.transaction.annotation.Transactional;

65

import org.springframework.transaction.annotation.EnableTransactionManagement;

66

67

// ORM Integration

68

import org.springframework.orm.jpa.JpaTransactionManager;

69

import org.springframework.orm.jpa.LocalEntityManagerFactoryBean;

70

import org.springframework.orm.hibernate5.HibernateTransactionManager;

71

import org.springframework.orm.hibernate5.LocalSessionFactoryBean;

72

73

// Exception Translation

74

import org.springframework.dao.DataAccessException;

75

import org.springframework.dao.EmptyResultDataAccessException;

76

import org.springframework.dao.DuplicateKeyException;

77

78

// R2DBC (Reactive)

79

import org.springframework.r2dbc.core.DatabaseClient;

80

import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;

81

```

82

83

## JDBC Support

84

85

### JdbcTemplate

86

87

```java { .api }

88

// Central class in JDBC core package providing JDBC operations

89

public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {

90

91

// Query methods

92

public <T> T queryForObject(String sql, Class<T> requiredType);

93

public <T> T queryForObject(String sql, Class<T> requiredType, Object... args);

94

public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args);

95

public <T> List<T> query(String sql, RowMapper<T> rowMapper);

96

public <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... args);

97

public <T> List<T> query(String sql, PreparedStatementSetter pss, RowMapper<T> rowMapper);

98

public <T> T query(String sql, ResultSetExtractor<T> rse);

99

100

// Update methods

101

public int update(String sql);

102

public int update(String sql, Object... args);

103

public int update(String sql, PreparedStatementSetter pss);

104

public int update(PreparedStatementCreator psc);

105

106

// Batch operations

107

public int[] batchUpdate(String sql, List<Object[]> batchArgs);

108

public int[] batchUpdate(String sql, BatchPreparedStatementSetter pss);

109

110

// Execute methods

111

public void execute(String sql);

112

public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action);

113

}

114

115

// RowMapper interface for mapping rows of ResultSet

116

@FunctionalInterface

117

public interface RowMapper<T> {

118

T mapRow(ResultSet rs, int rowNum) throws SQLException;

119

}

120

121

// Interface used by JdbcTemplate for extracting values from ResultSet

122

@FunctionalInterface

123

public interface ResultSetExtractor<T> {

124

T extractData(ResultSet rs) throws SQLException, DataAccessException;

125

}

126

127

// Interface for setting values on PreparedStatement

128

@FunctionalInterface

129

public interface PreparedStatementSetter {

130

void setValues(PreparedStatement ps) throws SQLException;

131

}

132

```

133

134

### NamedParameterJdbcTemplate

135

136

```java { .api }

137

// Template class with support for named parameters

138

public class NamedParameterJdbcTemplate implements NamedParameterJdbcOperations {

139

140

public NamedParameterJdbcTemplate(DataSource dataSource);

141

public NamedParameterJdbcTemplate(JdbcOperations classicJdbcTemplate);

142

143

// Query methods with named parameters

144

public <T> T queryForObject(String sql, Map<String, ?> paramMap, Class<T> requiredType);

145

public <T> T queryForObject(String sql, SqlParameterSource paramSource, Class<T> requiredType);

146

public <T> T queryForObject(String sql, Map<String, ?> paramMap, RowMapper<T> rowMapper);

147

public <T> List<T> query(String sql, Map<String, ?> paramMap, RowMapper<T> rowMapper);

148

public <T> List<T> query(String sql, SqlParameterSource paramSource, RowMapper<T> rowMapper);

149

150

// Update methods with named parameters

151

public int update(String sql, Map<String, ?> paramMap);

152

public int update(String sql, SqlParameterSource paramSource);

153

154

// Batch operations

155

public int[] batchUpdate(String sql, Map<String, ?>[] batchValues);

156

public int[] batchUpdate(String sql, SqlParameterSource[] batchArgs);

157

}

158

159

// Interface for parameter sources

160

public interface SqlParameterSource {

161

boolean hasValue(String paramName);

162

Object getValue(String paramName) throws IllegalArgumentException;

163

int getSqlType(String paramName);

164

String getTypeName(String paramName);

165

String[] getParameterNames();

166

}

167

168

// Map-based parameter source

169

public class MapSqlParameterSource implements SqlParameterSource {

170

public MapSqlParameterSource();

171

public MapSqlParameterSource(String paramName, Object value);

172

public MapSqlParameterSource(Map<String, ?> values);

173

174

public MapSqlParameterSource addValue(String paramName, Object value);

175

public MapSqlParameterSource addValue(String paramName, Object value, int sqlType);

176

public MapSqlParameterSource addValues(Map<String, ?> values);

177

}

178

```

179

180

## Transaction Management

181

182

### Core Transaction Interfaces

183

184

```java { .api }

185

// Central interface for transaction management

186

public interface PlatformTransactionManager {

187

TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

188

void commit(TransactionStatus status) throws TransactionException;

189

void rollback(TransactionStatus status) throws TransactionException;

190

}

191

192

// Interface that defines Spring-compliant transaction properties

193

public interface TransactionDefinition {

194

int PROPAGATION_REQUIRED = 0;

195

int PROPAGATION_SUPPORTS = 1;

196

int PROPAGATION_MANDATORY = 2;

197

int PROPAGATION_REQUIRES_NEW = 3;

198

int PROPAGATION_NOT_SUPPORTED = 4;

199

int PROPAGATION_NEVER = 5;

200

int PROPAGATION_NESTED = 6;

201

202

int ISOLATION_DEFAULT = -1;

203

int ISOLATION_READ_UNCOMMITTED = 1;

204

int ISOLATION_READ_COMMITTED = 2;

205

int ISOLATION_REPEATABLE_READ = 4;

206

int ISOLATION_SERIALIZABLE = 8;

207

208

int TIMEOUT_DEFAULT = -1;

209

210

int getPropagationBehavior();

211

int getIsolationLevel();

212

int getTimeout();

213

boolean isReadOnly();

214

String getName();

215

}

216

217

// Representation of status of transaction

218

public interface TransactionStatus extends SavepointManager, Flushable {

219

boolean isNewTransaction();

220

boolean hasSavepoint();

221

void setRollbackOnly();

222

boolean isRollbackOnly();

223

boolean isCompleted();

224

}

225

226

// Template class that simplifies programmatic transaction demarcation

227

public class TransactionTemplate extends DefaultTransactionDefinition implements TransactionOperations, InitializingBean {

228

229

public TransactionTemplate();

230

public TransactionTemplate(PlatformTransactionManager transactionManager);

231

232

public <T> T execute(TransactionCallback<T> action) throws TransactionException;

233

public void execute(TransactionCallbackWithoutResult action) throws TransactionException;

234

}

235

```

236

237

### Declarative Transactions

238

239

```java { .api }

240

// Describes transaction attributes on individual methods or classes

241

@Target({ElementType.METHOD, ElementType.TYPE})

242

@Retention(RetentionPolicy.RUNTIME)

243

@Inherited

244

@Documented

245

public @interface Transactional {

246

247

@AliasFor("transactionManager")

248

String value() default "";

249

250

String transactionManager() default "";

251

252

Propagation propagation() default Propagation.REQUIRED;

253

254

Isolation isolation() default Isolation.DEFAULT;

255

256

int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

257

258

boolean readOnly() default false;

259

260

Class<? extends Throwable>[] rollbackFor() default {};

261

262

String[] rollbackForClassName() default {};

263

264

Class<? extends Throwable>[] noRollbackFor() default {};

265

266

String[] noRollbackForClassName() default {};

267

}

268

269

// Enables Spring's annotation-driven transaction management capability

270

@Target(ElementType.TYPE)

271

@Retention(RetentionPolicy.RUNTIME)

272

@Documented

273

@Import(TransactionManagementConfigurationSelector.class)

274

public @interface EnableTransactionManagement {

275

boolean proxyTargetClass() default false;

276

AdviceMode mode() default AdviceMode.PROXY;

277

int order() default Ordered.LOWEST_PRECEDENCE;

278

}

279

280

// Propagation enum

281

public enum Propagation {

282

REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

283

SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

284

MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

285

REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

286

NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

287

NEVER(TransactionDefinition.PROPAGATION_NEVER),

288

NESTED(TransactionDefinition.PROPAGATION_NESTED);

289

}

290

291

// Isolation enum

292

public enum Isolation {

293

DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),

294

READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),

295

READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),

296

REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),

297

SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);

298

}

299

```

300

301

### Transaction Managers

302

303

```java { .api }

304

// PlatformTransactionManager implementation for single JDBC DataSource

305

public class DataSourceTransactionManager extends AbstractPlatformTransactionManager

306

implements ResourceTransactionManager, InitializingBean {

307

308

public DataSourceTransactionManager();

309

public DataSourceTransactionManager(DataSource dataSource);

310

311

public void setDataSource(DataSource dataSource);

312

public DataSource getDataSource();

313

public void setEnforceReadOnly(boolean enforceReadOnly);

314

}

315

316

// PlatformTransactionManager for JPA EntityManagerFactory

317

public class JpaTransactionManager extends AbstractPlatformTransactionManager

318

implements ResourceTransactionManager, BeanFactoryAware, InitializingBean {

319

320

public JpaTransactionManager();

321

public JpaTransactionManager(EntityManagerFactory emf);

322

323

public void setEntityManagerFactory(EntityManagerFactory emf);

324

public EntityManagerFactory getEntityManagerFactory();

325

public void setDataSource(DataSource dataSource);

326

}

327

328

// PlatformTransactionManager for Hibernate SessionFactory

329

public class HibernateTransactionManager extends AbstractPlatformTransactionManager

330

implements ResourceTransactionManager, BeanFactoryAware, InitializingBean {

331

332

public HibernateTransactionManager();

333

public HibernateTransactionManager(SessionFactory sessionFactory);

334

335

public void setSessionFactory(SessionFactory sessionFactory);

336

public SessionFactory getSessionFactory();

337

public void setDataSource(DataSource dataSource);

338

}

339

```

340

341

## ORM Integration

342

343

### JPA Integration

344

345

```java { .api }

346

// FactoryBean for creating JPA EntityManagerFactory

347

public class LocalEntityManagerFactoryBean extends AbstractEntityManagerFactoryBean {

348

349

public void setPersistenceUnitName(String persistenceUnitName);

350

public void setPersistenceUnitPostProcessors(PersistenceUnitPostProcessor... postProcessors);

351

352

@Override

353

protected EntityManagerFactory createNativeEntityManagerFactory() throws PersistenceException;

354

}

355

356

// Extended EntityManagerFactory interface for container integration

357

public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManagerFactoryBean

358

implements ResourceLoaderAware, LoadTimeWeaverAware {

359

360

public void setDataSource(DataSource dataSource);

361

public void setPackagesToScan(String... packagesToScan);

362

public void setJpaVendorAdapter(JpaVendorAdapter jpaVendorAdapter);

363

public void setJpaProperties(Properties jpaProperties);

364

}

365

366

// JPA Vendor Adapter for Hibernate

367

public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter {

368

public void setDatabase(Database database);

369

public void setDatabasePlatform(String databasePlatform);

370

public void setGenerateDdl(boolean generateDdl);

371

public void setShowSql(boolean showSql);

372

}

373

```

374

375

### Hibernate Integration

376

377

```java { .api }

378

// FactoryBean for creating Hibernate SessionFactory

379

public class LocalSessionFactoryBean extends HibernateExceptionTranslator

380

implements FactoryBean<SessionFactory>, ResourceLoaderAware, InitializingBean, DisposableBean {

381

382

public void setDataSource(DataSource dataSource);

383

public void setPackagesToScan(String... packagesToScan);

384

public void setAnnotatedClasses(Class<?>... annotatedClasses);

385

public void setHibernateProperties(Properties hibernateProperties);

386

public void setConfigLocation(Resource configLocation);

387

}

388

389

// Helper class for Hibernate data access (legacy approach)

390

public class HibernateTemplate extends HibernateAccessor implements HibernateOperations {

391

392

public HibernateTemplate();

393

public HibernateTemplate(SessionFactory sessionFactory);

394

395

// CRUD operations

396

public void save(Object entity);

397

public void update(Object entity);

398

public void saveOrUpdate(Object entity);

399

public void delete(Object entity);

400

public <T> T get(Class<T> entityClass, Serializable id);

401

public <T> T load(Class<T> entityClass, Serializable id);

402

403

// Query operations

404

public List<?> find(String queryString, Object... values);

405

public <T> List<T> findByNamedParam(String queryString, String[] paramNames, Object[] values);

406

}

407

```

408

409

## Reactive Data Access (R2DBC)

410

411

### R2DBC Support

412

413

```java { .api }

414

// Configuration base class for R2DBC

415

public abstract class AbstractR2dbcConfiguration {

416

417

@Bean

418

public abstract ConnectionFactory connectionFactory();

419

420

@Bean

421

public R2dbcTransactionManager transactionManager() {

422

return new R2dbcTransactionManager(connectionFactory());

423

}

424

425

@Bean

426

public DatabaseClient databaseClient() {

427

return DatabaseClient.builder()

428

.connectionFactory(connectionFactory())

429

.build();

430

}

431

}

432

433

// Reactive database client

434

public interface DatabaseClient {

435

436

GenericExecuteSpec sql(String sql);

437

GenericExecuteSpec sql(Supplier<String> sqlSupplier);

438

439

// Execute spec for generic SQL operations

440

interface GenericExecuteSpec {

441

GenericExecuteSpec bind(Object identifier, Object value);

442

GenericExecuteSpec bind(int index, Object value);

443

GenericExecuteSpec bindNull(Object identifier, Class<?> type);

444

445

FetchSpec<Map<String, Object>> fetch();

446

<T> FetchSpec<T> map(Function<Row, T> mappingFunction);

447

<T> FetchSpec<T> map(BiFunction<Row, RowMetadata, T> mappingFunction);

448

449

Mono<Void> then();

450

}

451

452

// Fetch specification

453

interface FetchSpec<T> {

454

Mono<T> one();

455

Mono<T> first();

456

Flux<T> all();

457

Mono<Long> rowsUpdated();

458

}

459

}

460

461

// R2DBC Transaction Manager

462

public class R2dbcTransactionManager extends AbstractReactiveTransactionManager

463

implements ResourceTransactionManager, InitializingBean {

464

465

public R2dbcTransactionManager(ConnectionFactory connectionFactory);

466

public void setConnectionFactory(ConnectionFactory connectionFactory);

467

public ConnectionFactory getConnectionFactory();

468

}

469

```

470

471

## Practical Usage Examples

472

473

### Basic JDBC Operations

474

475

```java { .api }

476

@Repository

477

public class UserRepository {

478

479

private final JdbcTemplate jdbcTemplate;

480

481

public UserRepository(JdbcTemplate jdbcTemplate) {

482

this.jdbcTemplate = jdbcTemplate;

483

}

484

485

// Simple query for object

486

public User findById(Long id) {

487

String sql = "SELECT id, username, email, created_date FROM users WHERE id = ?";

488

return jdbcTemplate.queryForObject(sql, new UserRowMapper(), id);

489

}

490

491

// Query for list

492

public List<User> findByEmail(String email) {

493

String sql = "SELECT id, username, email, created_date FROM users WHERE email LIKE ?";

494

return jdbcTemplate.query(sql, new UserRowMapper(), "%" + email + "%");

495

}

496

497

// Insert operation

498

public void save(User user) {

499

String sql = "INSERT INTO users (username, email, created_date) VALUES (?, ?, ?)";

500

jdbcTemplate.update(sql, user.getUsername(), user.getEmail(), user.getCreatedDate());

501

}

502

503

// Update operation

504

public void update(User user) {

505

String sql = "UPDATE users SET username = ?, email = ? WHERE id = ?";

506

int rowsAffected = jdbcTemplate.update(sql, user.getUsername(), user.getEmail(), user.getId());

507

508

if (rowsAffected == 0) {

509

throw new EmptyResultDataAccessException("User not found: " + user.getId(), 1);

510

}

511

}

512

513

// Delete operation

514

public void delete(Long id) {

515

String sql = "DELETE FROM users WHERE id = ?";

516

jdbcTemplate.update(sql, id);

517

}

518

519

// Custom RowMapper

520

private static class UserRowMapper implements RowMapper<User> {

521

@Override

522

public User mapRow(ResultSet rs, int rowNum) throws SQLException {

523

User user = new User();

524

user.setId(rs.getLong("id"));

525

user.setUsername(rs.getString("username"));

526

user.setEmail(rs.getString("email"));

527

user.setCreatedDate(rs.getTimestamp("created_date").toLocalDateTime());

528

return user;

529

}

530

}

531

}

532

```

533

534

### Named Parameter Operations

535

536

```java { .api }

537

@Repository

538

public class OrderRepository {

539

540

private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;

541

542

public OrderRepository(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {

543

this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;

544

}

545

546

// Named parameters with Map

547

public List<Order> findByUserId(Long userId) {

548

String sql = "SELECT id, user_id, total_amount, order_date FROM orders WHERE user_id = :userId";

549

550

Map<String, Object> params = new HashMap<>();

551

params.put("userId", userId);

552

553

return namedParameterJdbcTemplate.query(sql, params, new OrderRowMapper());

554

}

555

556

// Named parameters with MapSqlParameterSource

557

public void save(Order order) {

558

String sql = "INSERT INTO orders (user_id, total_amount, order_date) VALUES (:userId, :totalAmount, :orderDate)";

559

560

MapSqlParameterSource params = new MapSqlParameterSource()

561

.addValue("userId", order.getUserId())

562

.addValue("totalAmount", order.getTotalAmount())

563

.addValue("orderDate", order.getOrderDate());

564

565

namedParameterJdbcTemplate.update(sql, params);

566

}

567

568

// Batch operations

569

public void saveBatch(List<Order> orders) {

570

String sql = "INSERT INTO orders (user_id, total_amount, order_date) VALUES (:userId, :totalAmount, :orderDate)";

571

572

SqlParameterSource[] batchParams = orders.stream()

573

.map(order -> new MapSqlParameterSource()

574

.addValue("userId", order.getUserId())

575

.addValue("totalAmount", order.getTotalAmount())

576

.addValue("orderDate", order.getOrderDate()))

577

.toArray(SqlParameterSource[]::new);

578

579

namedParameterJdbcTemplate.batchUpdate(sql, batchParams);

580

}

581

582

// Complex queries with multiple parameters

583

public List<Order> findOrdersByDateRange(LocalDateTime startDate, LocalDateTime endDate, OrderStatus status) {

584

String sql = """

585

SELECT id, user_id, total_amount, order_date, status

586

FROM orders

587

WHERE order_date BETWEEN :startDate AND :endDate

588

AND status = :status

589

ORDER BY order_date DESC

590

""";

591

592

MapSqlParameterSource params = new MapSqlParameterSource()

593

.addValue("startDate", startDate)

594

.addValue("endDate", endDate)

595

.addValue("status", status.name());

596

597

return namedParameterJdbcTemplate.query(sql, params, new OrderRowMapper());

598

}

599

}

600

```

601

602

### Transaction Management

603

604

```java { .api }

605

// Configuration

606

@Configuration

607

@EnableTransactionManagement

608

public class DatabaseConfig {

609

610

@Bean

611

public DataSource dataSource() {

612

HikariDataSource dataSource = new HikariDataSource();

613

dataSource.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");

614

dataSource.setUsername("user");

615

dataSource.setPassword("password");

616

dataSource.setMaximumPoolSize(20);

617

return dataSource;

618

}

619

620

@Bean

621

public PlatformTransactionManager transactionManager(DataSource dataSource) {

622

return new DataSourceTransactionManager(dataSource);

623

}

624

625

@Bean

626

public JdbcTemplate jdbcTemplate(DataSource dataSource) {

627

return new JdbcTemplate(dataSource);

628

}

629

}

630

631

// Service with declarative transactions

632

@Service

633

public class OrderService {

634

635

private final OrderRepository orderRepository;

636

private final PaymentService paymentService;

637

private final EmailService emailService;

638

639

public OrderService(OrderRepository orderRepository, PaymentService paymentService, EmailService emailService) {

640

this.orderRepository = orderRepository;

641

this.paymentService = paymentService;

642

this.emailService = emailService;

643

}

644

645

@Transactional

646

public Order createOrder(Order order) {

647

// Save order

648

Order savedOrder = orderRepository.save(order);

649

650

// Process payment

651

Payment payment = paymentService.processPayment(savedOrder);

652

653

// Send confirmation email

654

emailService.sendOrderConfirmation(savedOrder);

655

656

return savedOrder;

657

}

658

659

@Transactional(readOnly = true)

660

public List<Order> getUserOrders(Long userId) {

661

return orderRepository.findByUserId(userId);

662

}

663

664

@Transactional(propagation = Propagation.REQUIRES_NEW)

665

public void cancelOrder(Long orderId) {

666

Order order = orderRepository.findById(orderId);

667

668

if (order.getStatus() == OrderStatus.CONFIRMED) {

669

paymentService.refundPayment(order.getPaymentId());

670

order.setStatus(OrderStatus.CANCELLED);

671

orderRepository.update(order);

672

673

emailService.sendCancellationNotification(order);

674

}

675

}

676

677

@Transactional(rollbackFor = {PaymentException.class, EmailException.class})

678

public void processOrderWithStrictRollback(Order order) {

679

orderRepository.save(order);

680

paymentService.processPayment(order); // Throws PaymentException

681

emailService.sendConfirmation(order); // Throws EmailException

682

}

683

684

// Custom transaction attributes

685

@Transactional(

686

isolation = Isolation.REPEATABLE_READ,

687

timeout = 30,

688

rollbackFor = Exception.class

689

)

690

public void complexOrderProcessing(Order order) {

691

// Complex business logic with specific transaction requirements

692

}

693

}

694

```

695

696

### Programmatic Transactions

697

698

```java { .api }

699

@Service

700

public class AccountService {

701

702

private final AccountRepository accountRepository;

703

private final TransactionTemplate transactionTemplate;

704

705

public AccountService(AccountRepository accountRepository, PlatformTransactionManager transactionManager) {

706

this.accountRepository = accountRepository;

707

this.transactionTemplate = new TransactionTemplate(transactionManager);

708

}

709

710

public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) {

711

transactionTemplate.execute(status -> {

712

try {

713

Account fromAccount = accountRepository.findById(fromAccountId);

714

Account toAccount = accountRepository.findById(toAccountId);

715

716

if (fromAccount.getBalance().compareTo(amount) < 0) {

717

throw new InsufficientFundsException("Not enough funds");

718

}

719

720

fromAccount.setBalance(fromAccount.getBalance().subtract(amount));

721

toAccount.setBalance(toAccount.getBalance().add(amount));

722

723

accountRepository.update(fromAccount);

724

accountRepository.update(toAccount);

725

726

return null; // TransactionCallbackWithoutResult

727

728

} catch (Exception e) {

729

status.setRollbackOnly();

730

throw e;

731

}

732

});

733

}

734

735

// With return value

736

public Account createAccountWithInitialDeposit(String accountNumber, BigDecimal initialDeposit) {

737

return transactionTemplate.execute(status -> {

738

Account account = new Account();

739

account.setAccountNumber(accountNumber);

740

account.setBalance(initialDeposit);

741

742

Long accountId = accountRepository.save(account);

743

account.setId(accountId);

744

745

// Log transaction

746

auditService.logAccountCreation(account);

747

748

return account;

749

});

750

}

751

752

// Custom transaction definition

753

public void performOperationWithCustomTransaction() {

754

DefaultTransactionDefinition txDef = new DefaultTransactionDefinition();

755

txDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

756

txDef.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);

757

txDef.setTimeout(30);

758

759

TransactionTemplate customTemplate = new TransactionTemplate(transactionManager, txDef);

760

761

customTemplate.execute(status -> {

762

// Custom transaction logic

763

return null;

764

});

765

}

766

}

767

```

768

769

### JPA Integration

770

771

```java { .api }

772

// JPA Configuration

773

@Configuration

774

@EnableJpaRepositories(basePackages = "com.example.repository")

775

@EnableTransactionManagement

776

public class JpaConfig {

777

778

@Bean

779

public DataSource dataSource() {

780

HikariDataSource dataSource = new HikariDataSource();

781

dataSource.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");

782

dataSource.setUsername("user");

783

dataSource.setPassword("password");

784

return dataSource;

785

}

786

787

@Bean

788

public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

789

LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();

790

em.setDataSource(dataSource());

791

em.setPackagesToScan("com.example.entity");

792

793

HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();

794

vendorAdapter.setGenerateDdl(true);

795

vendorAdapter.setShowSql(true);

796

em.setJpaVendorAdapter(vendorAdapter);

797

798

Properties properties = new Properties();

799

properties.setProperty("hibernate.hbm2ddl.auto", "update");

800

properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");

801

em.setJpaProperties(properties);

802

803

return em;

804

}

805

806

@Bean

807

public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {

808

JpaTransactionManager transactionManager = new JpaTransactionManager();

809

transactionManager.setEntityManagerFactory(emf);

810

return transactionManager;

811

}

812

}

813

814

// JPA Entity

815

@Entity

816

@Table(name = "users")

817

public class User {

818

819

@Id

820

@GeneratedValue(strategy = GenerationType.IDENTITY)

821

private Long id;

822

823

@Column(name = "username", nullable = false, unique = true)

824

private String username;

825

826

@Column(name = "email", nullable = false)

827

private String email;

828

829

@Column(name = "created_date")

830

private LocalDateTime createdDate;

831

832

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)

833

private List<Order> orders = new ArrayList<>();

834

835

// Constructors, getters, setters

836

}

837

838

// Repository using EntityManager directly

839

@Repository

840

public class UserJpaRepository {

841

842

@PersistenceContext

843

private EntityManager entityManager;

844

845

public User save(User user) {

846

if (user.getId() == null) {

847

entityManager.persist(user);

848

return user;

849

} else {

850

return entityManager.merge(user);

851

}

852

}

853

854

public User findById(Long id) {

855

return entityManager.find(User.class, id);

856

}

857

858

public List<User> findByUsername(String username) {

859

TypedQuery<User> query = entityManager.createQuery(

860

"SELECT u FROM User u WHERE u.username LIKE :username", User.class);

861

query.setParameter("username", "%" + username + "%");

862

return query.getResultList();

863

}

864

865

public void delete(User user) {

866

if (entityManager.contains(user)) {

867

entityManager.remove(user);

868

} else {

869

entityManager.remove(entityManager.merge(user));

870

}

871

}

872

873

// Native query example

874

@SuppressWarnings("unchecked")

875

public List<User> findUsersWithOrdersInDateRange(LocalDate startDate, LocalDate endDate) {

876

Query query = entityManager.createNativeQuery(

877

"SELECT DISTINCT u.* FROM users u " +

878

"JOIN orders o ON u.id = o.user_id " +

879

"WHERE o.order_date BETWEEN ?1 AND ?2", User.class);

880

881

query.setParameter(1, startDate);

882

query.setParameter(2, endDate);

883

884

return query.getResultList();

885

}

886

}

887

```

888

889

### R2DBC Reactive Data Access

890

891

```java { .api }

892

// R2DBC Configuration

893

@Configuration

894

@EnableR2dbcRepositories

895

public class R2dbcConfig extends AbstractR2dbcConfiguration {

896

897

@Override

898

@Bean

899

public ConnectionFactory connectionFactory() {

900

return ConnectionFactories.get(ConnectionFactoryOptions.builder()

901

.option(DRIVER, "postgresql")

902

.option(HOST, "localhost")

903

.option(PORT, 5432)

904

.option(USER, "user")

905

.option(PASSWORD, "password")

906

.option(DATABASE, "mydb")

907

.build());

908

}

909

910

@Bean

911

public R2dbcEntityTemplate r2dbcEntityTemplate(DatabaseClient databaseClient) {

912

return new R2dbcEntityTemplate(databaseClient);

913

}

914

}

915

916

// Reactive Repository

917

@Repository

918

public class ReactiveUserRepository {

919

920

private final DatabaseClient databaseClient;

921

922

public ReactiveUserRepository(DatabaseClient databaseClient) {

923

this.databaseClient = databaseClient;

924

}

925

926

public Mono<User> findById(Long id) {

927

return databaseClient

928

.sql("SELECT id, username, email, created_date FROM users WHERE id = :id")

929

.bind("id", id)

930

.map((row, metadata) -> {

931

User user = new User();

932

user.setId(row.get("id", Long.class));

933

user.setUsername(row.get("username", String.class));

934

user.setEmail(row.get("email", String.class));

935

user.setCreatedDate(row.get("created_date", LocalDateTime.class));

936

return user;

937

})

938

.one();

939

}

940

941

public Flux<User> findAll() {

942

return databaseClient

943

.sql("SELECT id, username, email, created_date FROM users ORDER BY created_date DESC")

944

.map(this::mapUser)

945

.all();

946

}

947

948

public Mono<User> save(User user) {

949

if (user.getId() == null) {

950

return databaseClient

951

.sql("INSERT INTO users (username, email, created_date) VALUES (:username, :email, :createdDate)")

952

.bind("username", user.getUsername())

953

.bind("email", user.getEmail())

954

.bind("createdDate", user.getCreatedDate())

955

.fetch()

956

.rowsUpdated()

957

.map(count -> user);

958

} else {

959

return databaseClient

960

.sql("UPDATE users SET username = :username, email = :email WHERE id = :id")

961

.bind("id", user.getId())

962

.bind("username", user.getUsername())

963

.bind("email", user.getEmail())

964

.fetch()

965

.rowsUpdated()

966

.map(count -> user);

967

}

968

}

969

970

public Mono<Void> deleteById(Long id) {

971

return databaseClient

972

.sql("DELETE FROM users WHERE id = :id")

973

.bind("id", id)

974

.then();

975

}

976

977

private User mapUser(Row row, RowMetadata metadata) {

978

User user = new User();

979

user.setId(row.get("id", Long.class));

980

user.setUsername(row.get("username", String.class));

981

user.setEmail(row.get("email", String.class));

982

user.setCreatedDate(row.get("created_date", LocalDateTime.class));

983

return user;

984

}

985

}

986

987

// Reactive Service with Transactions

988

@Service

989

public class ReactiveUserService {

990

991

private final ReactiveUserRepository userRepository;

992

private final R2dbcTransactionManager transactionManager;

993

994

public ReactiveUserService(ReactiveUserRepository userRepository,

995

R2dbcTransactionManager transactionManager) {

996

this.userRepository = userRepository;

997

this.transactionManager = transactionManager;

998

}

999

1000

@Transactional

1001

public Mono<User> createUser(User user) {

1002

return userRepository.save(user)

1003

.doOnNext(savedUser -> log.info("User created: {}", savedUser.getId()));

1004

}

1005

1006

public Mono<User> updateUser(Long id, User updatedUser) {

1007

return userRepository.findById(id)

1008

.switchIfEmpty(Mono.error(new UserNotFoundException("User not found: " + id)))

1009

.map(existingUser -> {

1010

existingUser.setUsername(updatedUser.getUsername());

1011

existingUser.setEmail(updatedUser.getEmail());

1012

return existingUser;

1013

})

1014

.flatMap(userRepository::save);

1015

}

1016

1017

// Manual transaction control

1018

public Mono<Void> performTransactionalOperation(User user1, User user2) {

1019

TransactionalOperator operator = TransactionalOperator.create(transactionManager);

1020

1021

return userRepository.save(user1)

1022

.then(userRepository.save(user2))

1023

.then()

1024

.as(operator::transactional);

1025

}

1026

}

1027

```

1028

1029

## Exception Handling

1030

1031

### DAO Exception Hierarchy

1032

1033

```java { .api }

1034

// Common exception handling patterns

1035

@Service

1036

public class UserService {

1037

1038

private final UserRepository userRepository;

1039

1040

public UserService(UserRepository userRepository) {

1041

this.userRepository = userRepository;

1042

}

1043

1044

public User getUserById(Long id) {

1045

try {

1046

return userRepository.findById(id);

1047

} catch (EmptyResultDataAccessException e) {

1048

throw new UserNotFoundException("User not found with id: " + id);

1049

} catch (DataIntegrityViolationException e) {

1050

throw new UserServiceException("Data integrity violation", e);

1051

} catch (DataAccessException e) {

1052

throw new UserServiceException("Database access error", e);

1053

}

1054

}

1055

1056

public User createUser(User user) {

1057

try {

1058

return userRepository.save(user);

1059

} catch (DuplicateKeyException e) {

1060

throw new UserAlreadyExistsException("User already exists: " + user.getUsername());

1061

} catch (DataAccessException e) {

1062

throw new UserServiceException("Failed to create user", e);

1063

}

1064

}

1065

}

1066

```

1067

1068

Spring's data access support provides a comprehensive foundation for working with databases, offering both traditional JDBC operations and modern reactive programming models, all integrated with robust transaction management capabilities.