spring jdbc及事務管理


Spring提供了一個jdbc模板,它類似於dbutils工具。

快速入門

創建數據庫

CREATE DATABASE springtest;

USE springtest;

CREATE TABLE t_user(
    
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(20),
    age INT,
    sex VARCHAR(20)
)

INSERT INTO t_user VALUES(NULL,'tom',20,'');
INSERT INTO t_user VALUES(NULL,'fox',30,'');
INSERT INTO t_user VALUES(NULL,'tony',40,'');    

SELECT * FROM t_user;

C3P0開源連接池配置

  

<!-- 配置連接池對象 -->
    <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="jdbc:mysql:///spring"></property>
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

為了便於修改
引入外部屬性文件,在src下的db.properties文件

jdbc.jdbcUrl=jdbc:mysql:///spring
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=root

在applicationContext.xml文件中引入

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

所以C3P0連接池的引入可以改為

<!-- 配置c3p0連接池 -->
    <bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

在自己配置中需要從properties文件中引入的信息可以使用${name}方式來獲取

 

JdbcTemplate CRUD

執行insert update delete操作

只需要使用JdbcTemplate的update方法就可以執行insert update delete操作

@Autowired
    @Qualifier("jdbcTemplate")
    private JdbcTemplate template;

    @Test
    public void test1() {
        // update執行update操作
        template.update("update t_user set name = ? where id = ?", "猥瑣逼", 1);
    }

    @Test
    public void test2() {
        // update執行update操作
        template.update("insert into t_user values(null,?,?,?)", "龜哥", 25, "男");
    }

    @Test
    public void test3() {
        // update執行delete操作
        template.update("delete from t_user where id = ?", 4);
    }

執行select操作

簡單數據返回

/*
     * 下面測試查詢語句返回簡單的數據類型
     */
    @Test
    public void test4() {
        String name = template.queryForObject("select name from t_user where id = 1", String.class);
        System.out.println(name);
    }

    @Test
    public void test5() {
        Integer count = template.queryForObject("select count(*) from t_user", Integer.class);
        System.out.println(count);
    }

復雜數據返回

/*
     * 下面測試復雜返回類型
     */
    @Test
    public void test6() {
        // 如果返回的是一個對象,可以使用queryForObject方法
        User user = template.queryForObject("select * from t_user where id = ?", new RowMapper<User>() {

            @Override
            public User mapRow(ResultSet rs, int arg1) throws SQLException {
                User user = new User();
                user.setId(rs.getInt("id"));
                user.setName(rs.getString("name"));
                user.setAge(rs.getInt("age"));
                user.setSex(rs.getString("sex"));
                return user;
            }
        },1);
        System.out.println(user);
    }
    
    @Test
    public void test7() {
        // 如果返回的是一個集合,使用query方法
        List<User> list = template.query("select * from t_user", new RowMapper<User>() {

            @Override
            public User mapRow(ResultSet rs, int arg1) throws SQLException {
                User user = new User();
                user.setId(rs.getInt("id"));
                user.setName(rs.getString("name"));
                user.setAge(rs.getInt("age"));
                user.setSex(rs.getString("sex"));
                return user;
            }
        });
        System.out.println(list);
    }


注意:如果只返回一個domain對象,可以使用queryForObject方法,如果返回的是List<?>對象,
可以使用query方法,但是都需要使用RowMapper來對ResultSet進行處理。

RowMapper它有一個實現類叫BeanPropertyRowMapper
如果使用BeanPropertyRowmapper,實體類必須提供一個無參數的public構造方法,類中的bean屬性名稱與表中的列要對應

@Test
    public void test8() {
        // 使用rowMapper的實現類BeanPropertyRowMapper
        /*List<User> list = template.query("select * from t_user", new BeanPropertyRowMapper<User>(User.class));
        System.out.println(list);*/
        
        List<User> user = template.query("select * from t_user where id = 1", new BeanPropertyRowMapper<User>(User.class));
        System.out.println(user);
    }

spring的事務管理

下面通過一個轉賬案例來了解spri

創建數據庫表

CREATE TABLE account(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(20),
    money DOUBLE
)

INSERT INTO account VALUES(NULL,'tom',1000);
INSERT INTO account VALUES(NULL,'fox',1000);

 

創建service於dao

