Java學習筆記之使用反射+泛型構建通用DAO


PS:最近簡單的學了學后台Servlet+JSP.也就只能學到這里了.沒那么多精力去學SSH了,畢竟Android還有很多東西都沒學完..

 

學習內容:

1.如何使用反射+泛型構建通用DAO.

 

1.使用泛型+反射構建通用DAO.

 DAO的概念曾經寫過.其實就是數據訪問對象,Date Access Object.其實就是數據庫中的數據和Java對象里的一種關聯關系的一系列操作.由於以前寫過這個概念.就不啰嗦了..簡單說一說思想.

 比如說:有兩張表 User表和Shop表.

 這是User表中需要進行的操作.

public interface UserDao { void add(User user); void delete(User user); void update(User user); User select(User user); }

 這是Shop表中需要進行的操作.

public interface ShopDao { void add(Shop shop); void delete(Shop shop); void update(Shop shop); Shop select(Shop shop); }

 不 難發現,二者都有相同的操作.這樣使得代碼冗余度較高.那么能否將這兩個DAO相同的方法封裝成一個呢.這是可以的.這樣就構建了一個BaseDao里面 封裝了二者相同的操作.當我們需要操作表的時候,我們將T換成User或者是Shop就可以了.當我們實際項目面對的表有非常多的時候,如果都具有相同的 方法.那么就可以這樣進行抽取.

public interface BaseDao<T> { void add(T t); void delete(T t); void update(T t); T select(T t); }

 這樣就構建了一個通用的DAO抽象接口.這里我拿User表來說,如果User表還有其他的業務邏輯呢(比如說查詢所有信息等等).那么我們只需要這樣.

public interface UserDao extends BaseDao<User> { //按照行查詢,額外的業務邏輯. List<User>findAll(); }

 這樣我們只需要定義一個新的UserDao就可以了,它繼承了BaseDao中的所有方法,當有額外的業務邏輯的時候,只需要添加額外方法就可以了.這樣光有接口當然是不行的.我們需要有具體的實現.

 我們先看BaseDao的實現類BaseDaoImp

 我們來看一下思路.

 先上一張原理圖.

 1.首先我們如果想對User表進行操作,那么我們首先需要獲取User類型.告訴BaseDaoImp,我們當前是需要對User表進行操作.因此構造函數就是用來干這個的.

 2. 當我們獲取了User類型之后,如果想要對其進行操作,那么首先需要知道 sql 語句,因此我們需要對sql語句進行拼接.那么拼接過程中,我們需要知道User表內部到底聲明了哪些變量.這樣就需要使用反射機制.通過反射機制來獲取 User實體類中聲明的變量,然后對sql進行相關的拼接.那么getsql函數用來完成sql的拼接過程.

 3. 那么拼接完之后還是不行,因為拼接出來的sql語句是這樣的:insert into User(id,username,password,email,grade) values(?,?,?,?,?)我們需要對占位符進行賦值操作.那么首先我們需要獲取具體的值,那么setArgs就是來獲取屬性的具體值的.

 4.當獲取了具體的值之后,我們就可以通過sql提供給我們的相關函數來執行sql語句了.

 這里函數其實都非常的簡單,只要細看,還是能明白其中的道理的.

package com.example.daoimp;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import com.example.dao.BaseDao;
import com.example.helper.JdbcDaoHelper;

//通用DAO

public class BaseDaoImp<T> implements BaseDao<T> {

    /** 操作常量 */
    public static final String SQL_INSERT = "insert";
    public static final String SQL_UPDATE = "update";
    public static final String SQL_DELETE = "delete";
    public static final String SQL_SELECT = "select";

    private Class<T> EntityClass; // 獲取實體類

    private PreparedStatement statement;

    private String sql;          

    private Object argType[];

    private ResultSet rs;

    @SuppressWarnings("unchecked")
    public BaseDaoImp() {
        
        /**
         *  傳遞User就是 com.example.daoimp.BaseDaoImp<com.example.bean.User>
         *  傳遞Shop就是 com.example.daoimp.BaseDaoImp<com.example.bean.Shop>
         * */
        ParameterizedType type = (ParameterizedType) getClass()
                .getGenericSuperclass();      
        
        /**
         * 這里如果傳遞的是User.那么就是class com.example.bean.User 
         * 如果傳遞的是Shop.       那么就是class com.example.bean.Shop
         * */
        EntityClass = (Class<T>) type.getActualTypeArguments()[0];  
    }

