JavaEE學習日誌持續更新----> 必看!JavaEE學習路線(文章總彙)
JavaEE學習日誌(九十二)
Spring
spring事務控制的api
- org.springframework.orm.hibernate5.
HibernateTransactionManager
: 在hibernate環境下使用 - org.springframework.jdbc.datasource.
DataSourceTransactionManager
: 在jdbcTemplate,mybatis(ibatis)環境下使用
事務的特性
- 原子性:不可分割
- 隔離性:事務之間的隔離級別
- 一致性:要麼全部完成,要麼全部不完成
- 持久性:一旦提交,持久化到數據庫中
事務的隔離級別
- 讀未提交:read uncommited
產生的問題:髒讀、幻讀、不可重複讀 - 讀已提交:read commited
產生的問題:幻讀,不可重複讀
解決的問題:髒讀 - 重複讀:repeatable read
產生的問題:幻讀
解決的問題:髒讀,不可重複讀 - 串行化(序列化):serializable
解決的問題:所有問題,隔離級別最高,效率最低
詳細看jdbc中的事務部分
數據庫支持的隔離級別
mysql:全支持,默認是repeatable read
oracle:read commited、serializable、read only(只讀) 默認是read commited
事務的傳播
-
REQUIRED
:必要的,如果沒有事務,則新建事務;如果有事務,則加入事務中。spring中指定爲默認值
增刪改 -
SUPPORTS
:支持的,如果沒有事務,非事務執行;如果有事務,加入事務
查詢
瞭解其他事務傳播
MANDATORY:可以使用當前事務,如果沒有事務,則拋出異常
REQUERS_NEW:新建一個事務,如果存在事務,則掛起事務
NOT_SUPPORTED:必須非事務執行,如果有事務則掛起
NEVER:非事務執行,如果存在事務,拋出異常
NESTED:有事務,嵌套執行;沒有事務,執行REQUIRED
是否爲只讀的事務
- 如果是查詢,則爲只讀的事務,readOnly=true
- 如果是增刪改,則爲非只讀的事務,readOnly=false
spring聲明式xml事務管理(推薦)
引入spring的事務管理jar包
<!--spring的事務管理jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
一、創建事務管理器對象
<!--創建事務管理器對象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入數據源屬性:事務存在連接中,連接存在連接池中-->
<property name="dataSource" ref="springDataSource"></property>
</bean>
二、事務的增強:過濾方法是否需要攔截
有四個屬性
isolation
:隔離級別,一般選擇默認propagation
:傳播的行爲read-only
:是否爲只讀的事務,增刪改選擇非只讀;查詢選擇只讀timeout
:超時時間,單位爲s,-1時爲永不超時
注意*
:通配符配置,如find*
,只要以find開頭即可
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--方法的過濾-->
<tx:attributes>
<!--指定需要攔截的方法
isolation:隔離級別,一般選擇默認
propagation:傳播的行爲
read-only:是否爲只讀的事務,增刪改選擇非只讀;查詢選擇只讀
timeout:超時時間,單位爲s,-1時爲永不超時
*:通配符配置,如find*,只要以find開頭即可
-->
<!--增刪改-->
<tx:method name="insert*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
<tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
<tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
<tx:method name="del*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
<tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
<tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
<!--查詢-->
<tx:method name="find*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
<tx:method name="select*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
<tx:method name="query*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
<tx:method name="get*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
</tx:attributes>
</tx:advice>
可以將默認的屬性刪除掉
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--方法的過濾-->
<tx:attributes>
<!--指定需要攔截的方法
isolation:隔離級別,一般選擇默認
propagation:傳播的行爲
read-only:是否爲只讀的事務,增刪改選擇非只讀;查詢選擇只讀
timeout:超時時間,單位爲s,-1時爲永不超時
*:通配符配置,如find*,只要以find開頭即可
-->
<!--增刪改-->
<tx:method name="insert*" />
<tx:method name="add*" />
<tx:method name="update*" />
<tx:method name="del*" />
<tx:method name="delete*"/>
<tx:method name="save*" />
<!--查詢-->
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="select*" propagation="SUPPORTS" read-only="true" />
<tx:method name="query*" propagation="SUPPORTS" read-only="true" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
再簡化:可以將查詢或增刪改的其中一部分,用*來代替
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--方法的過濾-->
<tx:attributes>
<!--查詢-->
<tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="select*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="query*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"></tx:method>
<!--其他配置事務,增刪改-->
<tx:method name="*"></tx:method>
</tx:attributes>
</tx:advice>
三、配置切面
advice-ref
:通知關聯對象pointcut
:切入點
<aop:config>
<!--切面配置
advice-ref:通知關聯對象
pointcut:切入點
-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service..*.*(..))"></aop:advisor>
</aop:config>
四、測試
業務層
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
AccountDao accountDao;
@Override
public void transfer(String fromName, String toName, Float money) {
//查詢轉賬的賬戶
Account fromAccount = accountDao.findByName(fromName);
//查詢接收的賬戶
Account toAccount = accountDao.findByName(toName);
//開始轉賬
fromAccount.setMoney(fromAccount.getMoney()-money);
toAccount.setMoney(toAccount.getMoney()+money);
//持久化到數據庫
accountDao.update(fromAccount);
System.out.println(0/0);
accountDao.update(toAccount);
}
}
測試類
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class testJdbcTemplate {
@Autowired
AccountService accountService;
@Test
public void test(){
accountService.transfer("aaa","ccc",500f);
}
}
註解的事務管理
引入依賴和上面完全相同
一、創建事務管理器對象
<!--創建事務管理器對象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入數據源屬性:事務存在連接中,連接存在連接池中-->
<property name="dataSource" ref="springDataSource"></property>
</bean>
二、開啓事務的註解管理和aop的自動代理
<!--開啓事務的註解管理-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
<!--開啓aop的註解,自動代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
三、標記註解
1、在業務層的類上標記註解@Transactional
會出現一個問題:類中所有的方法都會使用事務管理
2、刪除在類上的註解,在方法上標記註解@Transactional
:只有該方法按照事務執行
四、配置屬性
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, readOnly = false, timeout = -1)
public void transfer(String fromName, String toName, Float money) {
//查詢轉賬的賬戶
Account fromAccount = accountDao.findByName(fromName);
//查詢接收的賬戶
Account toAccount = accountDao.findByName(toName);
//開始轉賬
fromAccount.setMoney(fromAccount.getMoney()-money);
toAccount.setMoney(toAccount.getMoney()+money);
//持久化到數據庫
accountDao.update(fromAccount);
System.out.println(0/0);
accountDao.update(toAccount);
}
也可以省略
@Transactional()
public void transfer(String fromName, String toName, Float money) {
//查詢轉賬的賬戶
Account fromAccount = accountDao.findByName(fromName);
//查詢接收的賬戶
Account toAccount = accountDao.findByName(toName);
//開始轉賬
fromAccount.setMoney(fromAccount.getMoney()-money);
toAccount.setMoney(toAccount.getMoney()+money);
//持久化到數據庫
accountDao.update(fromAccount);
System.out.println(0/0);
accountDao.update(toAccount);
}