Apache出品的極爲輕量級的Jdbc訪問框架,核心類只有兩個:QueryRunner和ResultSetHandler。
各種ResultSetHandler:
ArrayHandler:把結果集中的第一行數據轉成對象數組。
ArrayListHandler:把結果集中的每一行數據都轉成一個數組,再存放到List中。
BeanListHandler:將結果集中的每一行數據都封裝到一個對應的JavaBean實例中,存放到List裏。html
請參考孤傲蒼狼的博客javaweb學習總結(四十一)——Apache的DBUtils框架學習,牆裂推薦。java
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
看上去還湊合,調用比較簡單,那麼接下來看看具體的實現吧!數據庫
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); } }
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; } }
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對象。數組