JavaWeb-12-Spring聲明式事務-JdbcTemplate

Table of Contents

四:JdbcTemplate

1:Spring配置c3p0的數據庫連接池

1.1:導包:

1.2:配置c3p0數據庫連接池

1.3:設置數據庫連接池參數

1.4:測試

2:Spring使用JdbcTemplate

2.1:導包:

2.2:配置JdbcTemplate

2.3:測試

3:JdbcTemplate的操作

3.1:更新sql:jdbcTemplate.update(sql, 1000, 5);

3.2:批量插入:jdbcTemplate.batchUpdate(sql, batchArgs);

3.3:查詢單個對象或者單個字段值:jdbcTemplate.queryForObject();

3.4:查詢結果爲對象的集合:jdbcTemplate.query

3.5:查詢返回list:jdbcTemplate.queryForList()

3.6:使用具名參數的的jdbcTemplate

3.7:也可以使用namedParameterJdbcTemplate直接傳入類對象

五:聲明式事務

1:事務簡介

2:以前的編程式事務

3:Spring的聲明時事務

3.1:導包

3.2:配置數據源

3.3:寫相關的邏輯操作

3.4:配置聲明式事務管理

 

4:事務管理器的屬性

4.1:timout-超時

4.2:readOnly ---只讀屬性

4.3:觸發事務回滾異常---noRollbackFor和rollbackFor

4.4:事務隔離級別---數據庫事務一個讀,一個寫就會發生問題;

4.5:事務的傳播特性

5:使用註解聲明事務


四:JdbcTemplate

1:Spring配置c3p0的數據庫連接池

1.1:導包:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wkl</groupId>
    <artifactId>spring03JDBC</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.1</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
    </dependencies>

</project>

1.2:配置c3p0數據庫連接池

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

    <context:property-placeholder location="classpath:jdbcproperties.properties"></context:property-placeholder>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
    </bean>
</beans>

1.3:設置數據庫連接池參數

jdbc.user=root
jdbc.password=123456
jdbc.url=jdbc:mysql://127.0.0.1:3306/jdbc_template?serverTimezone=Hongkong
jdbc.driverClass=com.mysql.cj.jdbc.Driver

1.4:測試

public class TestAop {

    ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");

    @Test
    public void test01(){
//        UserInter bean = ioc.getBean(UserInter.class);
//        bean.add();
//        System.out.println(""+bean.getClass());

        UserImpl userImpl = (UserImpl) ioc.getBean("userImpl");
        userImpl.add();
        System.out.println(""+userImpl.getClass());

    }
}

2:Spring使用JdbcTemplate

2.1:導包:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wkl</groupId>
    <artifactId>spring03JDBC</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.1</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>

        <!--導入jdbctemplate包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
    </dependencies>

</project>

2.2:配置JdbcTemplate

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>

2.3:測試

    @Test
    public void test02() throws SQLException {
        JdbcTemplate bean = ioc.getBean(JdbcTemplate.class);
        System.out.println(""+bean);
    }

3:JdbcTemplate的操作

3.1:更新sql:jdbcTemplate.update(sql, 1000, 5);

@Test
    public void test02() throws SQLException {
        String sql = "UPDATE employee set salary = ? where emp_id = ?";
        int update = jdbcTemplate.update(sql, 1000, 5);
        System.out.println(""+update);
    }

3.2:批量插入:jdbcTemplate.batchUpdate(sql, batchArgs);

@Test
    public void test03(){
        String  sql ="INSERT INTO employee(emp_name,salary) VALUES(?,?)";
        //List<Object[]>
        //List的長度就是sql語句要執行的次數
        //Object[]:每次執行要用的參數
        List<Object[]> batchArgs = new ArrayList<Object[]>();
        batchArgs.add(new Object[]{"張三",1998.98});
        batchArgs.add(new Object[]{"李四",2998.98});
        batchArgs.add(new Object[]{"王五",3998.98});
        batchArgs.add(new Object[]{"趙六",4998.98});

        int[] is = jdbcTemplate.batchUpdate(sql, batchArgs);
        for (int i : is) {
            System.out.println(i);
        }
    }

3.3:查詢單個對象或者單個字段值:jdbcTemplate.queryForObject();

@Test
    public void test04(){
        String sql = "SELECT emp_id empId,emp_name empName,salary FROM employee WHERE emp_id=?";
        //RowMapper:每一行記錄和javaBean的屬性如何映射
        Employee employee = null;
        try {
             Double object = jdbcTemplate.queryForObject(sql, Double.class);
            employee = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Employee.class), 50);
        } catch (DataAccessException e) {

        }
        System.out.println(employee);
    }

3.4:查詢結果爲對象的集合:jdbcTemplate.query

 @Test
    public void test05(){
        String sql = "SELECT emp_id empId,emp_name empName,salary FROM employee WHERE salary>?";
        //封裝List;集合裏面元素的類型
        List<Employee> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Employee.class), 4000);

        for (Employee employee : list) {
            System.out.println(employee);
        }
    }

3.5:查詢返回list<map>:jdbcTemplate.queryForList()

