[Spring transaction], the reason why the service implements non-transactional methods in the class of direct calling of its own transaction method causes the transaction to be invalid, transaction is not
First, prepare the service interface.
public interface AccountService { public void createAccount(Account account, int throwExpFlag) throws Exception; public void createAccountShell(Account account, int i) throws Exception;}
public interface RoleService { public void createAccountShell(Account account, int i) throws Exception;}
Related impl
@Servicepublic class AccountServiceImpl implements AccountService { @Resource private AccountDAO accountDAO; @Override @Transactional public void createAccount(Account account, int throwExpFlag) throws Exception { accountDAO.save(account); RoleServiceImpl.throwExp(throwExpFlag); } @Override public void createAccountShell(Account account, int i) throws Exception { this.createAccount(account, i); }}
@Servicepublic class RoleServiceImpl implements RoleService { @Autowired AccountService accountService; public static void throwExp(int throwExpFlag) throws Exception { if (throwExpFlag == 0) { throw new RuntimeException("<< rollback transaction >>"); } else if (throwExpFlag != 1) { throw new Exception("<< do not rollback transaction >>"); } } @Override public void createAccountShell(Account account, int i) throws Exception { accountService.createAccount(account, i); }}
Test class
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration({"classpath:spring/spring-dao.xml", "classpath:spring/spring-service.xml"})public class ServiceTransactionTest extends TestCase { public static Account account; static { account = new Account(); account.setId(779); account.setUsername("779 USER"); account.setPassword("0123456"); } @Autowired AccountService accountService; @Autowired RoleService roleService; @Test public void test_1() throws Exception { this.accountService.createAccount(account, 0); } @Test public void test_2() throws Exception { this.accountService.createAccountShell(account, 0); } @Test public void test_3() throws Exception { roleService.createAccountShell(account, 0); }}
(1) When testing the test_1 method of the test class, spring normally takes over the transaction because the AccountServiceImpl. createAccount method shows that the transaction is configured (@ Transactional.
(2) When testing the test_2 method of the test class, the AccountServiceImpl. createAccountShell method does not display the configuration transaction, but it calls the AccountServiceImpl. createAccount method (configured transaction ). When RuntimeException is thrown, there is no rollback, indicating that spring has not taken over the transaction. (Cause: AccountServiceImpl. spring knows when createAccountShell is displayed for calling, but because AccountServiceImpl. createAccountShell does not display the configuration transaction, and spring does not manage the transaction. In AccountServiceImpl. although the createAccount Method for transaction configuration is called in createAccountShell, spring does not know or cannot determine the transaction context, so the result is not rollback due to the thrown runtime exception ).
(3) test test_3. Although RoleServiceImpl. createAccountShell does not configure transactions, spring takes over the transactions and rollback when RuntimeException is thrown. (Cause of speculation: Although RoleServiceImpl. createAccountShell does not configure transactions, but it internally calls the method of another service instance, that is, AccountService. spring knows this when creating an account, and because AccountServiceImpl. createAccount indicates that the transaction is configured, so spring takes over the transaction ).
(4) If a transaction is configured in AccountServiceImpl. createAccountShell, spring can take over the transaction when executing test_2.