@Service("accountService")
public class AccountServiceImpl implements AccountService{
    @Autowired
    @Qualifier("accountDao")
    private AccountDaoImpl ad;
    @Override
    public void account(String outname, String inname, double money) {
        ad.accountIn(inname,money);
        //int a = 10 /0;
        ad.accountOut(outname,money);
    }
}
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao{
    @Autowired
    @Qualifier("jdbcTemplate")
    private JdbcTemplate temple;
    @Override
    public void accountIn(String inname, double money) {
        temple.update("update account set money = money + ? where name = ?",money,inname);
    }

    @Override
    public void accountOut(String outname, double money) {
        temple.update("update account set money = money - ? where name = ?",money,outname);
    }
}

不要忘了在applicationContext中配置開啟注解

<!-- 開啟注解配置 -->
    <context:component-scan base-package="com.learn"></context:component-scan>

如果打開//int a = 10 /0;

就一定會排出異常
這時我們就需要事務控制,讓事務具有一致性

 

Spring事務管理主要提供了三個接口來完成

1. org.springframework.transaction.PlatformTransactionManager
這是一個事務管理器,可以來選擇相關的平台(jdbc hibernate jpa…)
2. TransactionDefinition
它定義事務的一些相關信息 例如 隔離 傳播 超時 只讀
3. TransactionStatus
它主要描述事務具體的運行狀態

 

PlatformTransactionManager:
  平台事務管理器,在不同的持久化層解決技術它的事務代碼不一樣。
  DataSourceTransactionManager 主要針對於JdbcTemplate開發 MyBatis開發
  HibernateTransactionManasger主要針對於Hibernate開發
  JpaTransactionManager 主要針對於JPA開發。

TransactionDefinition:
  它描述的是事務的定義信息。
  在TransactionDefinition中定義了大量的常量
  事務的四個特性 ACID 原子性 一致性 隔離性 持久性。
  不考慮事務隔離性會出現:臟讀,不可重復讀 虛讀。
  ISOLATION_DEFUALT 它使用后端數據庫的默認隔離級別(spring中選項)
  ISOLATION_READ_UNCOMMITTED 不能解決問題,會發生臟讀 不可重復讀 虛讀
  ISOLATION_READ_COMMITTED 可以解決臟讀 會產生不可重復讀與虛讀。
  ISOLATION_REPEATABLE_READ 可以解決臟讀,不可重復讀 解決不了虛讀
  ISOLATION_SERIALIZABLE 串行化,可以解決所有問題
  對於不現的數據庫,它的底層默認事務隔離級別不一樣。
  Oracle數據庫它默認的是read_committed
  Mysql數據庫它默認的是repeatable_read.

  下面再介紹一下它的傳播特性:

    它解決的是兩個被事務管理的方法互相調用問題。它與數據庫沒關系,是程序內部維護的問題。

  最常用的三種:  

    PROPAGATION_REQUIRED 默認值 兩個操作處於同一個事務,如果之前沒有事務,新建一個事務
    PROPAGATION_REQUIRES_NEW,兩個操作處於不同的事務
    PROPAGATION_NESTED,它是一種嵌套事務,它是使用SavePoint來實現的。

事務回滾時可以回滾到指定的savepoint,注意:它只對DataSourceTransactionManager有作用

 

TransactionStatus:

它定義了事務狀態信息,在事務運行過程中,得到某個時間點的狀態

事務管理方式

  1. 編碼方案 不建議使用,它具有侵入性。在原有的業務代碼基礎上去添加事務管理代碼
  2. 聲明式事務控制,基於AOP對目標進行代理,添加around環繞通知。
這種方案,它不具有侵入性,不需要修改原來的業務代碼

 

基於xml配置聲明式事務管理方案

<!-- 配置開啟事務 -->
    <!-- 配置事務管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="c3p0DataSource"></property>
    </bean>
    <!-- 配置通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- name:必須的,對哪些方法進行事務控制 isolation 可選 設置事務隔離級別 默認是DEFAULT propagation:可選 
                設置事務傳播 默認值 REQUIRED timeout 可選 超時時間 默認值-1 read-only 可選 默認值是false 如果不是只讀,它可以對insert 
                update delete操作,如果是只讀不可以。 rollback-for 可選 可以設置一個異常,如果產生這個異常,觸發事務回滾 no-rolback-for 
                可選 可以設置一個異常,如果產生這個異常,不會觸發事務回滾 -->
            <tx:method name="account" />

        </tx:attributes>
    </tx:advice>
    <!-- 配置切面 -->
    <aop:config>
        <aop:pointcut
            expression="execution(* com.learn.service.AccountService.account(..))"
            id="txPointcut" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
    </aop:config>

基於annotation聲明式事務管理方案

<!-- 開啟注解配置事務 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

在需要開啟的類或方法上聲明

@Transactional

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM