#1
This guide shows how to use transactions and rollbacks in Spring Boot with an Apache Derby database to keep data consistent.

1. Add Dependencies

In pom.xml:
<dependency>
  <groupId>org.apache.derby</groupId>
  <artifactId>derby</artifactId>
  <scope>runtime</scope>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

2. Configure Derby

In application.properties:
spring.datasource.url=jdbc:derby:memory:txDB;create=true
spring.datasource.driver-class-name=org.apache.derby.jdbc.EmbeddedDriver
spring.jpa.hibernate.ddl-auto=create

3. Create Entity

import jakarta.persistence.*;

@Entity
public class Account {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String owner;
    private double balance;

    // getters and setters
}

4. Repository

import org.springframework.data.jpa.repository.JpaRepository;

public interface AccountRepository extends JpaRepository<Account, Long> {
}

5. Transactional Service

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class AccountService {
    private final AccountRepository repo;

    public AccountService(AccountRepository repo) {
        this.repo = repo;
    }

    @Transactional
    public void transfer(Long fromId, Long toId, double amount) {
        Account from = repo.findById(fromId).orElseThrow();
        Account to = repo.findById(toId).orElseThrow();

        from.setBalance(from.getBalance() - amount);

        // simulate error
        if (amount > 500) {
            throw new RuntimeException("Transfer limit exceeded");
        }

        to.setBalance(to.getBalance() + amount);

        repo.save(from);
        repo.save(to);
    }
}
Here, if the amount is greater than 500, an exception is thrown and the transaction is rolled back.

6. REST Controller

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/accounts")
public class AccountController {
    private final AccountService service;

    public AccountController(AccountService service) {
        this.service = service;
    }

    @PostMapping("/transfer")
    public String transfer(@RequestParam Long from, @RequestParam Long to, @RequestParam double amount) {
        try {
            service.transfer(from, to, amount);
            return "Transfer successful!";
        } catch (Exception e) {
            return "Transfer failed: " + e.getMessage();
        }
    }
}

7. Test

  1. Create accounts in Derby through JPA.
  2. Call the API:
    curl -X POST "http://localhost:8080/accounts/transfer?from=1&to=2&amount=200"
    Works fine.
    curl -X POST "http://localhost:8080/accounts/transfer?from=1&to=2&amount=600"
Rolls back automatically.

image quote pre code