1. 程式人生 > 實用技巧 >[Re] Spring-3(JdbcTemplate)

[Re] Spring-3(JdbcTemplate)

Spring 提供了 JdbcTemplate 以便捷地操作 DB。

CRUD 測試

導包

配置檔案

<!-- 
DAO 層元件自動裝配 JdbcTemplate:
    @Repository
    public class EmpDao {
        @Autowired
        JdbcTemplate jdbcTemplate;
    }
-->
<context:component-scan base-package="cn.edu.nuist"></context:component-scan>

<!-- 引用外部屬性檔案;"classpath:" 固定寫法,引入類路徑下資源 -->
<context:property-placeholder location="classpath:dbconfig.properties"/>

<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.jdbcUrl}"></property>
    <property name="driverClass" value="${jdbc.driverClass}"></property>
</bean>

<!-- Spring 提供了一個類 JdbcTemplate,使用它來操作 DB -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>

<!-- 配置一個具有 ‘具名引數’ 功能的 JdbcTemplate -->
<bean id="namedParameterJdbcTemplate"
        class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
    <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>

測試

public class TestJDBC {
    ApplicationContext ioc = new 
            ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);
    NamedParameterJdbcTemplate namedParamJdbcTemplate = 
            ioc.getBean(NamedParameterJdbcTemplate.class);

    // 8. 建立 EmpDao,自動裝配 JdbcTemplate 物件
    @Test
    public void testEmpDao() {
        EmpDao empDao = ioc.getBean(EmpDao.class);
        Employee emp = new Employee();
        emp.setEmpName("吳九");
        emp.setSalary(5000);
        empDao.saveEmp(emp);
        empDao.saveEmp(emp);
    }

    // 7. 以SqlParameterSource形式傳入引數值
    public void testSqlParameterSource() {
        String sql = "INSERT INTO employees(emp_name, salary) VALUES(:empName, :salary)";
        Employee emp = new Employee();
        emp.setEmpName("崔八");
        emp.setSalary(5000);
        // SqlParameterSource BeanPropertySqlParameterSource
        int update = namedParamJdbcTemplate.update(sql
                , new BeanPropertySqlParameterSource(emp));
        System.out.println(update);
    }

    /*
    6. 使用帶有具名引數的 SQL 語句插入一條員工記錄,並以 Map 形式傳入引數值
        佔位符引數:? 的順序不能亂,傳參的時候要注意
        具名引數:具有名字的引數,引數不是佔位符了,而是一個變數名
            語法格式 [:引數名]
            Spring 有一個支援具名引數功能的JdbcTemplate: NamedParameterJdbcTemplate
     */
    public void insertWithMap() {
        String sql = "INSERT INTO employees(emp_name, salary) VALUES(:empName, :salary)";
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("empName", "田七");
        paramMap.put("salary", 5000);
        int update = namedParamJdbcTemplate.update(sql, paramMap);
        System.out.println(update);
    }

    // 5. 更新資料
    public void testUpdate() {
        String sql = "UPDATE employees SET salary = ? WHERE emp_id = ?";
        int update = jdbcTemplate.update(sql, 4000, 4);
        System.out.println(update);
    }

    // 4. 批量插入資料
    public void testBatchAdd() {
        String sql = "INSERT INTO employees(emp_name, salary) VALUES(?, ?)";
        // List<Object[]>
        // List.size: SQL 語句要執行的次數
        // Object[]: 每次執行要用的引數
        List<Object[]> batchArgs = new ArrayList<>();
        batchArgs.add(new Object[] {"張三", 3000});
        batchArgs.add(new Object[] {"李四", 3000});
        batchArgs.add(new Object[] {"王五", 3000});
        batchArgs.add(new Object[] {"趙六", 3000});
        int[] updates = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(updates));
    }

    // 3. 查詢單個數據
    public void queryMaxSalary() {
        String sql = "SELECT MAX(salary) FROM employees";
        int maxSalary = jdbcTemplate.queryForObject(sql, Integer.class);
        System.out.println(maxSalary);
    }

    /*
     * JavaBean 需要和 DB 中表的欄位名一致,否則無法完成封裝
     * JdbcTemplate 在方法級別進行了區分
     *  > 查詢單個物件:template.queryForObject(),如果查詢不到就會拋異常
     *  > 查詢集合:template.query()
     */

    // 2. 查詢多條記錄,封裝到 List
    public void queryMore() {
        String sql = "SELECT emp_id empId, emp_name empName, salary"
                + " FROM employees WHERE salary > ?";
        // 封裝 List
        List<Employee> emps = jdbcTemplate.query(sql
                , new BeanPropertyRowMapper<>(Employee.class), 3000);
        System.out.println(emps);
    }

    // 1. 查詢 1 條記錄,封裝到 JavaBean
    public void queryOne() {
        String sql = "SELECT emp_id empId, emp_name empName, salary"
            + " FROM employees WHERE emp_id = ?";
        Employee emp = jdbcTemplate.queryForObject(sql
                , new BeanPropertyRowMapper<>(Employee.class), 40);
        System.out.println(emp);
    }
}

