利用java的反射機制實現通用dao
阿新 • • 發佈:2019-02-14
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)上,感興趣的可以去看一下。我也是個新手,有哪些地方不合理的歡迎大家批評指正,互相學習。