封裝JDBC

封裝JDBC-完成簡易版的ORM框架     

設計簡易版的ORM映射框架:對外隱藏jdbc的實現細節,對方法api調用者來說只需要以操作對象的方式來調用就可以了。

默認映射規則

     沒有特殊說明,類的簡單名稱對應關係數據庫的表明,如果不一致,我們自定義一個註解@Table對錶名進行映射。同理,對象屬性名稱默認對應關係表的字段,如果不一致,我們自定義一個註解@Column對列明進行映射說明。同時我們還需要指定id的定義@Id用來映射關係表的主鍵

  • @Table的定義:用來映射類和表名的對應關係
    package com.wise.tiger.annotation;  
      
    import java.lang.annotation.*;  
      
    /** 
     * 自定義註解 
     *      註解相當於配置說明,本身不具備任何功能,功能體現在程序中對該註解的處理 
     * 元註解 
     *      @Retention :註解保留的階段 
     *              SOURCE:源代碼階段 
     *              CLASS:字節碼階段 
     *              RUNTIME:運行時階段 
     *      @Target : 該註解可以貼在什麼地方 
     *          value = {}:表示屬性value取值是一個數組,如果只有一個取值,那麼{} 
     *      @Documented 
     * 屬性 
     *      數據類型 屬性名(); 
     *      可以給屬性指定默認值: String name() default "";使用註解時如果沒有指定屬性值,那麼會取默認值 
     *      屬性可以指定多個,如果只有一個屬性,且該屬性名爲value,那麼可以不用寫value = 
     */  
    @Retention(RetentionPolicy.RUNTIME)  
    @Target(ElementType.TYPE)  
    @Documented  
    public @interface Table {  
        String value();//表名  
    }  
  •  @Column的定義,用來映射屬性和列名的對應關係
    package com.wise.wise.annotation;  
    import java.lang.annotation.*;  
      
    @Retention(RetentionPolicy.RUNTIME)  
    @Target(ElementType.METHOD)  
    @Documented  
    public @interface Column {  
        /** 
         * 屬性名和數據庫字段的映射 
         * @return 數據庫表字段名稱 
         */  
        String value();  
    }  

     

  •  @Id的定義:用來定義主鍵id
    package com.wise.wise.annotation;  
    import java.lang.annotation.*;  
      
    @Retention(RetentionPolicy.RUNTIME)  
    @Target({ElementType.METHOD,ElementType.FIELD})  
    @Documented  
    public @interface Id {  
        /** 
         * id名稱 
         * @return id名稱 
         */  
        String value() default "id";  
    }  
  • 實體元數據的定義
    package com.wise.domain;  
      
    import com.wise.annotation.Column;  
    import com.wise.annotation.Id;  
    import com.wise.annotation.Table;  
      
    import java.time.LocalDate;  
      
    /** 
     * 圖書實體 
     */  
    @Table("tb_book")  
    public class Book{  
        /** 
         * id 
         */  
        private Integer id;  
        /** 
         * 圖書名稱 
         */  
        private String title;  
        /** 
         * 圖書作者 
         */  
        private String author;  
        /** 
         * 圖書價格 
         */  
        private float price;  
        /** 
         * 出版社信息 
         */  
        private String publisher;  
        /** 
         * 圖書簡介 
         */  
        private String intro;  
        /** 
         * 出版日期 
         */  
        private LocalDate publishDate = LocalDate.now();  
      
        @Column("publish_date")  
        public LocalDate getPublishDate() {  
            return publishDate;  
        }  
      
        //******************setter and getter ****************//  
    }  

     

設計持久化操作api

  • persist(T entity):將實體對象進行持久化(保存進數據庫)
  • remove(Serializable id,Class<?> clazz):根據id刪除對應的特定類型數據,具有緩存功能
  • merge(T entity):修改實體
  • T findById(Serializable id):根據主鍵(id)獲取對應的實體信息
  • List<T> list(Class<T> clazz,int offset,int size):根據偏移量和每次加載記錄數分頁查詢
  • long getCount(Class<?> clazz):獲取特定類型總記錄數

