天天看點

3.靜态代理、動态代理、AOP實作、mybatis整合、spring聲明式事務

文章目錄

         1.靜态代理

                 1.1加深了解

         2.動态代理

         3.AOP實作

                 3.1什麼是AOP

                 3.2AOP在spring中的作用

                 3.3使用spring實作AOP

                         3.31實作方式一

                         3.32實作方式二

                 3.4注解實作

         4.mybatis整合

                 4.1步驟

                 4.2回顧mybatis

                 4.3整合方式一

                 4.4整合方式二

         5.spring聲明式事務

                 5.1事務回顧

                 5.2spring聲明事務

1.靜态代理

為什麼要學習代理模式?因為這就是SpringAOP的底層【SpringAOP和SpringMVC】

代理模式的分類:

  • 靜态代理
  • 動态代理
    3.靜态代理、動态代理、AOP實作、mybatis整合、spring聲明式事務

靜态代理

角色分析:

  • 抽象角色:一般會使用接口或者抽象類來解決
  • 真實角色:被代理的角色
  • 代理角色:代理真實角色,代理真實角色後,我們一般會做一些附屬操作
  • 客戶:通路代理對象的人

代碼步驟:

1.接口

//租房
public interface Rent {
    public void rent();
}
           

2.真實角色

//房東
public class Host implements Rent{
    public void rent() {
        System.out.println("房東出租房子");
    }
}
           

3.代理角色

public class Proxy implements Rent{
    private Host host;

    public Proxy() {
    }

    public Proxy(Host host) {
        this.host = host;
    }

    public void rent() {
        host.rent();
        seeHouse();
        sign();
        fee();
    }

    //看房
    public void seeHouse(){
        System.out.println("中介帶着看房子");
    }

    //簽合同
    public void sign(){
        System.out.println("和中介簽署租賃合同");
    }

    //收費用
    public void fee(){
        System.out.println("中介收取費用");
    }
}
           

4.用戶端通路代理角色

public class Client {
    public static void main(String[] args) {
        //房東要出租房子
        Host host = new Host();
//        host.rent();

        //代理,中介幫房東出租房子,并且代理角色一般會有一些附屬操作
        Proxy proxy = new Proxy(host);

        //不用面對房東,直接找中介租房即可
        proxy.rent();
    }
}
           

代理模式的好處:

  • 可以使真實角色的操作更加純粹,不用去關注一些公共的業務
  • 公共角色就交給代理角色,實作了業務的分工
  • 公共業務發生擴充的時候,友善集中管理

缺點:

  • 一個真實角色就會産生一個代理角色,代碼量會翻倍,開發效率會變低

1.1加深了解

代碼步驟:

1.接口

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
           

2.真實角色

//真實角色
public class UserServiceImpl implements UserService{
    public void add() {
        System.out.println("增加了一個使用者");
    }

    public void delete() {
        System.out.println("删除了一個使用者");
    }

    public void update() {
        System.out.println("修改了一個使用者");
    }

    public void query() {
        System.out.println("查詢了一個使用者");
    }
}
           

3.代理角色

public class UserServiceProxy implements UserService{
    private UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    public void add() {
        log("add");
        userService.add();
    }

    public void delete() {
        log("delete");
        userService.delete();
    }

    public void update() {
        log("update");
        userService.update();
    }

    public void query() {
        log("query");
        userService.query();
    }

    public void log(String msg){
        System.out.println("[Debug] 使用了一個"+msg+"方法");
    }
}
           

4.用戶端通路代理角色

public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();

        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);

        proxy.delete();
    }
}
           

聊聊AOP

3.靜态代理、動态代理、AOP實作、mybatis整合、spring聲明式事務

2.動态代理

  • 動态代理和靜态代理角色一樣
  • 動态代理的代理類是動态生成的,不是我們直接寫好的
  • 動态代理分為兩大類:基于接口的動态代理,基于類的動态代理
    • 基于接口 — JDK動态代理【我們在這裡使用】
    • 基于類:cglib
    • java位元組碼實作:javassist

需要了解兩個類:Proxy:代理;InvocationHandler:調用處理程式。

代碼步驟:

1.接口

public interface Rent {
    public void rent();
}
           

2.真實角色

public class Host implements Rent{
    public void rent() {
        System.out.println("房東要出租房子");
    }
}
           

