簡單JDBC封裝類

fmms 12年前發布 | 55K 次閱讀 JDBC Java開發

為什么要做這個封裝類:

以前經常使用Hibernate來操作數據庫,但是時間長了以后發現,Hibernate的大多數功能其實我根本就用不上,而且,Hibernate的查詢執行效率的確很讓人糾結
所以,我又回到了JDBC的懷抱,而我又是個懶人,喜歡一個函數就能直接調用的那種,JDBC本身方法對我來說,要打的字太多了...@_@

 

入正題:

首先,畫一個結構的草圖

簡單JDBC封裝類

 

包結構:

簡單JDBC封裝類

先貼代碼:

1.IConnectionProvider 接口

package org.sol.util.db;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * 用于獲取連接的接口
 * @author SOL
 *
 */
public interface IConnectionProvider {
    public Connection getConnection(String sourceName) throws SQLException;
}
2.JDBCProvider JDBC數據提供源
package org.sol.util.db;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * 標準JDBC連接獲取代理
 * @author HUYAO
 *
 */
public class JdbcProvider implements IConnectionProvider {
    private String DBDriver;
    private String DBUrl;
    private String username;
    private String password;

    public JdbcProvider(String DBDriver,String DBUrl,String username,String password) throws ClassNotFoundException {
        this.DBDriver = DBDriver;
        this.DBUrl = DBUrl;
        this.username = username;
        this.password = password;

        Class.forName(DBDriver);
    }

    @Override
    public Connection getConnection(String sourceName) throws SQLException {
        return DriverManager.getConnection(DBUrl + ";DatabaseName=" + sourceName,username,password);
    }

    public String getDBUrl() {
        return DBUrl;
    }

    public void setDBUrl(String dBUrl) {
        DBUrl = dBUrl;
    }