對外開放sql語句(本地sql)的兩個api

  • executeUpdate
  • executeQuery
public class SqlSession {  
    /** 
     *  執行dml語句模板方法 
     * @param sql 傳遞的sql語句 
     * @param params 預編譯語句的站位參數 
     * @return 影響數據行數 
     */  
    public int executeUpdate(String sql,Object... params){  
        var ret = 0;  
        try(var conn = DBHelper.getConnection();  
            var ptst = conn.prepareStatement(sql)){  
            for(int i = 0; params.length > 0 && i < params.length; i++)  
                ptst.setObject(i + 1,params[i]);  
            ptst.executeUpdate();  
        }catch (SQLException e){  
            e.printStackTrace();  
        }  
        return ret;  
    }  
  
    /** 
     * 執行dql語句模板方法 
     * @param sql 傳遞的sql語句 
     * @param handler 結果集處理handler(策略模式) 
     * @param params 預編譯語句的站位參數 
     * @param <T> 參數化類型 
     * @return 查詢結果 
     */  
    public <T> T executeQuery(String sql, ResultSetHandler<T> handler, Object... params){  
        T ret = null;  
        try(var conn = DBHelper.getConnection();  
            var ptst = conn.prepareStatement(sql)){  
            for(int i = 0; params.length > 0 && i < params.length; i++)  
                ptst.setObject(i + 1,params[i]);  
            var rs = ptst.executeQuery();  
            ret = handler.handler(rs);  
        }catch (SQLException e){  
            e.printStackTrace();  
        }  
        return ret;  
    }  
  
    /** 
     * 添加實體數據到數據庫 
     * @param entity 實體信息 
     * @return 影響行數,後期可以返回自增的主鍵 
     */  
    public <T> int insert(T entity){  
        //生成具體的插入的sql語句  
        try {  
            var sp = insertSqlAndParams(entity);  
            System.out.println(sp.getSql());  
            System.out.println(Arrays.toString(sp.getParams()));  
            return executeUpdate(sp.getSql(),sp.getParams());  
        } catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {  
            e.printStackTrace();  
        }  
        return 0;  
    }  
  
    /** 
     * 根據主鍵primary key(id)刪除指定數據 
     * @param id 主鍵id 
     * @return 影響行數 
     */  
    public int delete(Serializable id,Class<?> clazz){  
        var sql = "DELETE FROM " + getTableName(clazz) + " WHERE " + getIdName(clazz) + " = ?";  
        return executeUpdate(sql,id);  
    }  
  
    /** 
     * 根據主鍵(id)修改對應的信息 
     * @param entity 修改好的實體信息 
     * @return 影響行數 
     */  
    public<T> int merge(T entity){  
        try {  
            var sp = updateSqlAndParams(entity);  
            return executeUpdate(sp.getSql(),sp.getParams());  
        } catch (IntrospectionException | InvocationTargetException  | IllegalAccessException e) {  
            e.printStackTrace();  
        }  
        return 0;  
    }  
  
    /** 
     * 根據主鍵(id)獲取對應的實體信息 
     * @param id id 
     * @param clazz 具體類型 
     * @return 
     */  
    public<T> T getById(Serializable id,Class<T> clazz){  
        var builder  = new StringBuilder("SELECT ");  
        try {  
            var pds = Introspector.getBeanInfo(clazz,Object.class).getPropertyDescriptors();  
            for(var pd : pds){  
                var method = pd.getReadMethod();  
                if(method.getAnnotation(Id.class) != null){  
                    builder.append(method.getAnnotation(Id.class).value()).append(',');  
                }else{  
                    builder.append(method.getAnnotation(Column.class) == null ? pd.getName() : method.getAnnotation(Column.class).value()).append(',');  
                }  
            }  
            var sql = builder.deleteCharAt(builder.length() - 1).append(" FROM ").append(getTableName(clazz)).append(" WHERE ").append(getIdName(clazz)).append(" = ?").toString();  
            return executeQuery(sql,new BeanHandler<>(clazz),id);  
        } catch (IntrospectionException e) {  
            e.printStackTrace();  
        }  
        return null;  
    }  
  
