1. 程式人生 > >利用java的反射機制實現通用dao

利用java的反射機制實現通用dao

java的反射機制前面已經講過,這裡不再贅述,這篇文章將會利用反射,來實現一個通用的dao層。

1 一般情況下的dao層

我們先來看一下,通常,我們是如何寫dao的。

public class Person {
    public int pid;
    public String pname;
    public double psalary;
    public Date pbirthday;

    public int getPid() {
        return pid;
    }
    public void setPid(int pid) {
        this
.pid = pid; } public String getPname() { return pname; } public void setPname(String pname) { this.pname = pname; } public double getPsalary() { return psalary; } public void setPsalary(double psalary) { this.psalary = psalary; } public
Date getPbirthday() { return pbirthday; } public void setPbirthday(Date pbirthday) { this.pbirthday = pbirthday; } public Person() { super(); } }

這是一個正常的javabean,這裡的變數寫成public不是我 不專業。。。而是我比較懶,方便後面寫程式碼。。。

CREATE DATABASE reflect;

USE reflect;

CREATE
TABLE person( pid INT PRIMARY KEY, pname VARCHAR(255) NOT NULL, psalary FLOAT, pbirthday DATE );
INSERT INTO person VALUES( 1,'jack','1000','1995-11-27' ); CREATE TABLE animal( aid INT PRIMARY KEY, aname VARCHAR(255) NOT NULL ); INSERT INTO animal VALUES( 1,'tom' );

資料庫指令碼檔案,這裡採用mysql資料庫

package com.mystery.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import com.mystery.domain.Person;

public class PersonDao {
    public static List<Person> select() throws SQLException {  
        Connection conn = null;  
        Statement st = null;  
        ResultSet rs = null;  
        List<Person> list = null;  
        try {  
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/reflect","root","123456"); 

            st = conn.createStatement();  
            rs = st.executeQuery("select * from person");  

            list = new ArrayList<Person>();  

            while (rs.next()) {  
                Person p = new Person();  
                p.setPid(rs.getInt("pid"));  
                p.setPname(rs.getString("pname"));
                p.setPsalary(rs.getFloat("psalary"));
                p.setPbirthday(rs.getDate("pbirthday"));

                list.add(p);  
            }  
        } finally {  
            if (rs != null)  
                rs.close();  
            if (st != null)  
                st.close();  
            if (conn != null)  
                conn.close();  
        }  

        return list;  
    }  

