javascript
Spring JDBC-使用注解配置声明式事务
- 系列
- 概述
- 使用Transactional注解
- txannotation-driven其他屬性
- 關于Transaction的屬性
- 在何處標注Transactional注解
- 在方法處使用注解
- 使用不同的事務管理器
- 示例
系列
Spring對事務管理的支持概述以及 編程式的事務管理
Spring JDBC-使用XML配置聲明式事務
Spring JDBC-使用注解配置聲明式事務
概述
除了基于XML的事務配置,Spring還提供了基于注解的事務配置,即通過@Transactional對需要事務增強的Bean接口、實現類或者方法進行標注:在容器中配置基于注解的事務增強驅動,即可以啟用基于注解的聲明式事務。
使用@Transactional注解
我們來對Spring JDBC-使用XML配置聲明式事務中的例子使用@Transactional對基于aop/tx命名空間的事務配置進行改造,我們來感受下二者在使用方式上的差異。
@Service @Transactional // 對業務類進行事務增強的標注 public class TeacherService {private TeacherDao teacherDao;public void addTeacher(Teacher teacher) {teacherDao.addTeacher(teacher);}public void updateTeacher(Teacher teacher) {teacherDao.updateTeacher(teacher);}public void getTeacherById(int teacherId) {teacherDao.getTeacher(teacherId);}public void addStudentForTeacher(Teacher teacher, Student student) {teacher.setStudent(student);teacherDao.addStudent(student);} }因為注解本身具有一組普適性的默認事務屬性,所以往往只需要在需要事務管理的業務類中添加一個@Transactional注解,就完成了業務類事務屬性的配置.
當然,注解只是提供元數據,它本身并不能完成事務切面織入的功能,因此,還需要在Spring中配置文件中通過一行小小的配置“通知”Spring容器對標注@Transactional注解的Bean進行加工處理,如下
<!--基于數據源的事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"p:dataSource-ref="dataSource"/><!--對標注了@Transactional注解Bean進行加工處理,以織入事務管理切面--> <tx:annotation-driven transaction-manager="transactionManager"/>在默認情況下,<tx:annotation-driven>會自動使用名為transactionManager事務管理器, 所以,如果我們的事務管理器的id為transactionManager,如上所示,則可以進一步簡化為
<tx:annotation-driven/><tx:annotation-driven>其他屬性
proxy-target-class: 如果為true ,Spring將通過創建子類來代理業務類,若果為false,則使用基于接口的代理。 如果使用子類代理,,需要在類路徑中添加CGlib.jar類庫
order:如果業務類除了事務切面外,還要織入其他的切面,則通過該屬性可以控制事務切面在目標連接點的織入順序。
- mode: 模式 ,默認為proxy ,可以選擇aspectj
關于@Transaction的屬性
基于@Transactional注解的配置和基于XML的配置方式一樣,它擁有一組普適性很強的默認事務屬性,往往可以直接使用這些默認的屬性
- 事務傳播行為: PROPAGATION_REQUIRED
- 事務隔離級別:ISOLATION_DEFAULT
- 讀寫事務屬性:讀/寫事務
- 超時時間:依賴底層的事務屬性的默認值
- 回滾設置:任何運行期異常引發回滾,任何檢查型異常不會已發回滾。
因為這些默認設置在大多數情況下是都是適用的,所以一般不需要手工設置事務注解的屬性(如下面的表格),當然Spring允許通過手工設定屬性值覆蓋默認值。
| propagation | 事務傳播行為,通過org.springframework.transaction.annotation.Propagation枚舉類,提供合法值,比如@Transactional(propagation=Propagation.REQUIRES_NEW) |
| isolation | 事務的隔離級別,通過org.springframework.transaction.annotation.Isolation枚舉類,提供合法值,比如@Transactional(isolation=Isolation.READ_COMMITED) |
| readOnly | 事務讀寫屬性,布爾型,比如 @Transactional(readOnly=true) |
| timeout | 超時時間,int型,單位為秒,例如 @Transactional(timeout=10) |
| rollbackFor | 一組異常類,遇到時進行回滾,類型為 Class[],比如@Transactional(rollbackFor={SQLException,class})。 多個異常之間使用逗號分隔 |
| rollbackForClassName | 組異常類,遇到時進行回滾,類型為Strin[],默認值為{},比如@Transactional(rollbackForClassName={“Exception”}) |
| noRollbackFor | 一組異常類,遇到時不回滾,類型為 Class[],默認值為{} |
| noRolbackForClassName | 一組異常類,遇到時不回滾,類型為 String[],默認值為{} |
在何處標注@Transactional注解
@Transactional注解可以被應用于接口定義和接口方法、類定義和類的Public方法上。
但是Spring建議在業務的實現類上使用@Transactional注解,當然也可以在業務接口上使用@Transactional注解,但是這樣會遺留下一些容易被忽視的隱患, 因為注解不能被繼承,所以在業務接口中標注的@Transactional注解不會被業務實現類繼承。 如果通過以下配置啟用了代理類
<tx:annotation-driven transaction-manager="transactionManager" proxy-target="true"/>那么業務類不會添加事務增強,照樣工作在非事務環境下。 舉個例子,如果使用子類代理,假設用戶為 XXX接口標注了@Transaction注解,那么其實現類XXXImpl依舊不會啟用事務機制。
因此,Spring建議在具體業務類上使用@Transactional注解,這樣不管tx:annotation-driven將proxy-target設置為true還是false,業務類都會啟用事務機制
@Transactional 注解應該只被應用到 public 方法上,這是由 Spring AOP 的本質決定的。如果我們在 protected、private 或者默認可見性的方法上使用 @Transactional 注解,這將被忽略,也不會拋出任何異常。
在方法處使用注解
方法處的注解會覆蓋類定義的注解,如果有些方法需要使用特殊的事務屬性,則可以在類注解的基礎上提供方法注解,比如
@Repository @Transactional // (1)類級注解,適用于類中所有的public方法 public class TeacherDaoImpl extends BaseDao implements TeacherDao {private Logger logger = Logger.getLogger(TeacherDaoImpl.class);private static final String addTeacherSQL = "insert into teacher(id,name,age,sex) values(teacher_id_seq.nextval,?,?,?)";private static final String queryTeacherByIdSQL = "select name ,age ,sex from teacher where id = ?";@Transactional(readOnly=true) // (2)提供額外的注解信息,它將覆蓋(1)處的類級注解@Overridepublic Teacher getTeacher(int teacherId) {logger.info("TeacherID:" + teacherId);final Teacher teacher = new Teacher();jdbcTemplate.query(queryTeacherByIdSQL, new Object[] { teacherId },new RowCallbackHandler() {@Overridepublic void processRow(ResultSet rs) throws SQLException {teacher.setAge(rs.getInt("age"));teacher.setName(rs.getString("name"));teacher.setSex(rs.getString("sex"));}});return teacher;}}(2)處的方法注解提供了readOnly屬性,它將覆蓋類級注解中默認的readOnly=false設置
使用不同的事務管理器
一般情況下,一個應用僅需要使用一個事務管理器, 如果希望在不同的地方使用不同的事務管理器,則可以通過如下方式實現
配置文件:
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"><!-- 掃描類包,將標注Spring注解的類自動轉化Bean,同時完成Bean的注入 --><context:component-scan base-package="com.xgj.dao.transaction.multiTxManager" /><!-- 使用context命名空間,引入數據庫的properties文件 --><context:property-placeholder location="classpath:spring/jdbc.properties" /><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close" p:driverClassName="${jdbc.driverClassName}"p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}" /><!-- 配置Jdbc模板 --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"p:dataSource-ref="dataSource" /><!--基于數據源的事務管理器,通過屬性引用數據源 可以使用dataSource-ref引用不同的數據源,我們這里只展示同一個--><bean id="forumTxManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"p:dataSource-ref="dataSource"><!-- 為事務管理器指定一個名字 --><qualifier value="forum"/></bean><bean id="topicTxManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"p:dataSource-ref="dataSource"><!-- 為事務管理器指定一個名字 --><qualifier value="topic"/></bean><!-- 通知Spring處理注解Bean --><tx:annotation-driven/></beans>使用
package com.xgj.dao.transaction.multiTxManager;import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;@Service public class MulitTxServiceWitSpecificName {// (1)使用名為forum的事務管理器@Transactional("forum")public void addForum() {}// 使用名為topic的事務管理器@Transactional("topic")public void addTopic() {} }在(1)處我們為事務管理器指定了一個數據源,每個事務管理器都可以綁定一個獨立的數據源。
在spring配置文件中
<!-- 為事務管理器指定一個名字 --><qualifier value="forum"/>指定了一個可以被@Transactional注解引用的事務管理器的標識。
我們發現在代碼中使用 @Transactional(“forum”) 來引用特定的事務管理器,如果很多地方都需要使用,則顯得很麻煩,我們可以通過自定義注解進行標識
package com.xgj.dao.transaction.multiTxManager;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;import org.springframework.transaction.annotation.Transactional;@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) // 綁定到forum的事務管理器中 @Transactional("forum") public @interface ForumTransactional {}引用的話,調整下代碼,如下
package com.xgj.dao.transaction.multiTxManager;import org.springframework.stereotype.Service;@Service public class MulitTxServiceWithSelfDefineAnno {// 使用名為forum的事務管理器@ForumTransactionalpublic void addForum() {}// 使用名為topic的事務管理器@TopicTransactionalpublic void addTopic() {} }示例
代碼已托管到Github—> https://github.com/yangshangwei/SpringMaster
總結
以上是生活随笔為你收集整理的Spring JDBC-使用注解配置声明式事务的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring JDBC-使用XML配置声
- 下一篇: gradle idea java ssm