您的位置:首页 > 编程语言 > Java开发

利用java的反射机制实现通用dao

2016-08-15 17:27 651 查看
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)上,感兴趣的可以去看一下。我也是个新手,有哪些地方不合理的欢迎大家批评指正,互相学习。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java dao 反射