輕量級封裝DbUtils&Mybatis之二Dbutils

DbUtils入門

Apache出品的極爲輕量級的Jdbc訪問框架,核心類只有兩個:QueryRunner和ResultSetHandler。
各種ResultSetHandler:
ArrayHandler:把結果集中的第一行數據轉成對象數組。
ArrayListHandler:把結果集中的每一行數據都轉成一個數組,再存放到List中。
BeanListHandler:將結果集中的每一行數據都封裝到一個對應的JavaBean實例中,存放到List裏。html

請參考孤傲蒼狼的博客javaweb學習總結(四十一)——Apache的DBUtils框架學習,牆裂推薦。java

Detail

測試樣例代碼

package org.wit.ff.jdbc;


import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.wit.ff.jdbc.access.IDataAccessor;
import org.wit.ff.jdbc.converter.ParamsConverter;
import org.wit.ff.jdbc.sql.db.MySQLBuilder;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by F.Fang on 2015/3/31.
 * Version :2015/3/31
 */
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class CRUDTest extends AbstractJUnit4SpringContextTests{

    @Autowired
    private IDataAccessor dataAccessor;

    @Test
    public void query(){
        MySQLBuilder builder = new MySQLBuilder();
        // sql: select * from Audience limit 2
        builder.SELECT("*").FROM("Audience").PAGE(0, 2);
        String sql = builder.toString();
        final Audience audience = new Audience();
        audience.setName("ff");

        // 也能夠直接填寫SQL.
        List<Audience> list = dataAccessor.query(sql, null, Audience.class);
        System.out.println(list);
    }

    @Test
    public void insert(){
        // 批量新增.
        String sql = "insert into audience(id,name,pay) values(?,?,?)";
        Object[][] params = new Object[1][];
        params[0] = new Object[]{100,"ff",100.1};
        dataAccessor.insert(sql, params);
    }

    @Test
    public void insertDate(){
        // 試試日期.
        String sql = "insert into test_date(id,current) values(?,?)";
        Object[][] params = new Object[1][];
        params[0] = new Object[]{100, new Date()};
        dataAccessor.insert(sql, params);
    }

    @Test
    public void insertWithParamsConverter(){
        // 試試PramsConverter好用不.
        ParamsConverter<Audience> converter = new ParamsConverter<Audience>() {
            @Override
            public Object[] convert(Audience audience) {
                return new Object[]{audience.getId(), audience.getName(), audience.getPay()};
            }
        };

        String sql = "insert into audience(id,name,pay) values(?,?,?)";
        // 構造數據.
        List<Audience> list = new ArrayList<>();
        Audience audience1 = new Audience();
        audience1.setId(250);
        audience1.setName("ff");
        audience1.setPay(1000.00);

        Audience audience2 = new Audience();
        audience2.setId(251);
        audience2.setName("ff1");
        audience2.setPay(1000.00);

        list.add(audience1);
        list.add(audience2);

        dataAccessor.insert(sql, list, Audience.class, converter);
        
    }

}

Audienceweb

package org.wit.ff.jdbc;

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

/**
 * Created by F.Fang on 2015/2/16.
 * Version :2015/2/16
 */
public class Audience {
    private Integer id;

    private String name;

    private Double pay;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getPay() {
        return pay;
    }

    public void setPay(Double pay) {
        this.pay = pay;
    }

    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this, ToStringStyle.SIMPLE_STYLE);
    }
}

配置spring

<!-- 數據庫鏈接池 -->
    <bean id="dataSource"
          class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close">
        <property name="driverClassName" value="${db.driverClass}"/>
        <property name="url" value="${db.jdbcUrl}"/>
        <property name="username" value="${db.user}"/>
        <property name="password" value="${db.password}"/>
    </bean>

    <bean id="dataAccessor" class="org.wit.ff.jdbc.access.dbutils.DefaultDataAccessor">
        <property name="dataSource" ref="dataSource"/>
    </bean>

備註:實際數據源配置仍是須要優化的sql

看上去還湊合,調用比較簡單,那麼接下來看看具體的實現吧!數據庫

AbstractDataAccessor

  • QueryRunner無參構造對象
  • 抽象獲取鏈接和關閉鏈接的方法
package org.wit.ff.jdbc.access.dbutils;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.*;
import org.apache.commons.lang3.StringUtils;
import org.wit.ff.jdbc.converter.ParamsConverter;
import org.wit.ff.jdbc.access.IDataAccessor;
import org.wit.ff.jdbc.exception.DbUtilsDataAccessException;
import org.wit.ff.jdbc.id.IdGenerator;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