    /** 
     * sql語句以及站位參數值 
     */  
    private class SqlAndParams{  
        //sql語句  
        private String sql;  
        //站位參數?對應的值  
        private Object[] params;  
        public String getSql() {  
            return sql;  
        }  
  
        public void setSql(String sql) {  
            this.sql = sql;  
        }  
  
        public Object[] getParams() {  
            return params;  
        }  
  
        public void setParams(Object[] params) {  
            this.params = params;  
        }  
    }  
  
    /** 
     * 生成具體實體entity的插入sql預編譯語句以及?佔位符參數值 
     *  INSERT INTO tb_name(fields...) VALUES(?,....?) 
     * @param entity 具體的javaBean實體 
     * @param <T> 具體的類型 
     * @return SqlAndParams 
     */  
    private<T> SqlAndParams insertSqlAndParams(T entity)  
            throws IntrospectionException,InvocationTargetException,IllegalAccessException {  
        var sp = new SqlAndParams();  
        var params = new ArrayList<>();  
        var builder = new StringBuilder("INSERT INTO ");  
        builder.append(getTableName(entity.getClass())).append('(');  
        var beanInfo = Introspector.getBeanInfo(entity.getClass(),Object.class);  
        var pds = beanInfo.getPropertyDescriptors();  
        for(var pd : pds){  
            //排除掉id的拼接  
            var method = pd.getReadMethod();  
            if(method.getAnnotation(Id.class) != null)continue;  
            //獲取屬性對應的屬性名,屬性名默認對應數據庫裏的字段名(非默認需處理)  
            var column = method.getAnnotation(Column.class);  
            var name = column != null ? column.value() : pd.getName();  
            builder.append(name).append(',');  
        }  
        builder.deleteCharAt(builder.length() - 1);//刪除最後多餘的,  
        builder.append(") VALUES(");  
        for(int i = 0; i < pds.length; i++){  
            //獲取該屬性對應的getter  
            var method = pds[i].getReadMethod();  
            //排除掉id的設置  
            if(method.getAnnotation(Id.class) != null)continue;  
            builder.append("?,");  
            params.add(method.invoke(entity));  
        }  
        builder.deleteCharAt(builder.length() - 1).append(')');  
        sp.setSql(builder.toString());  
        sp.setParams(params.toArray());  
        return sp;  
    }  
  
    /** 
     * 獲取指定表名 Book/User/Topic/Reply... 
     * @param clazz 實體類型 
     * @return 表名,默認爲類(類型)的簡單名稱 
     *  非默認需要額外處理: 
     *  簡單名稱        數據庫表名 
     *  Book                tb_book 
     */  
    private String getTableName(Class<?> clazz) {  
       /* var tableName = clazz.getSimpleName(); 
        *//* 
         * 處理表名和類的簡單名稱不一致的情況 
         *//* 
        var table = clazz.getAnnotation(Table.class); 
        if(table != null) 
            tableName = table.value();*/  
        return clazz.getAnnotation(Table.class) == null ? clazz.getSimpleName() : clazz.getAnnotation(Table.class).value();  
    }  
  
    /** 
     * 獲取id名稱 
     * @param clazz 類型 
     * @return id名稱 
     */  
    private String getIdName(Class<?> clazz){  
        String idName = "id";  
        try {  
            var pds = Introspector.getBeanInfo(clazz,Object.class).getPropertyDescriptors();  
            for(var pd : pds){  
                var id = pd.getReadMethod().getAnnotation(Id.class);  
                if(id != null) return id.value();  
            }  
        } catch (IntrospectionException e) {  
            e.printStackTrace();  
        }  
        return idName;  
    }  
  