    public String getDBDriver() {
        return DBDriver;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

}
3.Tomcat數據提供源
package org.sol.util.db;

import java.sql.Connection;
import java.sql.SQLException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

/**
 * TOMCAT JNDI連接池獲取代理
 * @author SOL
 *
 */
public class TomcatProvider implements IConnectionProvider {
    @Override
    public Connection getConnection(String sourceName) throws SQLException {
        try {
            Context ctx = new InitialContext();
            return ((DataSource)ctx.lookup("java:comp/env/" + sourceName)).getConnection();
        } catch (Exception e) {
            throw new SQLException(e);
        }
    }
}
4.DataConsole 功能封裝類
package org.sol.util.db;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class DataConsole {
    /**
     * 連接
     */
    private Connection connection;
    /**
     * 預處理對象
     */
    private PreparedStatement ps;
    /**
     * 存儲過程處理對象
     */
    private CallableStatement cs;

    /**
     * 結果集對象
     */
    private ResultSet rs;
    /**
     * 數據源
     */
    private String sourceName;

    /**
     * 連接獲取代理
     */
    private IConnectionProvider connectionProvider;

    /**
     * 事務超時事件
     */
    private int queryTime;

    private static final Log log = LogFactory.getLog(DataConsole.class);

    /**
     * 生成一個JDBC封裝對象
     * @param connectionProvider 連接提供代理
     * @param sourceName 數據源名稱,connectionProvider根據此名稱切換數據源
     * @param queryTime 事務處理超時時間 0:無超時
     */
    public DataConsole(IConnectionProvider connectionProvider,String sourceName,int queryTime) {
        this.connectionProvider = connectionProvider;
        this.sourceName = sourceName;
        this.queryTime = queryTime;
    }

    /**
     * 統計語句查詢 直接返回唯一值
     * @param sql
     * @param objs
     * @return
     * @throws SQLException
     */
    public Object findReturn(String sql,Object... objs) throws SQLException {
        log.debug("Query return value:[" + sql + "] " + (objs != null ? Arrays.deepToString(objs) : ""));

        try {
            getConnection();

            ps = connection.prepareStatement(sql);
            ps.setQueryTimeout(queryTime);

            if(objs != null) {
                for(int i = 0 ; i < objs.length; i ++)
                    ps.setObject(i + 1, objs[i]);
            }
            rs = ps.executeQuery();
            if(rs.next())
                return rs.getObject(1);
            else
                return null;
        } finally {
            close();
        }
    }

    /**
     * 執行存儲過程 首字段用于處理返回值 所以存儲過程寫法必須是 {?=call PRODUCENAME(?,?...,?)}
     * @param call 存儲過程
     * @param returnType 返回參數類型 [Types.XXXX]
     * @param objs 參數列表
     * @return
     * @throws SQLException
     */
    public Object callWithReturn(String call,int returnType,Object... objs) throws SQLException {
        log.debug("Call return value:[" + call + "] " + (objs != null ? Arrays.deepToString(objs) : ""));
        try {
            getConnection();

            cs = connection.prepareCall(call);
            cs.setQueryTimeout(queryTime);

            cs.registerOutParameter(1, returnType);
            for(int i = 0; i < objs.length; i ++)
                cs.setObject(i+2,objs[i]);

            cs.execute();
            return cs.getObject(1);
        } finally {
            close();
        }
    }

    /**
     * 用于執行返回列表的存儲過程  并映射到對象列表上
     * @param <X> 
     * @param clazz 映射對象
     * @param sql 查詢語句
     * @param smap 映射配置表<字段名,類型>
     * @param objs 參數列表
     * @return
     * @throws Exception
     */
    public <X> List<X> call(Class<X> clazz,String sql,Map<String,Class<?>> smap,Object... objs) throws Exception {
        log.debug("Call Query produce:[" + sql + "] " + (objs != null ? Arrays.deepToString(objs) : ""));
        try {
            rs = call(sql, objs);

            List<X> list = new ArrayList<X>();

            while(rs != null && rs.next()) {
                X obj = returnObject(clazz,smap);

                list.add(obj);
            }

            return list;
        } catch (Exception e) {
            close();
            throw e;
        } finally {
            close();
        }
    }

    private ResultSet call(String sql,Object... params) throws SQLException {
        getConnection();

        cs = connection.prepareCall(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,
                ResultSet.CONCUR_READ_ONLY);
        cs.setQueryTimeout(queryTime);

        cs.registerOutParameter(1, Types.REAL);
        for(int i = 0; i < params.length; i ++)
            cs.setObject(i + 2, params[i]);

        return cs.executeQuery();
    }

    /**
     * 查詢單個對象 并映射到對象上
     * @param <X> 
     * @param clazz 映射對象
     * @param sql 查詢語句
     * @param smap 映射配置表<字段名,類型>
     * @param objs 參數列表
     * @return
     * @throws Exception
     */
    public <X> X get(Class<X> clazz,String sql,Map<String,Class<?>> smap,Object... objs) throws SQLException {
        log.debug("Get Entity:[" + sql + "] " + (objs != null ? Arrays.deepToString(objs) : ""));
        try {
            rs = query(sql, objs);

            if(rs != null && rs.next()) {
                X obj = returnObject(clazz,smap);

                return obj;
            } else {
                return null;
            }

        } catch (Exception e) {
            throw new SQLException(e);
        } finally {
            close();
        }
    }

    private ResultSet query(String sql,Object... objs) throws Exception {
        getConnection();

        ps = connection.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,
                ResultSet.CONCUR_READ_ONLY);
        ps.setQueryTimeout(queryTime);

        if(objs != null) {
            for(int i = 0; i < objs.length; i ++)
                ps.setObject(i + 1, objs[i]);
        }
        return ps.executeQuery();
    }
    /**
     * 用于解析查詢結果 并映射到對象
     * @param <X>
     * @param clazz 映射對象
     * @param smap 映射表
     * @return
     * @throws InstantiationException
     * @throws SecurityException
     * @throws NoSuchMethodException
     * @throws SQLException
     * @throws IllegalAccessException
     */
    private <X> X returnObject(Class<X> clazz,Map<String,Class<?>> smap) throws InstantiationException,  SecurityException, NoSuchMethodException, SQLException, IllegalAccessException {
        X obj = clazz.newInstance();

        for(Entry<String,Class<?>> en : smap.entrySet()) {
            try {
                Object value = rs.getObject(en.getKey());

                setField(obj,en.getKey(),en.getValue(),(en.getValue().equals(String.class) ? (value != null ? value : null) : value));
            } catch (IllegalArgumentException e1) {
                log.error("不正確的對象映射. 映射類:" + clazz.getName() + 
                        " 配置字段名:" + en.getKey() + 
                        " 類型:" + en.getValue().getName() + 
                        " 數據庫字段類型:" + rs.getObject(en.getKey()).getClass().getName());
            } catch (InvocationTargetException e1) {
                e1.printStackTrace();
            }
        }

        return obj;
    }