/**
 * Created by F.Fang on 2015/3/31.
 * Version :2015/3/31
 */
public abstract class AbstractDataAccessor implements IDataAccessor {

    /**
     * 獲取鏈接的方法時抽象的,目的是爲了和事務綁定,開放Connection來源.
     * @return
     * @throws SQLException
     */
    protected abstract Connection getConnection() throws SQLException;

    /**
     * 獲取鏈接的方法時抽象的,目的是爲了和事務綁定,由於事務執行先後包含的sql邏輯可能不僅一條, 不可在一條sql執行邏輯完成後關閉.
     * @param conn
     */
    protected abstract void closeConn(Connection conn);

    @Override
    public <T> List<T> query(String sql, Class<T> resultType){
        return query(sql, null, resultType);
    }

    /**
     * 查詢時BeanListHandler將結果集轉換成對象列表.
     * @param sql 查詢語句
     * @param params 查詢參數
     * @param resultType 返回類型
     * @param <T>
     * @return
     */
    @Override
    public <T> List<T> query(String sql, Object[] params, Class<T> resultType) {
        if (resultType == null || StringUtils.isBlank(sql)) {
            throw new DbUtilsDataAccessException("resultType can't be null and sql can't be blank!");
        }
        QueryRunner runner = new QueryRunner();
        Connection conn = null;
        List<T> result = null;
        try {
            conn = getConnection();
            if (params != null) {
                result = (List<T>) runner.query(conn, sql, new BeanListHandler(resultType), params);
            } else {
                result = (List<T>) runner.query(conn, sql, new BeanListHandler(resultType));
            }
        } catch (Exception e) {
            throw new DbUtilsDataAccessException("query error, sql is:" + sql, e);
        } finally {
            closeConn(conn);
        }
        return result;
    }

    /**
     *
     * @param sql insert語句.
     * @param params 對象參數列表.
     * @param paramsType 參數類型.
     * @param converter 參數轉換器.
     * @param <T>
     */
    @Override
    public <T> void insert(String sql, List<T> params, Class<T> paramsType, ParamsConverter<T> converter) {
        if (StringUtils.isBlank(sql)) {
            throw new DbUtilsDataAccessException("sql can't be blank!");
        }
        if (params == null || params.isEmpty()) {
            throw new DbUtilsDataAccessException("params can't be null or empty!");
        }
        if (paramsType == null || converter == null) {
            throw new DbUtilsDataAccessException("paramsType or converter can't be null!");
        }

        QueryRunner runner = new QueryRunner();
        Connection conn = null;
        ArrayListHandler handler = new ArrayListHandler();
        List<Object[]> keys = null;
        try {
            conn = getConnection();
            int len = params.size();
            Object[][] arr = new Object[len][];
            for (int i = 0; i < len; ++i) {
                // 將單個對象處理成單行記錄,用數組表示.
                arr[i] = converter.convert(params.get(i));
            }
            // 獲取主鍵.
            keys = (List<Object[]>) runner.insertBatch(conn, sql, handler, arr);
            if (keys != null && keys.size() == len) {
                // implements IdGenerator.
                // 若是當前對象實現了IdGenerator接口, 則執行主鍵賦值的邏輯, 賦值邏輯由用戶自定義在具體的對象類型當中
                if (params.get(0) instanceof IdGenerator) {
                    for (int i = 0; i < len; ++i) {
                        // 對每個對象執行主鍵賦值(解析)
                        ((IdGenerator) params.get(i)).parseGenKey(keys.get(i));
                    }
                }
            }
        } catch (Exception e) {
            throw new DbUtilsDataAccessException("batch insert error, sql is:" + sql, e);
        }finally {
            closeConn(conn);
        }
    }

    /**
     * 批量插入.
     * @param sql
     * @param params
     */
    @Override
    public void insert(String sql, Object[][] params) {
        if(params==null || StringUtils.isBlank(sql)){
            throw new DbUtilsDataAccessException("params can't be null and sql can't be empty!");
        }
        QueryRunner runner = new QueryRunner();
        Connection conn = null;
        ArrayListHandler handler = new ArrayListHandler();
        try {
            conn = getConnection();
            runner.insertBatch(conn, sql, handler, params);
        } catch (Exception e) {
            throw new DbUtilsDataAccessException("batch insert error, sql is:" + sql, e);
        }finally {
            closeConn(conn);
        }
    }