    /** 
     * 更新的sql語句及預編譯參數佔位符值 
     * @param entity 更新後的實體信息 
     * @param <T> 類型 
     * @return 
     * @throws IntrospectionException 
     * @throws InvocationTargetException 
     * @throws IllegalAccessException 
     */  
    private<T> SqlAndParams updateSqlAndParams(T entity)  
            throws IntrospectionException,InvocationTargetException,IllegalAccessException {  
        var sp = new SqlAndParams();  
        var params = new ArrayList<>();  
        var builder = new StringBuilder("UPDATE ");  
        builder.append(getTableName(entity.getClass())).append(" SET ");  
        var beanInfo = Introspector.getBeanInfo(entity.getClass(),Object.class);  
        var pds = beanInfo.getPropertyDescriptors();  
        Object idValue = null;  
        for(var pd : pds){  
            //排除掉id的拼接  
            var method = pd.getReadMethod();  
            if(method.getAnnotation(Id.class) != null)  
                idValue = method.invoke(entity);  
            else {  
                //獲取屬性對應的屬性名,屬性名默認對應數據庫裏的字段名(非默認需處理)  
                var column = method.getAnnotation(Column.class);  
                var name = column != null ? column.value() : pd.getName();  
                builder.append(name).append(" = ?,");  
                params.add(method.invoke(entity));  
            }  
        }  
        builder.deleteCharAt(builder.length() - 1);//刪除最後多餘的,  
        builder.append(" WHERE ").append(getIdName(entity.getClass())).append(" = ?");  
        params.add(idValue);  
        sp.setSql(builder.toString());  
        sp.setParams(params.toArray());  
        return sp;  
    }  
}  

接下來添加緩存功能以及事務功能

//緩存池  
private Map<String, Object> cache = new HashMap<>();  
//可重入讀寫鎖  
private ReadWriteLock rwl = new ReentrantReadWriteLock();  
  
   /** 
     * 添加實體數據到數據庫 
     * @param entity 實體信息 
     * @return 影響行數,後期可以返回自增的主鍵 
     */  
    public <T> int insert(T entity){  
        var ret = 0;  
        //生成具體的插入的sql語句  
        try {  
            var sp = insertSqlAndParams(entity);  
            System.out.println(sp.getSql());  
            System.out.println(Arrays.toString(sp.getParams()));  
            ret = executeUpdate(sp.getSql(),sp.getParams());  
            var key = entity.getClass().getName() + "_" + getIdName(entity.getClass());  
            //將插入的數據添加進緩存池中  
            rwl.writeLock().lock();  
            try{  
                cache.put(key,entity);  
            }finally{  
                rwl.writeLock().unlock();  
            }  
        } catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {  
            e.printStackTrace();  
        }  
        return ret;  
    }  
  
/** 
     * 根據主鍵primary key(id)刪除指定數據 
     * @param id 主鍵id 
     * @return 影響行數 
     */  
    public int delete(Serializable id,Class<?> clazz){  
        var sql = "DELETE FROM " + getTableName(clazz) + " WHERE " + getIdName(clazz) + " = ?";  
        var ret = executeUpdate(sql,id);  
        var key = clazz.getName() + "_" + id;  
        //從緩存中移出刪除的對象  
        rwl.writeLock().lock();  
        try{  
            cache.remove(key);  
        }finally{  
            rwl.writeLock().unlock();  
        }  
        return ret;  
    }  
  
/** 
     * 根據主鍵(id)修改對應的信息 
     * @param entity 修改好的實體信息 
     * @return 影響行數 
     */  
    public<T> int merge(T entity){  
        var ret = 0;  
        try {  
            var sp = updateSqlAndParams(entity);  
            ret = executeUpdate(sp.getSql(),sp.getParams());  
            rwl.writeLock().lock();  
            try{  
                cache.replace(entity.getClass().getName() + "_" + getIdName(entity.getClass()),entity);  
            }finally{  
                rwl.writeLock().unlock();  
            }  
        } catch (IntrospectionException | InvocationTargetException  | IllegalAccessException e) {  
            e.printStackTrace();  
        }  
        return ret;  
    }  
  
