Learn Spring – Spring DAO

天歌發表於2017-02-08

1. 概念

  1. Spring的DAO異常體系建立在執行期異常的基礎上,封裝了源異常

  2. JDBC資料訪問流程:

    • 準備資源

    • 啟動事務

    • 在事務中執行具體資料訪問操作

    • 提交/回滾事務

    • 關閉資源,處理異常

  3. Spring將相同的資料訪問流程固化到模板類中,把資料訪問中固定和變化的部分分開,同時保證模板類是執行緒安全的。Spring為不同的持久化技術都提供了簡化操作的模板和回撥

  4. 資料庫事務:原子性,一致性,隔離性和永續性(ACID)

  5. 5類資料庫併發問題:

    • 髒讀:A事務讀取到B事務尚未提交的資料

    • 不可重複讀:A事務中讀取到B事務已經提交的==更新==資料,即連續兩次讀取結果不同

    • 幻讀:A事務讀取B事務的==新增==資料

    • 第一類更新丟失:A事務撤銷時覆蓋了B事務的提交

    • 第二類更新丟失:A事務覆蓋B事務已經提交的資料

  6. JDBC預設情況下自動提交,即每條執行的SQL語句都對應一個事務,AutoCommit = TRUE

  7. Spring基於ThreadLocal解決有狀態的Connetion的併發問題,事務同步管理器org.springframework.transaction.support.TransactionSynchronizationManager使用ThreadLocal為不同事務執行緒提供獨立的資源副本

  8. Spring事務管理基於3個介面:TransactionDefinitionTransactionStatusPlatformTransactionManager

  9. Spring為不同持久化技術提供了從TransactionSynchronizationManager獲取對應執行緒繫結資源的工具類,如DataSourceUtils.getConnection(DataSource dataSource)。模板類在內部通過工具類訪問TransactionSynchronizationManager中的執行緒繫結資源

  10. Spring通過事務傳播行為控制當前的事務如何傳播到被巢狀呼叫的目標服務介面方法中

  11. 使用<tx:annotation-driven transaction-manager="txManager">對標註@Transactional註解的bean進行加工處理,織入事務管理切面

  12. @Transactional註解的屬性

    • 事務傳播行為:propagation,預設PROPAGATION_REQUIRED,即如果當前沒有事務,就新建一個事務;否則加入到當前事務

    • 事務隔離級別:isolation,預設ISOLATION_DEFAULT

    • 讀寫事務屬性:readOnly

    • 超時時間:timeout

    • 回滾設定:rollbackForrollbackForClassNamenoRollbackFornoRollbackForClassName

  13. 在相同執行緒中進行相互巢狀呼叫的事務方法工作於相同的事務中;如果在不同執行緒中,則工作在獨立的事務中

  14. 特殊方法:

    • 註解不能被繼承,所以業務介面中的@Transactional註解不會被業務實現類繼承;方法處的註解會覆蓋類定義處的註解

    • 對於基於介面動態代理的AOP事務,由於介面方法都是public的,實現類的實現方法必須是public的,同時不能使用static修飾符。因此,可以通過介面動態代理實施AOP增強、實現Spring事務的方法只能是publicpublic final

    • 基於CGLib動態代理實施AOP的時候,由於使用finalstaticprivate的方法不能被子類覆蓋,相應的,這些方法不能實施AOP增強,實現事務

    • 不能被Spring進行AOP事務增強的方法不能啟動事務,但是外層方法的事務上下文仍然可以傳播到這些方法中

2. Spring中使用JDBC程式設計示例

  • 本地mysql建表

CREATE TABLE `t_user` (
  `user_id` varchar(256) NOT NULL,
  `user_name` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
  • springDAO.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:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:p="http://www.springframework.org/schema/p"
    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.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd ">

    <context:component-scan base-package="com.dao" />

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close"
        p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://localhost:3306/sampledb"
        p:username="root"
        p:password="123123" />

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
        p:dataSource-ref="dataSource" />

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
        p:dataSource-ref="dataSource" />
</beans>
  • User

package com.data;

public class User {

    private String userId;

    private String userName;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

}
  • BaseDAO

package com.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;

public class BaseDAO {

    @Autowired
    protected JdbcTemplate jdbcTemplate;
}
  • UserDAO

package com.dao;

import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.data.User;
import com.mapper.UserRowMapper;

@Repository
public class UserDAO extends BaseDAO {

    private static final String SQL_GET_USER = "select * from t_user where " + "user_id = ?;";

    private static final String SQL_INSERT_USER = "insert into t_user values(?, ?);";

    private static final String SQL_CLEAN_USER = "delete from t_user where 1=1;";

    @Transactional
    public User getUserById(String userId) {
    return jdbcTemplate.queryForObject(SQL_GET_USER, new Object[]{userId}, new UserRowMapper());
    }

    @Transactional
    public int insertUser(User user) {
        return jdbcTemplate.update(SQL_INSERT_USER, user.getUserId(), user.getUserName());
    }

    @Transactional
    public int cleanUser() {
        return jdbcTemplate.update(SQL_CLEAN_USER);
    }
}
  • UserRowMapper

package com.dao;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

import com.data.User;

public class UserRowMapper implements RowMapper<User>{

    public User mapRow(ResultSet rs, int rowNumber) throws SQLException {
        User user = new User();
        user.setUserId(rs.getString("user_id"));
        user.setUserName(rs.getString("user_name"));
        return user;
    }
}
  • BaseTestCase

package com;

import org.junit.Assert;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/springDAO.xml"})
public class BaseTestCase extends Assert {

}
  • TestUserDAO

package com.dao;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import com.BaseTestCase;
import com.data.User;

public class TestUserDAO extends BaseTestCase{

    @Before
    @After
    public void clean() {
        dao.cleanUser();
    }

    @Autowired
    private UserDAO dao;

    @Test
    public void getUserById() {
        User user = new User();
        String id = "id";
        String name = "name";
        user.setUserId(id);
        user.setUserName(name);
        assertEquals(dao.insertUser(user), 1);

        user = dao.getUserById(id);
        assertEquals(user.getUserId(), id);
        assertEquals(user.getUserName(), name);
    }
}

相關文章