1

I have a service method that performs the following operations in sequence:

  1. Calls a read method annotated with a custom read-only transaction annotation (equivalent to @Transactional(readOnly = true))

  2. Then calls a write method annotated with a custom annotation that should create a new transaction (equivalent to @Transactional(propagation = Propagation.REQUIRES_NEW))

I'm getting this error:

[ERROR: cannot execute UPDATE in a read-only transaction]

Exemple: thanks @Robert

// Service class with transactional methods
@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    // Read-only transaction
    @Transactional(readOnly = true)
    public User findUserById(Long id) {
        return userRepository.findById(id).orElse(null);
    }
    
    // Write transaction with REQUIRES_NEW
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public User updateUser(User user) {
        return userRepository.save(user);
    }
}

// Orchestrator service
@Service
public class UserOrchestrationService {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private NotificationService notificationService;
    
    // No transaction annotation on this method
    public void processUserUpdate(Long userId, String newEmail) {
        // Step 1: Read user with READ-ONLY transaction
        User existingUser = userService.findUserById(userId);
        
        if (existingUser == null) {
            throw new RuntimeException("User not found");
        }
        
        // Step 2: Modify user data
        existingUser.setEmail(newEmail);
        existingUser.setLastModified(new Date());
        
        // Step 3: Save with REQUIRES_NEW transaction - FAILS HERE!
        User updatedUser = userService.updateUser(existingUser);
        
        // Step 4: Send notification
        notificationService.sendUpdateNotification(updatedUser);
    }
}
1
  • 2
    It sounds like one or both of your custom annotations are not producing the effects you expect. Have you tested these independently? Can you reproduce the behavior with standard transaction-management annotations? Commented Nov 12 at 20:58

1 Answer 1

0

Spring’s transaction management uses proxy-based AOP. Because both findUserById() and updateUser() live in the same UserService bean, the call to updateUser() is a self-invocation and does not go through the proxy. Therefore the REQUIRES_NEW annotation is ignored, and the method executes inside the read-only transaction started earlier.

This causes:

cannot execute UPDATE in a read-only transaction

Split the read-only and write operations into separate Spring-managed beans (or call through the proxy).

like :

@Service

public class UserReaderService { @Transactional(readOnly = true) ... }

@Service

public class UserWriterService { @Transactional(REQUIRES_NEW) ... }

Then call them from the orchestrator.
This ensures Spring AOP proxies apply correctly, and the write operation runs inside a new separate transaction.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.