    /**
     * 設置對象上字段的值
     * 現在只支持簡單的對象類型 String Integer Short Double 等標準對象
     * 可以擴展這個方法用來支持一些比較復雜的對象格式
     * @param obj 映射的對象
     * @param fieldname 字段名稱 將調用它的set方法進行設置
     * @param type 字段類型
     * @param value 字段值
     * @throws SecurityException
     * @throws NoSuchMethodException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    protected void setField(Object obj,String fieldname,Class<?> type,Object value) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException  {
        Method method = obj.getClass().getMethod(setMethod(fieldname),type);
        method.invoke(obj, value);
    }

    /**
     * 執行插入語句,并返回生成的主鍵
     * @param sql 插入語句
     * @param objs 參數列表
     * @return 插入語句返回的主鍵值
     * @throws SQLException
     */
    public int insertAndReturnKey(String sql,Object... objs) throws SQLException {
        int countRow = 0;
        int key = 0;

        log.debug("Insert and return Id:[" + sql + "] " + (objs != null ? Arrays.deepToString(objs) : ""));
        try {
            getConnection();

            connection.setAutoCommit(false);

            ps = connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
            ps.setQueryTimeout(queryTime);

            if(objs != null) {
                for(int i = 0; i < objs.length; i ++)
                    ps.setObject(i+1,objs[i]);
            }

            countRow = ps.executeUpdate();
            if(countRow > 0) {
                ResultSet rs = ps.getGeneratedKeys();
                if(rs.next())
                    key = rs.getInt(1);
            }
            connection.commit();
        } catch (SQLException e) {
            countRow = 0;
            connection.rollback();
            closeConnection();
            throw e;
        } finally {
            if (connection != null) {
                connection.setAutoCommit(true);
            }
            close();
        }
        return key;
    }

    /**
     * 執行預編譯SQL 
     * @param sql SQL語句
     * @param objs 參數列表
     * @return 執行影響條數
     * @throws SQLException
     */
    public int updatePrepareSQL(String sql,Object... objs) throws SQLException {
        log.debug("Update:[" + sql + "]" + (objs != null ? objs.toString() : ""));

        int countRow = 0;
        try {
            getConnection();

            connection.setAutoCommit(false);

            ps = connection.prepareStatement(sql);
            ps.setQueryTimeout(queryTime);
            if(objs != null) {
                for(int i = 0; i < objs.length; i ++)
                    ps.setObject(i+1,objs[i]);
            }
            countRow = ps.executeUpdate();
            connection.commit();
        } catch (SQLException e) {
            countRow = 0;
            connection.rollback();
            closeConnection();
            throw e;
        } finally {
            if (connection != null) {
                connection.setAutoCommit(true);
            }
            close();
        }
        return countRow;
    }

    public <X> List<X> find(String sql,Class<X> clazz,Map<String,Class<?>> smap,Object... objs) throws SQLException {
        try {
            log.debug("Query:" + sql + " Class:" + clazz.getName() + (objs != null ? Arrays.deepToString(objs) : ""));

            rs = query(sql, objs);

            List<X> list = new ArrayList<X>();

            while(rs != null && rs.next()) {
                X obj = returnObject(clazz,smap);

                list.add(obj);
            }

            return list;
        } catch (Exception e) {
            throw new SQLException(e);
        } finally {
            close();
        }
    }

    public void closeConnection() {
        try {
            if (connection != null) {
                connection.close();
            }
            connection = null;
        } catch (Exception e) {
        }
    }

    public Connection getConnection() throws SQLException {
        if(sourceName == null)
            throw new SQLException("沒有設置數據源");

        int Times = 0;
        while (connection == null || connection.isClosed()) {
            try {
                closeConnection();
                connection = connectionProvider.getConnection(sourceName);
                break;
            } catch (Exception sqle) {
                log.error("error getConnection():" + sqle.getMessage(),sqle);
            }finally{
                if(Times>5){
                    throw new SQLException("獲取連接次數已經超過6次。不再嘗試重新獲取");
                }
                ++Times;
            }
        }
        return connection;
    }

    public void close() {
        try {
            super.finalize();
            if (rs != null) {
                rs.close();
                rs = null;
            }
            if (ps != null) {
                ps.close();
                ps = null;
            }
            if (cs != null) {
                cs.close();
                cs = null;
            }
            if (connection != null) {
                connection.close();
                connection = null;
            }
        } catch (Throwable te) {
        }
    }

    public static Map<String,Class<?>> parseSmap(Class<?> clazz,String... paramNames) {
        Map<String,Class<?>> smap = new HashMap<String, Class<?>>(paramNames.length);

        for(String name : paramNames) {
            try {
                Field field = clazz.getDeclaredField(name);
                smap.put(name, field.getType());
            } catch (Exception e) {
                throw new RuntimeException(e);
            } 
        }

        return smap;
    }

    public static String getMethod(String name) {
        return "get" + name.replaceFirst(name.substring(0,1), name.substring(0,1).toUpperCase());
    }

