說說在 Spring 中,如何基於註解來配置事務

Spring 提供了基於註解的事務配置,即對須要事務加強的 Bean 接口 、 實現類或者方法進行標註@Transactional,而後在容器中配置基於註解的事務加強驅動,便可使用基於註解的聲明式事務 。spring

1 配置事務示例

咱們使用 @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 容器爲這個類的全部方法,都織入了事務管理功能。代理

2 @Transactional 屬性

@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[]。

3 標註位置

@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);
    }
複製代碼

控制檯輸出結果:

image.png

從輸出結果中咱們能夠看出,在調用該方法時,事務加入了只讀屬性。

相關文章
相關標籤/搜索