@Test
    public void test03() throws SQLException {
        String sql = "select * from employee";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        for (Map<String, Object> map : maps) {
            System.out.println(""+map);
        }
    }

3.6:使用具名參數的的jdbcTemplate

<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
    <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>

    @Test
    public void test07(){
        String sql = "INSERT INTO employee(emp_name,salary) VALUES(:empName,:salary)";

        //Map
        Map<String, Object> paramMap = new HashMap<>();
        //將所有具名參數的值都放在map中;
        paramMap.put("empName", "田七");
        paramMap.put("salary", 9887.98);
        int update = namedJdbcTemplate.update(sql, paramMap);
        System.out.println(update);
    }

3.7:也可以使用namedParameterJdbcTemplate直接傳入類對象

 @Test
    public void test08(){
        String sql = "INSERT INTO employee(emp_name,salary) VALUES(:empName,:salary)";
        Employee employee = new Employee();
        employee.setEmpName("哈哈");
        employee.setSalary(998.98);

        //
        int i = namedJdbcTemplate.update(sql, new BeanPropertySqlParameterSource(employee));
        System.out.println(i);

    }

五:聲明式事務

1:事務簡介

●事務就是一組由於邏輯上緊密關聯而合併成一個整體(工作單元)的多個數據庫操作,這些操作要麼都執行要麼都不執行

●事務的四個關鍵屬性(ACID)

原子性(atomicity):“原子”的本意是“不可再分”,事務的原子性表現爲一個事務中涉及到的多個操作在邏輯上缺一不可。事務的原子性要求事務中的所有操作要麼都執行,要麼都不執行。

一致性(consistency):“一致”指的是數據的一致,具體是指:所有數據都處於滿足業務規則的一致性狀態。一致性原則要求:一個事務中不管涉及到多少個操作,都必須保證事務執行之前數據是正確的,事務執行之後數據仍然是正確的。如果一個事務在執行的過程中,其中某一個或某幾個操作失敗了,則必須將其他所有操作撤銷,將數據恢復到事務執行之前的狀態,這就是回滾

隔離性(isolation):在應用程序實際運行過程中,事務往往是併發執行的,所以很有可能有許多事務同時處理相同的數據,因此每個事務都應該與其他事務隔離開來,防止數據損壞。隔離性原則要求多個事務在併發執行過程中不會互相干擾

持久性(durability):持久性原則要求事務執行完成後,對數據的修改永久的保存下來,不會因各種系統錯誤或其他意外情況而受到影響。通常情況下,事務對數據的修改應該被寫入到持久化存儲器中。

2:以前的編程式事務

①使用原生的JDBC API進行事務管理

      [1]獲取數據庫連接Connection對象

      [2]取消事務的自動提交

      [3]執行操作

      [4]正常完成操作時手動提交事務

      [5]執行失敗時回滾事務

      [6]關閉相關資源

使用原生的JDBC API實現事務管理是所有事務管理方式的基石,同時也是最典型的編程式事務管理。編程式事務管理需要將事務管理代碼嵌入到業務方法中來控制事務的提交和回滾。在使用編程的方式管理事務時,必須在每個事務操作中包含額外的事務管理代碼。相對於核心業務而言,事務管理的代碼顯然屬於非核心業務,如果多個模塊都使用同樣模式的代碼進行事務管理,顯然會造成較大程度的代碼冗餘

3:Spring的聲明時事務

3.1:導包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wkl</groupId>
    <artifactId>Spring03Jdcbshiwu</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--ioc的包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>

        <!--aop的包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>

        <!--aop面向切面通知的包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>

        <!--c3p0的連接池包-->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.1</version>
        </dependency>

        <!--mysql8的驅動-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>

        <!--導入jdbctemplate包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
    </dependencies>

</project>

3.2:配置數據源

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

    <context:component-scan base-package="com.wkl"></context:component-scan>
    <context:property-placeholder location="classpath:jdbcproperties.properties"></context:property-placeholder>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>


</beans>

3.3:寫相關的邏輯操作

邏輯代碼在此不再贅述,無非就是

1:創建dao放入容器

2:創建service放入容器

3:service調dao,執行若干設置jdbc的方法

3.4:配置聲明式事務管理

快速的爲某個方法添加事務:

1)、配置出這個事務管理器讓他工作;

<!--配置事務管理器-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--傳入數據源-->
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>

2)、開啓基於註解的事務

名稱空間
xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd 
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    
<!--2:開啓基於註解的事務控制模式;依賴tx名稱空間  -->
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

3)、給事務方法加@Transactional註解

4:事務管理器的屬性

4.1:timout-超時

超時事務屬性:事務在強制回滾之前可以保持多久。這樣可以防止長期運行的事務佔用資源。

@Transactional(timeout = 3)

4.2:readOnly ---只讀屬性

只讀事務屬性: 表示這個事務只讀取數據但不更新數據, 這樣可以幫助數據庫引擎優化事務。默認爲false

@Transactional(readOnly = true)

4.3:觸發事務回滾異常---noRollbackFor和rollbackFor

