達內12 note unit 09 01
1.spring事務管理
2.spring提供了對事務管理支持
spring采用aop機制完成事務控制
可以實現在不修改原有組件代碼情況下實現事務控制功能。
spring提供了兩種事務管理方式:
a。編程式事務管理(編寫java代碼)
TransactionTemplate
b.聲明式事務管理(編寫配置,大家都用這種)
xml版本配置
注解版本配置
--配置DataSourceTransactionManager
--開啟事務注解配置<tx:annotation>
--在目標組件方法前添加@Transactional
注解版本例子:
例如UserServiceImpl中的regist注冊方法需要事務:
第一步,我們現在applicationContext.xml中配置事務管理組件
<!-- 配置事務管理組件 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dbcp"></property>
</bean>
<!-- 開啟事務注解標記@Transactional -->
<!-- 當調用帶@Transactional 標記的方法時,將txManager事務管理功能切入到方法-->
<tx:annotation-driven transaction-manager="txManager"/>
第二步,在所有service類上,加上@Transactional注解
package org.alexhe.note.service;
import javax.annotation.Resource;
import org.alexhe.note.dao.IUserDao;
import org.alexhe.note.entity.NoteResult;
import org.alexhe.note.entity.User;
import org.alexhe.note.util.NoteUtil;
import org.springframework.stereotype.Service;
@Service("userService")
@Transactional//這里加入事務注解
public class UserServiceImpl implements IUserService{
@Resource
private IUserDao userDao;//注入
@Override
public NoteResult checkLogin(String name, String pwd) throws Exception {
// TODO Auto-generated method stub
NoteResult result=new NoteResult();
User user=userDao.findByName(name);
if(user==null){
result.setStatus(1);
result.setMsg("用戶名不存在");
return result;
}
String md5_pwd=NoteUtil.md5(pwd);
if(!user.getCn_user_password().equals(md5_pwd)){
result.setStatus(2);
result.setMsg("密碼不正確");
return result;
}
result.setStatus(0);
result.setMsg("用戶名和密碼正確");
result.setData(user.getCn_user_id());//返回userid
return result;
}
@Override
public NoteResult regist(String name, String password, String nickname) throws Exception {
NoteResult result=new NoteResult();
//檢測用戶名是否被占用
User has_user=userDao.findByName(name);
if(has_user!=null){
result.setStatus(1);
result.setMsg("用戶名已被占用");
return result;
}
//注冊
User user=new User();
user.setCn_user_name(name);
user.setCn_user_desc(nickname);
String md5_pwd=NoteUtil.md5(password);
user.setCn_user_password(md5_pwd);//設置加密的密碼
String userId=NoteUtil.createId();
user.setCn_user_id(userId);//設置userid
//調用userDao保存
userDao.save(user);
result.setStatus(0);
result.setMsg("注冊成功");
return result;
}
}
xml版本配置例子:(配置比注解版復雜)
第一步,我們現在applicationContext.xml中配置事務管理組件,記得xml里加入aop的頭
<!-- 配置事務管理組件 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dbcp"></property>
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes><!-- 哪些方法用事務,就寫在里面 -->
<tx:method name="regist"/>
<tx:method name="checkLogin"/>
<tx:method name="add*"/> <!-- 以add開頭的所有方法 -->
<!-- <tx:method name="*"/> <!-- 所有方法都加注釋 -->
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="target" expression="within(org.alexhe.note.service..*)"/> <!-- expression代表哪個組件,作用在哪些組件上,這里代表service包及其下面的所有組件-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="target"/>
</aop:config>
3.Spring對事務管理的控制
a。控制事務可讀可寫特性
Spring分為可讀寫事務和只讀事務。默認為可讀寫,一般只涉及查詢操作,建議用只讀事務
@Transactional(readOnly=true)
b.控制事務是否回滾
Spring遇到runtimeException異常,會回滾。遇到非運行時異常,不會回滾。
@Transactional(readOnly=true,rollbackFor=IOException.class) 這樣遇到IOException也會發生回滾。
建議:自定義異常繼承自RuntimeException繼承
public class MyException extends RuntimeException
c。控制事務傳播類型
遇到帶有事務控制方法調用另一個事務控制方法時,可以選擇合適的傳播類型,默認是required類型,后者使用前者事務。
d。控制事務隔離級別
@Transactional(readOnly=true, isolation=Isolation.READ_COMMITED);
由低到高如下:
READ_UNCOMMITED讀未提交
READ_COMMITED讀已提交
REPEATABLE_READ可重復讀
SERIALIZABLE序列化操作
DEFAULT默認,根據數據庫隔離級別自動選擇,
4.spring aop應用
aop編程優點:可以動態將一個組件功能切入到指定的目標方法上。可以使結構更加靈活,也能實現組件重復利用。
aop編程:更注重於業務邏輯隔離,將一些共通處理邏輯和傳統處理邏輯解耦。
例如事務處理,日志記錄,異常處理等等。
適用環境:
--共通處理邏輯
--調用時機相同
例子,用xml配置方式,往controller上加方法:
1.新建了一個aspect包(非必須)
2.包里新建一個類,然后新建一個clogger方法。表示加上日志功能。
3.spring的文件里加配置,把需要aop的controller加上第二步類里的方法
package org.alexhe.note.aspect;
public class NoteLogger {
public void clogger(){
System.out.println("進入Controller處理");
}
}
spring的配置:
<!-- aop示例 -->
<bean id="noteLogger" class="org.alexhe.note.aspect.NoteLogger"></bean>
<aop:config>
<!-- 把上面的noteLogger定義為切面組件 -->
<aop:aspect ref="noteLogger">
<!-- 什么時候,向哪些方法上切入 --><!-- 在controler包及其子包下,所有執行的方法前,加入clogger方法 -->
<aop:before method="clogger" pointcut="within(org.alexhe.note.controller..*)"/>
</aop:aspect>
</aop:config>
例子,用注解方式,往service層加方法:
spring配置不用上面這一坨,只要加上:
<!-- 開啟aop注解支持,@Aspect,@通知標記 -->
<aop:aspectj-autoproxy />
java類:
package org.alexhe.note.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component//掃描,將組件掃描到Spring容器
@Aspect//將當前組件設置為切面組件
public class ServiceLogger {
@Before("within(org.alexhe.note.service..*)")//service下面所有方法加入這個slooger方法
public void slogger(){
System.out.println("進入service方法");
}
}
=====AOP應用=====
a。要切入什么功能
b。要切入的時機,什么時候切入,通知。
前置通知,在原有方法前插入新功能。@Before
后置通知,在執行完原有方法后,調入新的切面方法。@AfterReturning
異常通知,在原有方法出異常了,調入新的切面方法。@AfterThrowing
最終通知,不管有沒有異常,最終都要走他。@After
環繞通知=前置+后置
try{
前置通知@Before
//目標方法處理
后置通知@AfterReturning
}catch(){
異常通知@AfterThrowing
}finally{
最終通知@After
}
c。往那些組件方法切入-->切入點
--類型限定表達式
within(類型)
與類型匹配的組件都是目標
within(org.service.UserService)
within(org.service.*) 僅限於當前包下
within(org.service..*) 當前包和子包下
--方法限定表達式
execution(修飾符 返回類型 方法名(參數) 拋出異常) 返回類型和方法名參數是必須的,其他可以省略
execution(* find*(..)) 必須是find開頭的方法,參數返回值不限制
execution(* org.service.UserService.regist*(..))
execution(* org.service..*.*(..))
上述表達式可以使用 !,&&,|| 運算符連接。
案例:將異常信息寫入文件
package org.alexhe.note.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
import org.springframework.stereotype.Component;
@Component//掃描,將組件掃描到Spring容器
@Aspect//將當前組件設置為切面組件
public class ExceptionLogger {
@AfterThrowing(throwing="e",pointcut="within(org.alexhe.note.service..*)")//service下面所有方法加入這個log方法
public void log(Exception e){
System.out.println(e);
}
}