3.ProxyInvocationHandler類

//我們會用這個類,自動生成代理類
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成得到代理類
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                rent.getClass().getInterfaces(),this);
    }

    //處理代理執行個體,并傳回結果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //動态代理的本質,就是使用反射機制實作
        Object result = method.invoke(rent, args);
        seeHose();
        fee();
        return result;
    }

    public void seeHose(){
        System.out.println("中介帶着看房子");
    }

    public void fee(){
        System.out.println("中介收取費用");
    }
}
           

4.測試

public class Client {
    public static void main(String[] args) {
        //真實角色
        Host host = new Host();

        //代理角色:現在沒有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();

        //通過調用程式處理角色來處理我們要調用的接口對象
        pih.setRent(host);
        Rent proxy = (Rent) pih.getProxy(); //這裡的proxy就是動态生成的,我們并沒有寫
        proxy.rent();

    }
}
           

在此,我們可以提煉出ProxyInvocationHandler作為工具類

//用這個類自動生成代理類
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    //生成得到代理類
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }

    //處理代理執行個體,并傳回結果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }

    public void log(String msg){
        System.out.println("[Debug] 使用了一個"+msg+"方法");
    }
}
           

動态代理的好處:

  • 可以使真實角色的操作更加純粹,不用去關注一些公共的業務
  • 公共角色就交給代理角色,實作了業務的分工
  • 公共業務發生擴充的時候,友善集中管理
  • 一個動态代理類代理的是一個接口,一般就是對應的一類業務
  • 一個動态代理類可以代理多個類,隻要是實作了同一個接口即可

3.AOP實作

3.1什麼是AOP

AOP(Aspect Oriented Programming)意為:面向切面程式設計,通過預編譯方式和運作期動态代理實作程式功能的統一維護的一種技術。AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring架構中的一個重要内容,是函數式程式設計的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,進而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。

3.靜态代理、動态代理、AOP實作、mybatis整合、spring聲明式事務

3.2 AOP在Spring中的作用

提供聲明式事務;允許使用者自定義切面

  • 橫切關注點:跨越應用程式多個子產品的方法或功能。即是,與我們業務邏輯無關的,但是我們需要關注的部分,就是橫切關注點。如日志,安全,緩存,事務等等…
  • 切面(ASPECT):橫切關注點被子產品化的特殊對象。即,它是一個類。
  • 通知(Advice):切面必須要完成的工作。即,它是類中的一個方法。
  • 目标(Target):被通知對象。
  • 代理(Proxy):向目标對象應用通知之後建立的對象。
  • 切入點(PointCut):切面通知執行的“地點”的定義。
  • 連接配接點(JointPoint):與切入點比對的執行點。
3.靜态代理、動态代理、AOP實作、mybatis整合、spring聲明式事務

SpringAOP中,通過Advice定義橫切邏輯,Spring中支援5種類型的Advice:

3.靜态代理、動态代理、AOP實作、mybatis整合、spring聲明式事務
3.靜态代理、動态代理、AOP實作、mybatis整合、spring聲明式事務

即AOP在不改變原有代碼的情況下,去增加新的功能。

3.3 使用Spring實作AOP

【重點】使用AOP織入,需要導入一個依賴包

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>
           

3.31實作方式一

方式一: 使用Spring的API接口【主要是SpringAPI接口實作】

1.在service包下,定義UserService業務接口和UserServiceImpl實作類

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void select();
}
           
public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("增加了一個使用者");
    }

    public void delete() {
        System.out.println("删除了一個使用者");
    }

    public void update() {
        System.out.println("更新了一個使用者");
    }

    public void select() {
        System.out.println("查詢了一個使用者");
    }
}
           

2.在log包下,定義我們的增強類,一個Log前置增強和一個AfterLog後置增強類

public class Log implements MethodBeforeAdvice {

    //method: 要執行的目标對象的方法
    //args:參數
    //target:目标對象
    public void before(Method method, Object[] agrs, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被執行了");
    }
}
           
public class AfterLog implements AfterReturningAdvice {

    //returnValue: 傳回值
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("執行了"+method.getName()+"方法,傳回結果為:"+returnValue);
    }
}
           