    @Override
    public void add(T t) {
        // TODO Auto-generated method stub
        sql = this.getSql(SQL_INSERT);   //獲取sql.
        // 賦值.
        try {
            argType = setArgs(t, SQL_INSERT);
            statement = JdbcDaoHelper.getPreparedStatement(sql);  //實例化PreparedStatement.
            //為sql語句賦值.
            statement = JdbcDaoHelper.setPreparedStatementParam(statement,
                    argType);
            statement.executeUpdate(); //執行語句.
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            JdbcDaoHelper.release(statement, null);  //釋放資源.
        }
    }

    @Override
    public void delete(T t) {
        // TODO Auto-generated method stub
        sql = this.getSql(SQL_DELETE);
        try {
            argType = this.setArgs(t, SQL_DELETE);
            statement = JdbcDaoHelper.getPreparedStatement(sql);
            statement = JdbcDaoHelper.setPreparedStatementParam(statement,
                    argType);
            statement.executeUpdate();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            JdbcDaoHelper.release(statement, null);
        }
    }

    @Override
    public void update(T t) {
        // TODO Auto-generated method stub
        sql = this.getSql(SQL_UPDATE);
        try {
            argType = setArgs(t, SQL_UPDATE);
            statement = JdbcDaoHelper.getPreparedStatement(sql);
            statement = JdbcDaoHelper.setPreparedStatementParam(statement,
                    argType);
            statement.executeUpdate();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            JdbcDaoHelper.release(statement, null);
        }
    }