/** 
     * 根據主鍵(id)獲取對應的實體信息,先從緩存中去加載,沒有找到再到數據庫中去加載 
     * @param id id 
     * @param clazz 具體類型 
     * @return 
     */  
    public<T> T getById(Serializable id,Class<T> clazz){  
        var key = clazz.getName() + "_" +id;  
        rwl.readLock().lock();  
        Object value = null;  
        try{  
            value = cache.get(key);  
            if(value == null){  
                rwl.readLock().unlock();  
                rwl.writeLock().lock();  
                try{  
                    if(value==null){  
                       value = queryDB(id,clazz);  
                       if(value != null) cache.put(key,value);  
                    }  
                }finally{  
                    rwl.writeLock().unlock();  
                }  
                rwl.readLock().lock();  
            }  
        }finally{  
            rwl.readLock().unlock();  
        }  
        return (T)value;  
    }  
  
    /** 
     * 根據主鍵(id)獲取對應的實體信息 
     * @param id id 
     * @param clazz 具體類型 
     * @return 
     */  
    private <T> T queryDB(Serializable id,Class<T> clazz){  
        var builder  = new StringBuilder("SELECT ");  
        try {  
            var pds = Introspector.getBeanInfo(clazz,Object.class).getPropertyDescriptors();  
            for(var pd : pds){  
                var method = pd.getReadMethod();  
                if(method.getAnnotation(Id.class) != null){  
                    builder.append(method.getAnnotation(Id.class).value()).append(',');  
                }else{  
                    builder.append(method.getAnnotation(Column.class) == null ? pd.getName() : method.getAnnotation(Column.class).value()).append(',');  
                }  
            }  
            var sql = builder.deleteCharAt(builder.length() - 1).append(" FROM ").append(getTableName(clazz)).append(" WHERE ").append(getIdName(clazz)).append(" = ?").toString();  
            return executeQuery(sql,new BeanHandler<>(clazz),id);  
              
        } catch (IntrospectionException e) {  
            e.printStackTrace();  
            return null;  
        }  
    }  
public<T> List<T> list(Class<T> clazz,int offset, int size){  
        List<T> list = null;  
        var builder  = new StringBuilder("SELECT ");  
        try {  
            var pds = Introspector.getBeanInfo(clazz, Object.class).getPropertyDescriptors();  
            for (var pd : pds) {  
                var method = pd.getReadMethod();  
                if (method.getAnnotation(Id.class) != null) {  
                    builder.append(method.getAnnotation(Id.class).value()).append(',');  
                } else {  
                    builder.append(method.getAnnotation(Column.class) == null ? pd.getName() : method.getAnnotation(Column.class).value()).append(',');  
                }  
            }  
            builder.deleteCharAt(builder.length() - 1).append(" FROM ").append(getTableName(clazz));  
            if(offset > 0 && size > 1) {  
                builder.append(" LIMIT ?,?");  
                list = this.executeQuery(builder.toString(),new ListBeanHandler<>(clazz),offset,size);  
            }else{  
                list = this.executeQuery(builder.toString(),new ListBeanHandler<>(clazz));  
            }  
              
        }catch (IntrospectionException e){  
            e.printStackTrace();  
        }  
        return list;  
    }  
    public<T> Long getCount(Class<T> clazz){  
        var sql = "SELECT COUNT(1) FROM " + getTableName(clazz);  
        return this.executeQuery(sql,rs -> rs.next() ? rs.getLong(1) : 0);  
    }  

提供一個SqlSessionFactory工具類用來創建SqlSession對象,簡單使用

public class BookDaoImpl implements BookDao {  
    private SqlSession template = SqlSession.buildSession();  
    @Override  
    public void persistent(Book book) {  
        template.insert(book);  
    }  
  
    @Override  
    public void delete(Integer id) {  
       template.delete(id,Book.class);  
    }  
  
    @Override  
    public void update(Book book) {  
        template.merge(book);  
    }  
  
    @Override  
    public Book search(Integer id) {  
       return template.getById(id,Book.class);  
    }  
  
    @Override  
    public List<Book> search() {  
        return template.list(Book.class,-1,-1);  
    }  
  
    @Override  
    public long getCount() {  
        return template.getCount();  
    }  
}  

 

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