3.最後去spring的檔案中注冊 , 并實作aop切入實作 , 注意導入限制,配置applicationContext.xml檔案

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注冊bean-->
    <bean id="userService" class="com.redeemi.service.UserServiceImpl"/>
    <bean id="log" class="com.redeemi.log.Log"/>
    <bean id="afterLog" class="com.redeemi.log.AfterLog"/>

    <!--方式一:使用原生Spring API接口-->
    <!--配置aop:需要導入aop的限制-->
    <aop:config>
        <!--切入點:expression:表達式,execution(要執行的位置!* * * * *)-->
        <aop:pointcut id="pointcut" expression="execution(* com.redeemi.service.UserServiceImpl.*(..))"/>

        <!--執行環繞增加!-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

</beans>
           

4.測試

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        //動态代理代理的是接口:注意點
        UserService userService = (UserService) context.getBean("userService");

        userService.add();
//        userService.select();
    }
}
           

3.32實作方式二

方式二: 自定義類來實作AOP【主要是切面定義】

1.在diy包下定義自己的DiyPointCut切入類

public class DiyPointCut {
    public void before(){
        System.out.println("======方法執行前======");
    }

    public void after(){
        System.out.println("======方法執行後======");
    }
}
           

2.去spring中配置檔案

<!--方式二:自定義類-->
    <bean id="diy" class="com.redeemi.diy.DiyPointCut"/>

    <aop:config>
        <!--自定義切面,ref 要引用的類-->
        <aop:aspect ref="diy">
            <!--切入點-->
            <aop:pointcut id="point" expression="execution(* com.redeemi.service.UserServiceImpl.*(..))"/>
            <!--通知-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
           

3.測試

3.4注解實作

方式三: 使用注解實作

1.在diy包下定義注解實作的AnnotationPointCut增強類

//聲明式事務
@Aspect //标注這個類是一個切面
public class AnnotationPointCut {

    @Before("execution(* com.redeemi.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("====方法執行前====");
    }

    @After("execution(* com.redeemi.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("====方法執行後====");
    }

    //在環繞增強中,我們可以給定一個參數,代表我們要擷取處理切入的點;
    @Around("execution(* com.redeemi.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable{
        System.out.println("環繞前");

        Signature signature = jp.getSignature();// 獲得簽名
        System.out.println("signature:"+signature);

        Object proceed = jp.proceed(); //執行方法

        System.out.println("環繞後");

        System.out.println(proceed);
    }

}
           

2.在Spring配置檔案中,注冊bean,并增加支援注解的配置。

<!--方式三:使用注解-->
<bean id="annotationPointCut" class="com.redeemi.diy.AnnotationPointCut"/>
<!--開啟注解支援! JDK(預設是 proxy-target-class="false")  cglib(proxy-target-class="true")-->
<aop:aspectj-autoproxy/>
           

3.測試

4.mybatis整合

4.1步驟

1.導入相關jar包

  • junit
  • mybatis
  • mysql資料庫
  • spring相關
  • aop織入器
  • mybatis-spring整合包【重點】在此還導入了lombok包。
  • 配置Maven靜态資源過濾問題
<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>
        <!--Spring操作資料庫的話,還需要一個spring-jdbc
               -->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
    </build>
           

2.編寫配置檔案

3.測試

4.2回顧mybatis

1.編寫pojo實體類

@Data
public class User {
    private int id;
    private String name;
    private String pwd;
}
           

2.編寫實作mybatis的配置檔案

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
       PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
       "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

   <typeAliases>
       <package name="com.redeemi.pojo"/>
   </typeAliases>

   <environments default="development">
       <environment id="development">
           <transactionManager type="JDBC"/>
           <dataSource type="POOLED">
               <property name="driver" value="com.mysql.jdbc.Driver"/>
               <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf8"/>
               <property name="username" value="root"/>
               <property name="password" value="qwer2020"/>
           </dataSource>
       </environment>
   </environments>

   <mappers>
       <package name="com.redeemi.dao"/>
   </mappers>
</configuration>
           

3.編寫UserMapper接口

public interface UserMapper {
    public List<User> selectUser();
}
           

4.編寫UserMapper.xml檔案

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.redeemi.mapper.UserMapper">

    <!--sql-->
    <select id="selectUser" resultType="user">
        select * from mybatis.user
    </select>
</mapper>
           

5.測試

