Spring 提供了基於註解的事務配置,即對須要事務加強的 Bean 接口 、 實現類或者方法進行標註@Transactional,而後在容器中配置基於註解的事務加強驅動,便可使用基於註解的聲明式事務 。spring
咱們使用 @Transactional 來爲業務類配置事務:bash
@Service
@Transactional
public class UserService {
@Autowired
private UserDao userDao;
/**
* 新增
*
* @param user
*/
public int addUser(final User user) {
return userDao.save(user);
}
/**
* 依據 Id,獲取帳號
*
* @param userId
* @return
*/
public User getUser(Long userId) {
return userDao.get(userId);
}
/**
* 更新帳號所對應的密碼
* @param userId
* @param pwd
*/
public int update(Long userId, String pwd) {
return userDao.update(userId, pwd);
}
}
複製代碼
接着在 Spring 配置文件中, 告知 Spring 容器對標註了 @Transactional 註解的 Bean,織入事務管理切面:單元測試
<!-- 掃描帶 @Transactional 註解的 Bean,織入事務管理切面-->
<tx:annotation-driven transaction-manager="transactionManager"/>
複製代碼
在默認狀況下, <tx:annotation-driven>
會自動使用名爲 transactionManager 的事務管理器, 因此,若是咱們的事務管理器就叫作 transactionManager ,那麼就能夠進一步簡化爲測試
<tx:annotation-driven/>
複製代碼
<tx:annotation-driven>
擁有如下屬性:ui
屬性 | 默認值 | 說明 |
---|---|---|
transaction-manager | transactionManager | 事務管理器 Bean ID |
proxy-target-class | false | true 表示將經過建立子類來代理業務類(須要在類路徑中添加 CGlib.jar 類庫); false 表示使用基於接口來代理 。 |
order | - | 若是業務類除了須要事務切面以外,還須要織入其餘切面,那麼能夠經過該屬性,來控制事務切面在目標鏈接點中的織入順序。 |
單元測試:spa
public class UserServiceTest {
ApplicationContext context;
@BeforeMethod
public void setUp() throws Exception {
context = new ClassPathXmlApplicationContext("spring_anno.xml");
}
@Test
public void testAddUser() throws Exception {
UserService userService = (UserService) context.getBean("userService");
final User user = new User("deniro");
userService.addUser(user);
}
}
複製代碼
運行日誌:3d
從日誌中能夠看出,Spring 容器爲這個類的全部方法,都織入了事務管理功能。代理
@Transactional 擁有如下這些屬性:日誌
屬性 | 默認值 | 說明 |
---|---|---|
propagation | PROPAGATION_REQUIRED | 事務傳播行爲。可經過org.springframework.transaction.annotation.Propagation 枚舉類,來提供合法值,例:@Transactional(propagation=Propagation.SUPPORTS) |
isolation | ISOLATION_DEFAULT | 事務隔離級別。可經過 org.springframework.transaction.annotation.Isolation 枚舉類,來提供合法值,例:@Transactional(isolation=Isolation.READ_UNCOMMITTED) |
readOnly | false | 是否可讀寫事務。例:@Transactional(readOnly=true) |
timeout | 使用底層事務系統的默認值 | 超時時間,單位爲秒。例: @Transactional(timeout=3) |
rollbackFor | 回滾全部運行期異常。 | 須要回滾的一組異常類,類型爲 Class[], 多個異常類使用逗號分隔。例:@Transactional(rollbackFor={SQLException,class}) 。 |
rollbackForClassName | {} | 須要回滾的一組異常類,類型爲 String[]。例:@Transactional(rollbackForClassName={「xxxException」}) |
noRollbackFor | {} | 不須要回滾的一組異常類,類型爲 Class<? extends Throwable>[] 。 |
noRolbackForClassName | {} | 不須要回滾的一組異常類,類型爲 String[]。 |
@Transactional 註解能夠被標註於接口定義、接口方法 、 類定義和類的 Public 方法上 。code
但若是 @Transactional 註解被標註在業務接口上,那麼若是啓用了子類代理:
<tx:annotation-driven proxy-target="true"/>
複製代碼
那麼被代理的業務類並不會織入事務加強,仍然工做在非事務環境下。這顯然不是咱們想看到的。
建議在具體業務類上使用 @Transactional 註解,這樣無論是否開啓子類代理模式,業務類都會織入事務加強。
也能夠在直接在方法上定義註解。
方法上定義的註解會覆蓋類定義的註解,好比有些方法須要使用到特殊的事務屬性,那麼就能夠直接在方法上定義註解。
在如下示例中,咱們在 getUser() 方法上設置了只讀事務屬性:
@Service
@Transactional
public class UserService {
@Autowired
private UserDao userDao;
/**
* 依據 Id,獲取帳號
*
* @param userId
* @return
*/
@Transactional(readOnly = true)
public User getUser(Long userId) {
return userDao.get(userId);
}
...
}
複製代碼
單元測試:
@Test
public void testGetUser() throws Exception {
UserService userService = (UserService) context.getBean("userService");
User user = userService.getUser(1l);
logger.info("user={}", user);
}
複製代碼
控制檯輸出結果:
從輸出結果中咱們能夠看出,在調用該方法時,事務加入了只讀屬性。