Real World Example

Placing an Order with Plain JDBC

A Swiggy- or Flipkart-style checkout must reduce stock and create the order in one transaction. With plain JDBC this is how it looks — note how much code is plumbing:

public long placeOrder(long userId, long productId, int qty) throws SQLException {
    try (Connection conn = dataSource.getConnection()) {
        conn.setAutoCommit(false);
        try {
            BigDecimal price;
            try (PreparedStatement ps = conn.prepareStatement(
                    "select price, stock from products where id = ? for update")) {
                ps.setLong(1, productId);
                try (ResultSet rs = ps.executeQuery()) {
                    if (!rs.next()) throw new SQLException("Product not found: " + productId);
                    if (rs.getInt("stock") < qty) throw new SQLException("Out of stock");
                    price = rs.getBigDecimal("price");
                }
            }
            try (PreparedStatement ps = conn.prepareStatement(
                    "update products set stock = stock - ? where id = ?")) {
                ps.setInt(1, qty);
                ps.setLong(2, productId);
                ps.executeUpdate();
            }
            long orderId;
            try (PreparedStatement ps = conn.prepareStatement(
                    "insert into orders (user_id, status, total_amount) values (?, 'PLACED', ?)",
                    Statement.RETURN_GENERATED_KEYS)) {
                ps.setLong(1, userId);
                ps.setBigDecimal(2, price.multiply(BigDecimal.valueOf(qty)));
                ps.executeUpdate();
                try (ResultSet keys = ps.getGeneratedKeys()) { keys.next(); orderId = keys.getLong(1); }
            }
            conn.commit();
            return orderId;
        } catch (SQLException e) {
            conn.rollback();
            throw e;
        }
    }
}

The Same Logic with Spring Data JPA

@Transactional
public Long placeOrder(Long userId, Long productId, int qty) {
    Product product = productRepository.findById(productId)
        .orElseThrow(() -> new ProductNotFoundException(productId));
    if (product.getStock() < qty) throw new OutOfStockException(productId);

    product.setStock(product.getStock() - qty);           // UPDATE generated at commit

    Order order = new Order(userId, OrderStatus.PLACED,
                            product.getPrice().multiply(BigDecimal.valueOf(qty)));
    return orderRepository.save(order).getId();          // INSERT generated
}

About 40 lines of plumbing became 10 lines of business logic. The rest of this course explains how that works.