@Test
public void selectUser() throws IOException {

   String resource = "mybatis-config.xml";
   InputStream inputStream = Resources.getResourceAsStream(resource);
   SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
   SqlSession sqlSession = sqlSessionFactory.openSession();

   UserMapper mapper = sqlSession.getMapper(UserMapper.class);

   List<User> userList = mapper.selectUser();
   for (User user: userList){
       System.out.println(user);
  }

   sqlSession.close();
}
           

Mybatis-Spring

什麼是MyBatis-Spring?

MyBatis-Spring 會幫助你将 MyBatis 代碼無縫地整合到 Spring 中。

如果使用 Maven 作為建構工具,僅需要在 pom.xml 中加入以下代碼即可:

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.2</version>
    </dependency>
           

4.3整合方式一

1.引入Spring配置檔案spring-dao.xml

<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd

</beans>
           

2.配置資料源替換mybaits的資料源

<!--DataSource:使用Spring的資料源替換Mybatis的配置 c3p0 dbcp druid
    我們這裡使用Spring提供的JDBC:-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="qwer2020"/>
    </bean>
           

3.配置SqlSessionFactory,關聯MyBatis

<!--sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!--關聯mybatis配置檔案-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/redeemi/mapper/*.xml"/>
    </bean>
           

4.注冊sqlSessionTemplate,關聯sqlSessionFactory

<!--SqlSessionTemplate:就是我們使用的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--隻能使用構造器注入sqlSessionFactory,因為它沒有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory" />
    </bean>
           

5.需要UserMapper接口的UserMapperImpl 實作類,私有化sqlSessionTemplate

public class UserMapperImpl implements UserMapper {

    //我們的所有操作,都使用sqlSession來執行,在原來,現在都使用SqlsessionTemplate
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}
           

6.将自己寫的實作類,注入到Spring配置檔案中。

<bean id="userMapper" class="com.redeemi.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>
           

7.測試使用即可

@Test
    public void test () throws IOException {

        ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
        
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        for (User user : userMapper.selectUser()) {
            System.out.println(user);
        }
    }
           

結果成功輸出,現在我們的Mybatis配置檔案的狀态,發現都可以被Spring整合

<?xml version="1.0" encoding="GBK" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration core file-->
<configuration>
    
    <typeAliases>
        <package name="com.redeemi.pojo"/>
    </typeAliases>

</configuration>
           

4.4整合方式二

mybatis-spring1.2.3版以上的才有這個,官方文檔截圖:

dao繼承Support類 , 直接利用 getSqlSession() 獲得 , 然後直接注入SqlSessionFactory . 比起整合方式一 , 不需要管理SqlSessionTemplate , 而且對事務的支援更加友好 . 可跟蹤源碼檢視。

3.靜态代理、動态代理、AOP實作、mybatis整合、spring聲明式事務

測試:

1.将我們上面寫的UserMapperImpl修改一下

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {

    public List<User> selectUser() {
        
        return getSqlSession().getMapper(UserMapper.class).selectUser();
    }
}
           

2.注入到Spring配置檔案中。

3.測試

@Test
    public void test () throws IOException {

        ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
        
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        for (User user : userMapper.selectUser()) {
            System.out.println(user);
        }
    }
           

5.spring聲明式事務

5.1事務回顧

  • 把一組業務當成一個業務來做;要麼都成功,要麼都失敗
  • 事務在項目開發中,十分的重要,涉及到資料的一緻性問題,不能馬虎
  • 確定完整性和一緻性。

事務ACID原則:

  • 原子性(atomicity)
    • 事務是原子性操作,由一系列動作組成,事務的原子性確定動作要麼全部完成,要麼完全不起作用。
  • 一緻性(consistency)
    • 一旦所有事務動作完成,事務就要被送出。資料和資源處于一種滿足業務規則的一緻性狀态中。
  • 隔離性(isolation)
    • 可能多個事務會同時處理相同的資料,是以每個事務都應該與其他事務隔離開來,防止資料損壞。
  • 持久性(durability)
    • 事務一旦完成,無論系統發生什麼錯誤,結果都不會受到影響。通常情況下,事務的結果被寫到持久化存儲器中。

測試:

将上面的代碼拷貝到一個新項目中

在之前的案例中,我們給userMapper接口新增兩個方法,删除和增加使用者;

//添加一個使用者
int addUser(User user);

//根據id删除使用者
int deleteUser(int id);
           

UserMapper檔案,我們故意把 deletes 寫錯,測試

<insert id="addUser" parameterType="com.redeemi.pojo.User">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>

<delete id="deleteUser" parameterType="int">
deletes from user where id = #{id}
</delete>
           

編寫接口的UserMapperImpl實作類,在實作類中,去操作一下

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {


    //增加一些操作
    public List<User> selectUser() {
        User user = new User(5, "小王", "185161");
        UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
        mapper.addUser(user);
        mapper.deleteUser(5);

        return mapper.selectUser();
    }
    
    //新增
    public int addUser(User user) {
        return getSqlSession().getMapper(UserMapper.class).addUser(user);
    }

    //删除
    public int deleteUser(int id) {
        return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
    }
}
           

測試

@Test
public void test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

    UserMapper userMapper = context.getBean("userMapper", UserMapper.class);

    for (User user : userMapper.selectUser()) {
        System.out.println(user);
    }
}
           

報錯:sql異常,delete寫錯了

結果 :資料庫結果顯示插入成功

沒有進行事務的管理;我們想讓他們都成功才成功,有一個失敗,就都失敗,我們就應該需要事務

以前我們都需要自己手動管理事務,十分麻煩

但是Spring給我們提供了事務管理,我們隻需要配置即可;

5.2spring聲明式事務

Spring在不同的事務管理API之上定義了一個抽象層,使得開發人員不必了解底層的事務管理API就可以使用Spring的事務管理機制。Spring支援程式設計式事務管理和聲明式的事務管理。

程式設計式事務管理

  • 将事務管理代碼嵌到業務方法中來控制事務的送出和復原
  • 缺點:必須在每個事務操作業務邏輯中包含額外的事務管理代碼

聲明式事務管理

  • 一般情況下比程式設計式事務好用。
  • 将事務管理代碼從業務方法中分離出來,以聲明的方式來實作事務管理。
  • 将事務管理作為橫切關注點,通過aop方法子產品化。Spring中通過Spring AOP架構支援聲明式事務管理。

1.使用Spring管理事務,注意頭檔案的限制導入 : tx

xmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
           

2.JDBC事務

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
           

3.配置好事務管理器後我們需要去配置事務的通知

<!--結合AOP實作事務的織入-->
<!--配置事務通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--給那些方法配置事務-->
    <!--配置事務的傳播特性: new -->
    <tx:attributes>
        <tx:method name="add" propagation="REQUIRED"/>
        <tx:method name="delete" propagation="REQUIRED"/>
        <tx:method name="update" propagation="REQUIRED"/>
        <tx:method name="query" read-only="true"/>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>
           

spring事務傳播特性:

事務傳播行為就是多個事務方法互相調用時,事務如何在這些方法間傳播。spring支援7種事務傳播行為:

  • propagation_requierd:如果目前沒有事務,就建立一個事務,如果已存在一個事務中,加入到這個事務中,這是最常見的選擇。
  • propagation_supports:支援目前事務,如果沒有目前事務,就以非事務方法執行。
  • propagation_mandatory:使用目前事務,如果沒有目前事務,就抛出異常。
  • propagation_required_new:建立事務,如果目前存在事務,把目前事務挂起。
  • propagation_not_supported:以非事務方式執行操作,如果目前存在事務,就把目前事務挂起。
  • propagation_never:以非事務方式執行操作,如果目前事務存在則抛出異常。
  • propagation_nested:如果目前存在事務,則在嵌套事務内執行。如果目前沒有事務,則執行與propagation_required類似的操作。

Spring 預設的事務傳播行為是 PROPAGATION_REQUIRED,它适合于絕大多數的情況。

就好比我們剛才的幾個方法存在調用,是以會被放在一組事務當中

4.配置AOP,導入aop的頭檔案

<!--配置事務切入-->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.redeemi.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>
           

5.删掉剛才插入的資料,再次測試

@Test
public void test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

    UserMapper userMapper = context.getBean("userMapper", UserMapper.class);

    for (User user : userMapper.selectUser()) {
        System.out.println(user);
    }
}
           

思考:

為什麼需要事務?

  • 如果不配置事務,可能存在資料送出不一緻的情況;
  • 如果我們不在Spring中去配置聲明式事務,我們就需要在代碼中手動配置事務
  • 事務在項目的開發中十分重要,涉及到資料的一緻性和完整性問題,不容馬虎