事務默認異常回滾機制:

  • 捕獲到RuntimeException(運行時異常)或Error時回滾,
  • 而捕獲到編譯時異常(受檢查異常)不回滾。

@Transactional 註解

[1]rollbackFor屬性:指定默認不會滾,但是遇到時必須進行回滾的異常類型,可以爲多個

[2]noRollbackFor屬性:指定默認回滾,但是遇到時不回滾的異常類型,可以爲多個

實例:

@Transactional(rollbackFor = {FileNotFoundException.class,NullPointerException.class},
                    noRollbackFor = {ArithmeticException.class})

4.4:事務隔離級別---數據庫事務一個讀,一個寫就會發生問題;

1:數據庫事務併發問題;假設現在有兩個事務:Transaction01和Transaction02併發執行。

髒讀---讀到了別人未提交的

      [1]Transaction01將某條記錄的AGE值從20修改爲30。

      [2]Transaction02讀取了Transaction01更新後的值:30。

      [3]Transaction01回滾,AGE值恢復到了20。

      [4]Transaction02讀取到的30就是一個無效的值。

不可重複讀---兩次都得值不一樣

      [1]Transaction01讀取了AGE值爲20。

      [2]Transaction02將AGE值修改爲30。

      [3]Transaction01再次讀取AGE值爲30,和第一次讀取不一致。

幻讀---兩次讀的數據量不一樣

      [1]Transaction01讀取了STUDENT表中的一部分數據。

      [2]Transaction02向STUDENT表中插入了新的行。

      [3]Transaction01讀取了STUDENT表時,多出了一些行。

2:隔離級別;

數據庫系統必須具有隔離併發運行各個事務的能力,使它們不會相互影響,避免各種併發問題。一個事務與其他事務隔離的程度稱爲隔離級別。SQL標準中規定了多種事務隔離級別,不同隔離級別對應不同的干擾程度,隔離級別越高,數據一致性就越好,但併發性越弱。

讀未提交:READ UNCOMMITTED

       允許Transaction01讀取Transaction02未提交的修改。

讀已提交:READ COMMITTED

         要求Transaction01只能讀取Transaction02已提交的修改。但在同一事務中有可能讀到兩次的值;

可重複讀:REPEATABLE READ

         確保Transaction01可以多次從一個字段中讀取到相同的值,即Transaction01執行期間禁止其它事務對這個字段進行更新。

串行化:SERIALIZABLE

         確保Transaction01可以多次從一個表中讀取到相同的行,在Transaction01執行期間,禁止其它事務對這個表進行添加、更新、刪除操作。可以避免任何併發問題,但性能十分低下。

3:各級別解決問題的能力;

 

髒讀

不可重複讀

幻讀

READ UNCOMMITTED(讀未提交)

READ COMMITTED(讀已提交)

REPEATABLE READ(可重複讀)

SERIALIZABLE(串行化)

4:各種數據庫產品對事務隔離級別的支持程度

 

Oracle

MySQL

READ UNCOMMITTED(讀未提交)

×

READ COMMITTED(讀已提交)

REPEATABLE READ(可重複讀)

×

√(默認)

SERIALIZABLE(串行化)

 

5:用mysql演示

6:如果是兩個修改的事務,那麼在數據庫底層就會自動排隊,必須等一個修改完,另一個才執行;

4.5:事務的傳播特性

當事務方法被另一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼續在現有事務中運行,也可能開啓一個新事務,並在自己的事務中運行。

事務的傳播行爲可以由傳播屬性指定。Spring定義了7種類傳播行爲。默認爲REQUIRED

注:事務的傳播特性針對的是內部的事務對於外部事務的反應

 

畫圖說明:

假如TX事務中調用了A和B兩個事務

1:REQUIRED:該事務和他父事務坐一個車,並且事務屬性都是繼承父事務的,不能改變;

2:REQUIRED_NEW:改事務自己開車,不和他父親做一個車;內部新開事務只是爲了不讓外邊影響裏邊的;

5:使用註解聲明事務

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

    <context:component-scan base-package="com.wkl"></context:component-scan>
    <context:property-placeholder location="classpath:jdbcproperties.properties"></context:property-placeholder>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>

    <!--1聲明事務管理器-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--傳入數據源-->
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>

    <!--2:開啓基於註解的事務控制模式;依賴tx名稱空間  -->
    <!--<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>-->

    <!--2:配置Aop代理,攔截需要綁定事務的方法-->
    <aop:config>
        <!--切入短表達式-->
        <aop:pointcut id="myponint" expression="execution(public String com.wkl.BaseService.checkout())"/>
        <!--配置事務建議,事務增強-->
        <aop:advisor advice-ref="transactionInterceptor" pointcut-ref="myponint"></aop:advisor>
    </aop:config>

    <!--3:配置事務管理器 ,transaction-manager指定配置那個事務管理器-->
    <tx:advice id="transactionInterceptor" transaction-manager="dataSourceTransactionManager">
        <!--事務屬性-->
        <tx:attributes>
            <!--指明那些方法是事務方法-->
            <tx:method name="checkout" timeout="-1"/>
        </tx:attributes>
    </tx:advice>


</beans>

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章