package com.example.demo.service;
import com.example.demo.model.Order;
import com.example.demo.model.OrderItem;
import com.example.demo.model.User;
import com.example.demo.repository.OrderRepository;
import com.example.demo.repository.UserRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class TransactionalService {
private final OrderRepository orderRepository;
private final UserRepository userRepository;
private final EmailService emailService;
public TransactionalService(OrderRepository orderRepository,
UserRepository userRepository,
EmailService emailService) {
this.orderRepository = orderRepository;
this.userRepository = userRepository;
this.emailService = emailService;
}
// Basic transaction - rolls back on RuntimeException
@Transactional
public Order createOrder(Order order) {
Order saved = orderRepository.save(order);
updateUserLastOrderDate(order.getUserId());
return saved;
}
// Custom rollback rules
@Transactional(rollbackFor = Exception.class,
noRollbackFor = IllegalArgumentException.class)
public void processOrder(Long orderId) throws Exception {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new Exception("Order not found"));
// Process order
}
// Read-only transaction for optimization
@Transactional(readOnly = true)
public List<Order> findOrdersByUser(Long userId) {
return orderRepository.findByUserId(userId);
}
// New transaction - always starts fresh transaction
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logOrderCreation(Long orderId) {
// This runs in separate transaction, commits independently
System.out.println("Order created: " + orderId);
}
// Nested transaction
@Transactional(propagation = Propagation.NESTED)
public void updateOrderItems(Long orderId, List<OrderItem> items) {
// Can rollback to savepoint without affecting outer transaction
Order order = orderRepository.findById(orderId).orElseThrow();
order.setItems(items);
orderRepository.save(order);
}
// Isolation level to prevent phantom reads
@Transactional(isolation = Isolation.SERIALIZABLE)
public long countPendingOrders() {
return orderRepository.countByStatus("PENDING");
}
// Transaction with timeout
@Transactional(timeout = 30)
public void processBatchOrders(List<Order> orders) {
orders.forEach(orderRepository::save);
}
// Complex transaction orchestration
@Transactional
public Order createOrderWithNotification(Order order) {
Order saved = orderRepository.save(order);
// This runs in new transaction, commits even if outer fails
logOrderCreation(saved.getId());
// This can fail without rolling back log
emailService.sendOrderConfirmation(saved);
return saved;
}
private void updateUserLastOrderDate(Long userId) {
User user = userRepository.findById(userId).orElseThrow();
user.setLastOrderDate(java.time.LocalDateTime.now());
userRepository.save(user);
}
}
package com.example.demo.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
@Service
public class ProgrammaticTransactionService {
private final TransactionTemplate transactionTemplate;
public ProgrammaticTransactionService(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void executeWithTransaction() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
// Business logic
performDatabaseOperations();
} catch (Exception e) {
status.setRollbackOnly();
}
}
});
}
public String executeWithResult() {
return transactionTemplate.execute(status -> {
try {
return performOperationWithResult();
} catch (Exception e) {
status.setRollbackOnly();
return null;
}
});
}
private void performDatabaseOperations() {
// Database operations
}
private String performOperationWithResult() {
// Operation that returns result
return "Success";
}
}
Spring's @Transactional manages database transactions declaratively. Transaction propagation controls behavior when methods call other transactional methods—REQUIRED, REQUIRESNEW, NESTED. Isolation levels prevent concurrent access issues—READUNCOMMITTED, READCOMMITTED, REPEATABLEREAD, SERIALIZABLE. Rollback rules specify which exceptions trigger rollback—default is unchecked exceptions. Timeout prevents long-running transactions. Read-only transactions optimize performance for queries. Programmatic transactions with TransactionTemplate offer finer control. Distributed transactions use JTA for multiple resources. Proper transaction boundaries ensure data consistency. Over-broad transactions harm performance; too narrow ones risk partial updates. Understanding ACID properties guides correct transaction usage. Transaction management is critical for data integrity in production systems.