    public static String setMethod(String name) {
        return "set" + name.replaceFirst(name.substring(0,1), name.substring(0,1).toUpperCase());
    }
}
簡單介紹一下這個封裝類

封裝類通過ConnectionProvider獲取相應的數據源類型,提供幾個快速使用的JDBC方法,包括查詢,更新語句,插入并返回主鍵,調用存儲過程等幾個常用方法,可以看源文件里的說明

用一個測試類來說明吧

package org.sol.util.db;

import java.sql.SQLException;
import java.sql.Types;

public class test {
    private IConnectionProvider connectionProvider = null;
    private String sourceName = "megajoysms";
    private int queryTime = 5;

    private DataConsole dc;

    public test() {
        /*
         * 首先建立數據源對象,現在演示的使用的是JDBC提供源
         * 三個參數 1.驅動名稱 2.url(數據庫名不需要填寫,由sourceName參數執行) 3.登錄用戶名 4.密碼
         * 代碼中還提供了一個tomcat的jndi數據提供源,其他類似的源可以直接繼承IConnectionProvider接口實現
         */
        try {
            connectionProvider = new JdbcProvider(
                    "com.microsoft.sqlserver.jdbc.SQLServerDriver", 
                    "jdbc:sqlserver://127.0.0.1:1433",
                    "sa","123456");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        dc = new DataConsole(connectionProvider, sourceName, queryTime);
    }

    /**
     * Find 方法用來查詢對象
     * 接收3+ 個參數
     * 第一個參數表示執行的Select語句
     * 第二個參數表示查詢結果映射到一個對象類型上
     * 第三個參數表示對查詢結果字段的映射關系
     * 第四個字段用于如果SQL語句是帶參數的,從這里開始填入變量列表
     * 
     * 數據表test結構
     * int id,
     * varchar text,
     * varchar value
     * 映射對象結構
     * class pojo {
        private Integer id;
        private String text;
        get/set 方法...
        }
     * 第三個參數的類型是Map<String,Class<?>>表示 字段名-字段類型的映射關系
     * 可以自己建立這個map,map的鍵用來匹配查詢結果集的字段及映射類的字段
     * 也可以用DataConsole.parseSmap(Class,String...)方法獲取map
     * 這個函數接收兩個參數,第一個參數表示映射的類型,第二個開始的參數表示字段名稱,函數會在映射類中匹配相應的字段類型
     * PS:映射類內部的字段類型不能是基礎類型比如int,short,要寫成Integer,Short
     */
    public void find() {
        try {
            System.out.println(dc.find(
                    "select top 10 * from test where id>?",
                    pojo.class,
                    DataConsole.parseSmap(pojo.class, "id","whichservice"), 
                    10));
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 執行更新語句的方法
     * 接收1+個參數
     * 第一個參數表示SQL更新語句
     * 第二個以后的參數表示傳入的變量列表,無參數語句不填這些參數
     * 返回結果是查詢影響的記錄條數
     */
    public void update() {
        try {
            System.out.println(dc.updatePrepareSQL(
                    "insert into test(id,text,value) values(?,?,?)", 
                    1,"text","value"));
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 執行插入動作,并返回這個操作記錄的主鍵,如果操作無主鍵或主鍵不是自增型,返回0
     * 參數類似于更新動作,這里演示了沒有sql變量的情況
     */
    public void insertAndReturnKey() {
        try {
            System.out.println(dc.insertAndReturnKey("insert into test(text,value) values('text','value')"));
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 類似于執行select語句,此方法用于執行帶有返回結果集的存儲過程
     */
    public void callAndReturnResultSet() {
        try {
            System.out.println(dc.call(pojo.class, 
                    "{call proc_returnpojo(?)}", 
                    DataConsole.parseSmap(pojo.class, "id","whichservice"), 
                    1));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 用于執行存儲過程,如果存儲過程帶有返回值(非output參數),返回接受到的返回值
     *  首字段用于處理返回值 所以存儲過程寫法必須是 {?=call PRODUCENAME(?,?...,?)}
     * 比如:
     * create proc_testreturn
     * begin
     * return 1
     * end
     */
    public void callAndReturn() {
        try {
            System.out.println(dc.callWithReturn("{?=call proc_testreturn}", Types.INTEGER));
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
其中用到的pojo類的定義:
package org.sol.util.db;

public class pojo {
    private Integer id;
    private String text;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }

    @Override
    public String toString() {
        return "pojo [" + (id != null ? "id=" + id + ", " : "")
                + (text != null ? "text=" + text : "") + "]";
    }

}

 本文由用戶 fmms 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!