宣告式事務

事務管理程式碼的固定模式作為一種橫切關注點,可以通過 AOP 方法模組化,進而藉助 Spring AOP 框架實現宣告式事務管理。

這個事務管理器就可以在目標方法執行前後進行事務控制(事務切面)。我們目前使用 DataSourceTransactionManager 即可。

配置檔案

<!-- 事務控制-->
<!-- 1. 配置事務管理器讓其進行事務管理 -->
<bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 控制住資料來源 (控制事務實際就是控制住Connection) -->
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 2. 開啟基於註解的事務控制模式(依賴 tx 名稱空間), 預設 transactionManager -->
<tx:annotation-driven transaction-manager="transactionManager"/>

<!-- 3. 給事務方法加 @Transactional → 環繞通知Around -->

@Transactional

propagation

事務的傳播行為(事務的傳播 + 事務的行為),如果有多個事務進行巢狀執行,子事務是否和大事務共用一個事務(使用同一條連線)。

  • 如果是 REQUIRED,則子事務的屬性都是繼承自大事務。
  • REQUIRED 是將之前事務用的 Connection 傳遞過來
  • REQUIRES_NEW 直接使用新的 Connection

isolation

事務的隔離級別。

  • READ_UNCOMMITTED
  • READ_COMMITTED
  • REPEATABLE_READ
  • SERIALIZABLE

timeout

  • 超時屬性
  • 事務超出指定時長(秒為單位) 後自動終止並回滾

readOnly

  • 預設 false
  • 可以進行事務優化
  • 能加快查詢速度,不用管事務那堆操作了
  • 如果設定為 true 後,做非查詢操作,會拋異常

rollback 相關

  • 異常分類
    • 執行時異常(非檢查異常):可以不用處理,預設都回滾
    • 編譯時異常(檢查異常):預設不回滾
  • 回滾相關屬性
    • rollbackFor:出現哪些異常,事務可以不回滾
    • rollbackForClassName
    • noRollbackFor:原來不回滾的異常,指定讓其回滾
    • noRollbackForClassName

基於 XML 配置的事務

  1. 依賴 tx、aop 名稱空間
  2. Spring 中提供事務管理器(事務切面),配置這個事務管理器
  3. 配置事務方法
  4. 告訴 Spring 哪些方法是事務方法 (事務切面按照我們的切入點表示式去切入事務方法)
<aop:config>
    <aop:pointcut id="txPoint" expression="execution(* com.atguigu.ser*.*.*(..))"/>
    <!-- 事務建議;事務增強     advice-ref:指向事務管理器的配置 -->
    <aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"/>
</aop:config>

<tx:advice id="myAdvice" transaction-manager="transactionManager">
    <!-- 事務屬性 -->
    <tx:attributes>
        <!-- 指明哪些方法是事務方法:
        切入點表示式只是說,事務管理器要切入這些方法,
        至於哪些方法加事務,還需要使用 tx:method 來指定
        -->
        <tx:method name="*"/>
        <tx:method name="checkout" propagation="REQUIRED" timeout="-1"/>
        <tx:method name="get*" read-only="true"/>
    </tx:attributes>
</tx:advice>

<!-- 都用;重要的用配置,不重要的用註解 -->