    /**
     * 單條插入.
     * @param sql
     * @param params
     */
    @Override
    public void insert(String sql, Object[] params) {
        if(params==null || StringUtils.isBlank(sql)){
            throw new DbUtilsDataAccessException("params can't be null and sql can't be empty!");
        }
        QueryRunner runner = new QueryRunner();
        Connection conn = null;
        ArrayHandler handler = new ArrayHandler();
        try {
            conn = getConnection();
            runner.insert(conn, sql, handler, params);
        } catch (Exception e) {
            throw new DbUtilsDataAccessException("insert error, sql is:" + sql, e);
        }finally {
            closeConn(conn);
        }
    }

    /**
     * 批量更新.
     * @param sql
     * @param params
     * @return
     */
    @Override
    public int[] update(String sql, Object[][] params) {
        if(params==null || StringUtils.isBlank(sql)){
            throw new DbUtilsDataAccessException("params can't be null and sql can't be empty!");
        }
        QueryRunner runner = new QueryRunner();
        Connection conn = null;
        int[] result = null;
        try {
            conn = getConnection();
            result = runner.batch(conn, sql, params);
        } catch (Exception e) {
            throw new DbUtilsDataAccessException("batch update error, sql is:" + sql, e);
        }finally {
            closeConn(conn);
        }
        return result;
    }

    /**
     * 單條更新.
     * @param sql
     * @param params
     * @return
     */
    @Override
    public int update(String sql, Object[] params) {
        QueryRunner runner = new QueryRunner();
        Connection conn = null;
        int result = 0;
        try {
            conn = getConnection();
            if (params != null) {
                result = runner.update(conn, sql, params);
            } else {
                result = runner.update(conn, sql);
            }
        } catch (Exception e) {
            throw new DbUtilsDataAccessException("update error, sql is:" + sql, e);
        }finally {
            closeConn(conn);
        }
        return result;
    }

    /**
     * 刪除,沒有必要批量.
     * 即便是批量,也能夠調用批量更新的方法.
     * 事實上此方法並沒有太大必要,只是爲了不歧義而已.
     * @param sql
     * @param params
     * @return
     */
    @Override
    public int delete(String sql, Object[] params) {
        return update(sql, params);
    }

}

DefaultDataAccessor

package org.wit.ff.jdbc.access.dbutils;

import org.apache.commons.dbutils.DbUtils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * Created by F.Fang on 2015/3/31.
 * 默認以配置數據源的方式獲取鏈接.
 * Version :2015/3/31
 */
public class DefaultDataAccessor extends AbstractDataAccessor {

    private DataSource dataSource;

    protected Connection getConnection() throws SQLException{
        return dataSource.getConnection();
    }

    @Override
    protected void closeConn(Connection conn) {
        try {
            DbUtils.close(conn);
        } catch (SQLException e) {
            // do nothing!
        }
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

DefaultTransactionDataAccessor

package org.wit.ff.jdbc.access.dbutils;

import org.springframework.jdbc.datasource.DataSourceUtils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * Created by F.Fang on 2015/4/23.
 * Version :2015/4/23
 */
public class DefaultTransactionDataAccessor extends AbstractDataAccessor {

    private DataSource dataSource;

    protected Connection getConnection() throws SQLException {

        return DataSourceUtils.getConnection(dataSource);
        // 雖然能夠將鏈接綁定到事務,可是當外部循dataAccessor的方法時,循環調用時會產生多個鏈接.
        //return DataSourceUtils.getConnection(dataSource);
        // 如下寫法沒法解決事務問題,沒法將鏈接綁定到Spring 事務.
        // return dataSource.getConnection();
    }

    @Override
    protected void closeConn(Connection conn) {
        // do nothing!
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

採用Spring事務集成方案,將Connection交給Spring管理,實際上Spring將Connection綁定到當前執行線程當中(以ConnectionHolder包裝Connection,ThreadLocal包裝ConnectionHolder綁定線程)
詳情參考DataSourceTransactionMananger的源代碼,最好在doBeign和doCleanupAfterCompletion執行斷點調試apache

備註:AbstractDataAccessor開放getConnection()和closeConnection方法的緣由也是調用過程Connection的開啓和關閉不能由自身維持,不然會致使事務處理時Spring持有的Connection和AbstractDataAccessor執行方法的Connection不一致或在執行時拿到了關閉了的Connection對象數組

QA

相關文章
相關標籤/搜索