    @Override
    public T select(T t) {
        // TODO Auto-generated method stub
        sql = this.getSql(SQL_SELECT);
        T obj = null;
        try {
            argType = setArgs(t, SQL_SELECT);
            statement = JdbcDaoHelper.getPreparedStatement(sql);
            statement = JdbcDaoHelper.setPreparedStatementParam(statement,
                    argType);
            rs = statement.executeQuery();
            Field fields[] = EntityClass.getDeclaredFields();
            while (rs.next()) {
                obj = EntityClass.newInstance();
                for (int i = 0; i < fields.length; i++) {
                    fields[i].setAccessible(true);
                    fields[i].set(obj, rs.getObject(fields[i].getName()));
                }
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return obj;

    }

    // sql拼接函數 形如 : insert into User(id,username,password,email,grade) values(?,?,?,?,?)
    private String getSql(String operator) {

        StringBuffer sql = new StringBuffer();
        // 通過反射獲取實體類中的所有變量
        Field fields[] = EntityClass.getDeclaredFields();

        // 插入操作
        if (operator.equals(SQL_INSERT)) {
            sql.append("insert into " + EntityClass.getSimpleName());
            sql.append("(");
            for (int i = 0; fields != null && i < fields.length; i++) {
                fields[i].setAccessible(true);    //這句話必須要有,否則會拋出異常.
                String column = fields[i].getName();
                sql.append(column).append(",");
            }
            sql = sql.deleteCharAt(sql.length() - 1);
            sql.append(") values (");
            for (int i = 0; fields != null && i < fields.length; i++) {
                sql.append("?,");
            }
            sql.deleteCharAt(sql.length() - 1);
            // 是否需要添加分號
            sql.append(")");
        } else if (operator.equals(SQL_UPDATE)) {
            sql.append("update " + EntityClass.getSimpleName() + " set ");
            for (int i = 0; fields != null && i < fields.length; i++) {
                fields[i].setAccessible(true);
                String column = fields[i].getName();
                if (column.equals("id")) {
                    continue;
                }
                sql.append(column).append("=").append("?,");
            }
            sql.deleteCharAt(sql.length() - 1);
            sql.append(" where id=?");
        } else if (operator.equals(SQL_DELETE)) {
            sql.append("delete from " + EntityClass.getSimpleName()
                    + " where id=?");
        } else if (operator.equals(SQL_SELECT)) {
            sql.append("select * from " + EntityClass.getSimpleName()
                    + " where id=?");
        }
        return sql.toString();
    }

    // 獲取參數.
    private Object[] setArgs(T entity, String operator)
            throws IllegalArgumentException, IllegalAccessException {

        Field fields[] = EntityClass.getDeclaredFields();
        if (operator.equals(SQL_INSERT)) {

            Object obj[] = new Object[fields.length];
            for (int i = 0; obj != null && i < fields.length; i++) {
                fields[i].setAccessible(true);
                obj[i] = fields[i].get(entity);
            }
            return obj;

        } else if (operator.equals(SQL_UPDATE)) {

            Object Tempobj[] = new Object[fields.length];
            for (int i = 0; Tempobj != null && i < fields.length; i++) {
                fields[i].setAccessible(true);
                Tempobj[i] = fields[i].get(entity);
            }

            Object obj[] = new Object[fields.length];
            System.arraycopy(Tempobj, 1, obj, 0, Tempobj.length - 1);
            obj[obj.length - 1] = Tempobj[0];
            return obj;

        } else if (operator.equals(SQL_DELETE)) {

            Object obj[] = new Object[1];
            fields[0].setAccessible(true);
            obj[0] = fields[0].get(entity);
            return obj;
        } else if (operator.equals(SQL_SELECT)) {

            Object obj[] = new Object[1];
            fields[0].setAccessible(true);
            obj[0] = fields[0].get(entity);
            return obj;
        }
        return null;
    }

}

 這樣就對BaseDao進行了具體的實現.因為我們的User表還有其他額外的操作,那么我們只需要這樣.它通過繼承BaseDaoImp,然后實現UserDao接口,那么UserDaoImp就即具有了BaseDaoImp的通用方法,還具有了自己其他的額外方法.

package com.example.daoimp; import java.lang.reflect.ParameterizedType; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; import com.example.bean.User; import com.example.dao.UserDao; import com.example.helper.JdbcDaoHelper; public class UserDaoImp extends BaseDaoImp<User> implements UserDao { private Class<?> EntityClass; private String sql; private PreparedStatement statement; private ResultSet rs; private List<User> list; public UserDaoImp() { ParameterizedType type = (ParameterizedType) getClass() .getGenericSuperclass(); EntityClass = (Class<?>) type.getActualTypeArguments()[0]; } @Override public List<User> findAll() { // TODO Auto-generated method stub StringBuffer b = new StringBuffer(); list = new ArrayList<User>(); sql = b.append("select * from " + EntityClass.getSimpleName()) .toString(); try { statement = JdbcDaoHelper.getPreparedStatement(sql); rs = statement.executeQuery(); while (rs.next()) { User user = new User(); user.setId(rs.getInt("id")); user.setPassword(rs.getString("password")); user.setEmail(rs.getString("email")); user.setUsername(rs.getString("username")); user.setGrade(rs.getInt("grade")); list.add(user); } } catch (Exception e) { // TODO Auto-generated catch block  e.printStackTrace(); } return list; } }

 有 了他們,我們就可以進行具體的操作了,如果還有Shop表,那么同理我們可以去創建一個ShopDao去繼承BaseDao,然后在自己的ShopDao 定義其他的額外方法就可以了.當表非常多的時候,我們就可以采用這種思想進行封裝.這樣寫出的代碼質量就顯得非常的高,耦合度也非常的松散.

 在添加上工具類.

package com.example.helper; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class JdbcDaoHelper { private static final String USER = "root"; private static final String PASSWORD = ""; private static final String URL = "jdbc:mysql://localhost:3306/usermanager"; private static Connection con; // 獲取數據庫連接對象 public static Connection getConnection() { if (con == null) { try { Class.forName("com.mysql.jdbc.Driver"); con = DriverManager.getConnection(URL, USER, PASSWORD); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block  e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block  e.printStackTrace(); } } else { return con; } return con; } public static PreparedStatement getPreparedStatement(String sql) throws SQLException { return getConnection().prepareStatement(sql); } public static PreparedStatement setPreparedStatementParam( PreparedStatement statement, Object obj[]) throws SQLException { for (int i = 0; i < obj.length; i++) { statement.setObject(i + 1, obj[i]); } return statement; } // 釋放資源 public static void release(PreparedStatement ps, ResultSet rs) { try { if (con != null) { con.close(); con = null; } if (ps != null) { ps.close(); ps = null; } if (rs != null) { rs.close(); rs = null; } } catch (Exception e) { // TODO: handle exception  } } }

 最后加上UserBean.

package com.example.bean; public class User { private int id; private String username; private String password; private String email; private int grade; public User(){ } public User(int id,String username,String password,String email,int grade){ this.id = id; this.username = username; this.password = password; this.email = email; this.grade = grade; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public int getGrade() { return grade; } public void setGrade(int grade) { this.grade = grade; } }

 測試類.

package com.example.jdbc; import java.util.List; import com.example.bean.User; import com.example.daoimp.UserDaoImp; public class Main { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub  List<User>list = null; UserDaoImp imp = new UserDaoImp(); list = imp.findAll(); for(User user:list){ System.out.println(user.getId()+" "+user.getUsername()+" "+user.getPassword()+" "+user.getEmail()+" "+user.getGrade()); } //insert操作. User user = new User(); user.setId(1); user.setUsername("代碼如風"); user.setPassword("123456"); user.setEmail("123"); user.setGrade(5); imp.add(user); //update操作. User user_1 = new User(); user.setId(1); user.setUsername("心靜如水"); user.setPassword("123456"); user.setEmail("123"); user.setGrade(5); imp.update(user_1); } }

 注意(別忘了引入mysql.jar包.)

 最后放上一個源代碼:files.cnblogs.com/files/RGogoing/JDBCDao.rar

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM