jdbc實現的orm工具類

新公司的ORM框架使用了hibernate,但是我並不會,剛來項目老闆催的緊,而且項目還是我獨立開發,所以就自己用JDBC完成功能,但是你懂的,jdbc代碼的冗餘,操作的複雜都是我們初學就很煩的事兒了,所以花了半天時間寫了一個BaseDao出來,代碼如下:

import java.lang.reflect.ParameterizedType;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.uqiauto.util.ConnectionUtils;


/**
 * dao層父類
 * 封裝了大部分的增刪改查代碼,但是關鍵實現都設置爲抽象,繼承此類需要提供具體實現
 * 因爲父類try了SQLException,如果聲明其他異常可能會導致程序終止
 * 所以如果需要限制訪問權限,可以在子類實現中聲明SQLException或其子類異常
 * 例:限制用戶添加 throw new SQLException("This table is not allowed to be added");
 */
public abstract class BaseDao<T>{

    private final Class<T> entityClass;
    private final String entityClassName;

    @SuppressWarnings("unchecked")
    public BaseDao() {
        // 通過範型反射,獲取在子類中定義的entityClass.
        this.entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        entityClassName = entityClass.getSimpleName();
    }

    /**
     * 獲取實體類對象
     */
    public Class<T> getEntityClass() {
        return entityClass;
    }

    /**
     * 獲取實體類名(不包括包結構)
     */
    public String getEntityClassName() {
        return entityClassName;
    }

    /**
     * 調用子類方法的入口,子類中只需要提供toObject方法的實現
     * 查詢無參實現
     * @param sql
     * @param values
     * @return
     */
    public T queryOne(String sql){
        T data = null;
        try {
            data = this.entityClass.newInstance();
            PreparedStatement pstmt = null;
            ResultSet rs = null;
            try {
                Connection conn = ConnectionUtils.getConnection();
                pstmt = conn.prepareStatement(sql);
                rs = pstmt.executeQuery();
                toObject(rs, data);
            } catch (SQLException e) {
                e.printStackTrace();
            }finally {
                ConnectionUtils.closeAll(rs, pstmt, Boolean.TRUE);
            }
        } catch (InstantiationException | IllegalAccessException e1) {
            e1.printStackTrace();
        }
        return data;
    }

    /**
     * 由子類繼承,父類中不提供任何實現
     * @param rs    
     *      查詢返回的結果集
     * @param data
     *      查詢後保存的對象
     * @return
     *      參數data
     * @throws SQLException
     *      必須處理異常
     */
    protected abstract void toObject(ResultSet rs, T entity) throws SQLException;

    /**
     * 查詢結果數量
     * @param sql
     * @return
     */
    public int queryCount(String sql){
        int count = 0;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            Connection conn = ConnectionUtils.getConnection();
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();
            if(rs.next()){
                count = rs.getInt(1);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            ConnectionUtils.closeAll(rs, pstmt, Boolean.TRUE);
        }
        return count;
    }

    /**
     * 調用子類方法的入口,子類中只需要提供toObjectOfList方法的實現
     * 查詢不帶參數實現
     * @param sql
     * @param values
     * @return
     */
    public List<T> queryList(String sql){
        List<T> data = new ArrayList<>();
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            Connection conn = ConnectionUtils.getConnection();
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();
            toObjectOfList(rs, data);
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            ConnectionUtils.closeAll(rs, pstmt, Boolean.TRUE);
        }
        return data;
    }

    /**
     * 分頁查詢,注意調用者必須驗證返回值中的success函數
     * @param sql
     *      查詢的SQL語句,不需要limit
     * @param curPage
     *      當前頁碼
     * @param pageSize
     *      每頁顯示條數
     * @return
     *      保存分頁相關參數的map集合
     *      totalRows:數據條數
     *      page:頁數
     *      data:返回數據
     *      success:驗證分頁 true->成功  false->失敗
     */
    public Map<String, Object> queryListByPage(String sql, int curPage, int pageSize){
        Map<String, Object> map = new HashMap<>();
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            Connection conn = ConnectionUtils.getConnection();

            String countSql = sql.replace("*", "count(*)");
            pstmt = conn.prepareStatement(countSql);
            rs = pstmt.executeQuery();
            int count = rs.next() ? rs.getInt(1) : 0;
            int pageCount = count%pageSize == 0 ? count/pageSize : count/pageSize+1;
            map.put("totalRows", count);
            map.put("page", pageCount);

            List<T> data = new ArrayList<>();
            int begin = (curPage-1)*pageSize+1;
            sql = sql.contains("where") ? sql + " limit " : sql + " where 1=1 limit ";
            sql = sql + begin + "," + pageSize;
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();
            toObjectOfList(rs, data);
            map.put("data", data);
            map.put("success", true);
        } catch (SQLException e) {
            map.put("success", false);
            e.printStackTrace();
        }finally {
            ConnectionUtils.closeAll(rs, pstmt, Boolean.TRUE);
        }
        return map;
    }

    /**
     * 可以由子類繼承,父類中所提供默認實現的內部是由子類實現的toObject方法
     * @param rs    
     *      查詢返回的結果集
     * @param data
     *      查詢後保存的集合
     * @return
     *      參數data
     * @throws SQLException
     *      必須處理異常
     */
    protected List<T> toObjectOfList(ResultSet rs, List<T> data) throws SQLException{
        if(rs != null && data != null){
            int rowCount = rs.last() ? rs.getRow() : 0;
            rs.beforeFirst();
            try {
                T t;
                for(int i=0; i< rowCount; i++){
                    t = this.entityClass.newInstance();
                    toObject(rs, t);
                    data.add(t);
                }
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return data;
    }

    /**
     * 添加一條數據方法,參數放到SQL中直接執行
     * @param sql 執行SQL
     * @return 驗證操作成功
     */
    public boolean insert(String sql){
        PreparedStatement pstmt = null;
        Connection conn =  null;
        try {
            conn = ConnectionUtils.getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.executeUpdate();
            conn.commit();
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            return false;
        }finally{
            ConnectionUtils.closeAll(null, pstmt, Boolean.TRUE);
        }
    }

    /**
     * 添加一條數據方法,參數的傳遞使用實體的值和實體對應dao層封裝的賦值方法
     * @param sql 執行SQL
     * @param t  帶參數的實體對象
     * @return 驗證操作成功
     */
    public boolean insert(String sql, T entity){
        PreparedStatement pstmt = null;
        Connection conn =  null;
        try {
            conn = ConnectionUtils.getConnection();
            pstmt = conn.prepareStatement(sql);
            doInsertSetPstmt(pstmt, entity);
            pstmt.executeUpdate();
            conn.commit();
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            return false;
        }finally{
            ConnectionUtils.closeAll(null, pstmt, Boolean.TRUE);
        }
    }

    /**
     * 批量添加數據,參數的傳遞使用實體的值和實體對應dao層封裝的賦值方法
     * @param sql 執行SQL
     * @param dataList  保存帶參數實體對象的集合
     * @return 驗證操作成功
     */
    public boolean batchInsert(String sql, List<T> dataList){
        PreparedStatement pstmt = null;
        Connection conn =  null;
        try {
            conn = ConnectionUtils.getConnection();
            pstmt = conn.prepareStatement(sql);
            conn.setAutoCommit(false);
            int count = 0;
            for (T t : dataList) {
                doInsertSetPstmt(pstmt, t);
                pstmt.addBatch();
                if(++count == 500){
                    pstmt.executeBatch();
                    count = 0;
                }
            }
            pstmt.executeBatch();
            conn.commit();
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            return false;
        }finally{
            ConnectionUtils.closeAll(null, pstmt, Boolean.TRUE);
        }
    }

    /**
     * 由子類繼承,父類中不提供任何實現
     * @param pstmt
     * @param entity
     * @throws SQLException 
     */
    protected abstract void doInsertSetPstmt(PreparedStatement pstmt, T entity) throws SQLException;

    /**
     * 刪除SQL中的數據
     * @param sql
     * @return
     */
    public boolean delete(String sql){
        PreparedStatement pstmt = null;
        Connection conn =  null;
        try {
            conn = ConnectionUtils.getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.executeUpdate();
            doDelete(conn);
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            return false;
        }finally{
            ConnectionUtils.closeAll(null, pstmt, Boolean.TRUE);
        }
    }

    /**
     * 根據id集合批量刪除
     * @param sql
     * @param ids
     * @return
     */
    public boolean batchDelete(String sql, List<Integer> ids){
        PreparedStatement pstmt = null;
        Connection conn =  null;
        try {
            conn = ConnectionUtils.getConnection();
            pstmt = conn.prepareStatement(sql);
            conn.setAutoCommit(false);
            int count = 0;
            for (int i : ids) {
                pstmt.setInt(1, i);
                pstmt.addBatch();
                if(++count == 500){
                    pstmt.executeBatch();
                    count = 0;
                }
            }
            pstmt.executeBatch();
            doDelete(conn);
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            return false;
        }finally{
            ConnectionUtils.closeAll(null, pstmt, Boolean.TRUE);
        }
    }
    protected abstract void doDelete(Connection conn) throws SQLException;

    /**
     * 修改SQL中的數據
     * @param sql
     * @return
     */
    public boolean update(String sql){
        PreparedStatement pstmt = null;
        Connection conn =  null;
        try {
            conn = ConnectionUtils.getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.executeUpdate();
            conn.commit();
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            return false;
        }finally{
            ConnectionUtils.closeAll(null, pstmt, Boolean.TRUE);
        }
    }

    /**
     * 批量修改集合中的所有數據
     * @param sql
     * @param dataList
     * @return
     */
    public boolean batchUpdate(String sql, List<T> dataList){
        PreparedStatement pstmt = null;
        Connection conn =  null;
        try {
            conn = ConnectionUtils.getConnection();
            pstmt = conn.prepareStatement(sql);
            conn.setAutoCommit(false);
            int count = 0;
            for (T t : dataList) {
                doUpdateSetPstmt(pstmt, t);
                pstmt.addBatch();
                if(++count == 500){
                    pstmt.executeBatch();
                    count = 0;
                }
            }
            pstmt.executeBatch();
            conn.commit();
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            return false;
        }finally{
            ConnectionUtils.closeAll(null, pstmt, Boolean.TRUE);
        }
    }

    protected abstract void doUpdateSetPstmt(PreparedStatement pstmt, T entity) throws SQLException;

    public java.sql.Date toSqlDate(Date date){
        java.sql.Date sqlDate = new java.sql.Date(new Date().getTime());
        if(date != null)
            sqlDate = new java.sql.Date(date.getTime());
        return sqlDate;
    }

    public Date toUtilDate(java.sql.Date sqlDate){
        Date date = new Date();
        if(sqlDate != null)
            date = new Date(sqlDate.getTime());
        return date;
    }

}

連接MySQL數據庫的工具類:


import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class ConnectionUtils {

    // 線程單例

    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    private static String url;

    private static String username;

    private static String password;

    static {
        // 裝載驅動參數
        try {
            ClassLoader classLoader = ConnectionUtils.class.getClassLoader();
            InputStream is = classLoader.getResourceAsStream("standard.properties");
            Properties props = new Properties();
            props.load(is);
            url = props.getProperty("url");
            username = props.getProperty("username");
            password = props.getProperty("password");
            // 註冊驅動
            Class.forName(props.getProperty("jdbc.driverName"));
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException{
        Connection con = tl.get();
        if (con == null || con.isClosed()) {
            con = DriverManager.getConnection(url, username, password);
            tl.set(con);
        }
        return con;
    }

    public static void closeConnection() {
        Connection conn = tl.get();
        if (conn == null)
            return;
        try {
            if (!conn.isClosed()) {
                //關閉數據庫連接
                conn.close();
            }
        } catch (SQLException e) {
            System.err.println("#ERROR# :關閉數據庫連接發生異常,請檢查!\n" + e.getMessage());
        }
    }

    public static void closeAll(ResultSet rs, Statement stmt, boolean closeConn) {
        try {
            if (rs != null)
                rs.close();
            if (stmt != null)
                stmt.close();
            if(closeConn)
                closeConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

實體類如下:

public class Area{
    //----------- object properties
    private Integer id;
    private String name;
    private Integer parent_id;
    private Byte sort;
    private Byte deep;
    private String city_code;
    private String region;
    private Integer status;
    private Integer ad_code;

    //------------ database columns
    public static final String ID = "AREA_ID";
    public static final String NAME = "AREA_NAME";
    public static final String PARENT_ID = "AREA_PARENT_ID";
    public static final String SORT = "AREA_SORT";
    public static final String DEEP = "AREA_DEEP";
    public static final String CITY_CODE = "CITY_CODE";
    public static final String REGION = "AREA_REGION";
    public static final String STATUS = "AREA_STATUS";
    public static final String AD_CODE = "AD_CODE";
    //------------ get or set ...

dao實現類如下:


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.entity.Area;

public class AreaDao extends BaseDao<Area>{

    @Override
    protected void toObject(ResultSet rs, Area data) throws SQLException{
        if(rs.next()){
            data.setId(rs.getInt(Area.ID));
            data.setName(rs.getString(Area.NAME));
            data.setParent_id(rs.getInt(Area.PARENT_ID));
            data.setSort(rs.getByte(Area.SORT));
            data.setDeep(rs.getByte(Area.DEEP));
            data.setCity_code(rs.getString(Area.CITY_CODE));
            data.setRegion(rs.getString(Area.REGION));
            data.setStatus(rs.getInt(Area.STATUS));
            data.setAd_code(rs.getInt(Area.AD_CODE));
        }
    }

    @Override
    protected void doInsertSetPstmt(PreparedStatement pstmt, Area t) throws SQLException {
        throw new SQLException("This table is not allowed to be added");
    }

    @Override
    protected void doUpdateSetPstmt(PreparedStatement pstmt, Area t) throws SQLException {
        throw new SQLException("This form is not allowed to be amended");
    }
    @Override
    protected void doDelete(Connection conn) throws SQLException {
        throw new SQLException("This form is not allowed to be deleted");
    }

}

我這裏只是封裝了一些冗餘代碼,並沒有完成屬性映射,一開始想着是用反射實現,後來發現時間不夠。。。 然後就先寫到這裏了,寫的過程想到我們可以用自定義註解來配置實體和表的映射,後面有時間會研究研究。

然後突然發現ORM框架其實不難(說這句話的時候我還沒有研究過人家框架的源碼,也許並不像我想的那麼簡單),也許以後有時間會自己完成一個ORM框架吧,先到這裏,去趕項目了。
最後,這只是給像我這樣菜鳥看的,大神勿噴[拜謝]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章