    @Test
    public void test(){
        try {
            List<Person> l = select();
            for (int i = 0; i < l.size(); i++) {
                System.out.println(l.get(i).getPname());
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }
}

通常,我們都是這麼寫的,程式碼量也不是很大,為了省事,我就只寫一個select方法了。但是,假如我們又多了一張表,也就是animal,怎麼辦呢?正常情況下,我們是這麼做的:

package com.mystery.domain;

public class Animal {
    public int aid;
    public String aname;

    public int getAid() {
        return aid;
    }
    public void setAid(int aid) {
        this.aid = aid;
    }
    public String getAname() {
        return aname;
    }
    public void setAname(String aname) {
        this.aname = aname;
    }

}

話不多說,直接javabean

package com.mystery.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import com.mystery.domain.Animal;

public class AnimalDao {
    public static List<Animal> select() throws SQLException {  
        Connection conn = null;  
        Statement st = null;  
        ResultSet rs = null;  
        List<Animal> list = null;  
        try {  
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/reflect","root","123456"); 

            st = conn.createStatement();  
            rs = st.executeQuery("select * from animal");  

            list = new ArrayList<Animal>();  

            while (rs.next()) {  
                Animal a = new Animal();  
                a.setAid(rs.getInt("aid"));  
                a.setAname(rs.getString("aname"));

                list.add(a);  
            }  
        } finally {  
            if (rs != null)  
                rs.close();  
            if (st != null)  
                st.close();  
            if (conn != null)  
                conn.close();  
        }  

        return list;  
    }  

    @Test
    public void test(){
        try {
            List<Animal> l = select();
            for (int i = 0; i < l.size(); i++) {
                System.out.println(l.get(i).getAname());
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }
}

然後很無恥的copy&paste一下,稍微做一些修改就好了。感覺也不是很費力。但是,假如又多了10張表呢?copy&paste是那些勤快的人的寫法,像我這種比較懶的,就喜歡一勞永逸的寫法。

一勞永逸的dao

    /**
     * 查詢功能
     * @param cls 要查詢的物件的class物件
     * @return
     * @throws Exception
     */
    public ArrayList<? extends Object> selectAll(Class<?> cls) throws Exception {  
        Connection conn = null;  
        Statement st = null;  
        ResultSet rs = null;  

        ArrayList<Object> list = null;
        Object obj = cls.newInstance();

        try {  
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/reflect","root","123456"); 
            st = conn.createStatement();
            rs = st.executeQuery(String.format(  
                     "select * from %s",cls.getSimpleName()  
                     )); 
            list = new ArrayList<Object>();  

            //這裡如果javabean的屬性是私有的會訪問不到,解決方案有三種
            //1.像我一樣改成public  (極其不推薦)
            //2.新增一句field.setAccessible(true);//設定允許訪問  
            //3.呼叫javabean裡的getset方法
            Field[] fields = cls.getFields(); // 獲得域列表  
            while (rs.next()) {  
                for (Field field : fields) {                
                    Object value = rs.getObject(field.getName());  
                    field.set(obj, value);  // 設定域值  
                }  
                list.add(obj);  
            }  
        } catch (Exception e) {
            e.printStackTrace();
        }finally {  
            if (rs != null)  
                rs.close();  
            if (st != null)  
                st.close();  
            if (conn != null)  
                conn.close();  
        }  

        return list;  
    }

做個測試:

    @Test
    public void testSelectAll(){
        try {
            @SuppressWarnings("unchecked")
            //List<Person> l = (List<Person>) selectAll(Person.class,new Person());
            List<Animal> l = (List<Animal>) selectAll(Animal.class);
            for (int i = 0; i < l.size(); i++) {
                System.out.println(l.get(i).getAname());
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

這樣,一個方法就可以查詢所有的物件,無論是animal還是person,一個dao就夠了,是不是方便了許多?

再寫一個插入和刪除

    /**
     * 插入功能
     * @param obj 要插入的物件
     * @throws Exception
     */
    public void insert(Object obj) throws Exception {  
        Connection conn = null;  
        PreparedStatement st = null;  
        try {  
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/reflect","root","123456"); 

            Class<?> cls = obj.getClass();
            Field[] fields = cls.getFields(); 

            // 下面一段程式碼準備SQL語句的兩部分。  
            StringBuilder sbForFieldName = new StringBuilder();  
            StringBuilder sbForQuestionMark = new StringBuilder();  

            for (int i = 0; i < fields.length; i++) {  
                if(i>0) {  
                    sbForFieldName.append(",");  
                    sbForQuestionMark.append(",");  
                }  
                sbForFieldName.append(fields[i].getName());  
                sbForQuestionMark.append("?");  
            }  

            String FieldNames = sbForFieldName.toString();  
            String QuestionMarks = sbForQuestionMark.toString();  

            // 安全起見,我們需要用prepareStatement處理使用者輸入。  
            // 但是因為類的名稱是可以由程式設計師控制的,我們用String.format生成語句  
            st = conn.prepareStatement(String.format(  
                        "INSERT INTO %s(%s) values(%s)",  
                        cls.getSimpleName(), FieldNames,  QuestionMarks));  

            //填充PreparedStatement  
            for (int i = 0; i < fields.length; i++) {  
                st.setObject(i + 1, fields[i].get(obj));  
            }  

            st.executeUpdate();  
        } finally {  
            if (st != null)  
                st.close();  
            if (conn != null) {  
                conn.close();  
            }  
        }  
    }

    /**
     * 根據id刪除物件  出入的物件必須已經對id賦值並且域名稱必須包含'id'
     * @param obj
     * @return
     * @throws Exception
     */
    public Boolean delete(Object obj) throws Exception {  
        Connection conn = null;  
        Statement st = null;  

        Class<?> cls = obj.getClass();

        try {  
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/reflect","root","123456"); 
            st = conn.createStatement();

            Field[] fields = cls.getFields();
            Object id = null;
            for (Field field : fields) {                
                if(field.getName().indexOf("id") != -1){
                    id = field.get(obj);
                    break;
                }
            } 

            st.executeUpdate(String.format(  
                     "DELETE FROM %s WHERE pid = %s;",cls.getSimpleName(),id  
                     )); 


        } catch (Exception e) {
            e.printStackTrace();
        }finally {   
            if (st != null)  
                st.close();  
            if (conn != null)  
                conn.close();  
        }  

        return true;  
    }


    @SuppressWarnings("deprecation")
    @Test
    public void testInsert(){
        Person p = new Person();
        p.setPid(2);
        p.setPbirthday(new Date(0,1,1));
        p.setPname("jery");
        p.setPsalary(1200);

        try {
            insert(p);
            System.out.println("ok");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @SuppressWarnings("deprecation")
    @Test
    public void testDelete(){
        Person p = new Person();
        p.setPid(2);
        p.setPbirthday(new Date(0,1,1));
        p.setPname("jery");
        p.setPsalary(1200);

        try {
            delete(p);
            System.out.println("ok");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

其他的功能就不貼出來了,原始碼我會放到我的github主頁(https://github.com/CleverFan)上,感興趣的可以去看一下。我也是個新手,有哪些地方不合理的歡迎大家批評指正,互相學習。