JDBC之---DAO 设计模式
准备一个类:
package com.zhou.tools;
public class Student {
/**
* 这些字段都是跟数据库中examstudent数据表示对应的:
*/
// 流水号
private int flowId;
// 考试的类型
private int type;
// 身份证号
private String idCard;
// 准考证号
private String examCard;
// 学生姓名
private String studentName;
// 地址
private String location;
// 考试分数
private int grade;
/**
* 当然会有对应的getter和setter方法:
*/
public int getFlowId() {
return flowId;
}
public void setFlowId(int flowId) {
this.flowId = flowId;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
public String getExamCard() {
return examCard;
}
public void setExamCard(String examCard) {
this.examCard = examCard;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
/**
* 还会有一个有参数的构造器:
* @param flowId
* @param type
* @param idCard
* @param examCard
* @param studentName
* @param location
* @param grade
*/
public Student(int flowId, int type, String idCard, String examCard,
String studentName, String location, int grade) {
super();
this.flowId = flowId;
this.type = type;
this.idCard = idCard;
this.examCard = examCard;
this.studentName = studentName;
this.location = location;
this.grade = grade;
}
/**
* 还会有一个无参数的构造器:
*/
public Student() {
super();
}
/**
* 还会有一个toString()方法来做测试:
*/
@Override
public String toString() {
return "Student [flowId=" + flowId + ", type=" + type + ", idCard="
+ idCard + ", examCard=" + examCard + ", studentName="
+ studentName + ", location=" + location + ", grade=" + grade
+ "]" + "\r\n";
}
}
写一个DAO类:
package com.zhou.tools;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtils;
/**
* 写完是不是感觉像一个接口啊,但是现在写接口意义还不大;
*
* @author Administrator
*
*/
public class DAO {
/**
* INSERT、UPDATE、DELETE操作都可以包含在其中; 这个在前面我们已经写过了, 里面使用的当然是PreparedStatement;
* 我们暂时把Connection放在方法的里面,在后面我们需要把Connection放在
* 方法的外面,因为放里面的话,做不了事务,特别是里面是直接获取;
*
* @param sql
* @param args
*/
public void update(String sql, Object... args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
// 写一个大的try...catch...finally
try {
connection = JdbcTools.getConnection();
preparedStatement = connection.prepareStatement(sql);
// 填充占位符:
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
}
// 然后我执行一个更新:
preparedStatement.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcTools.release_2(null, preparedStatement, connection);
}
}
/**
* 先查一个对象的:我要是查一个对象的话呢,我也传入一个SQL,我也传入
* 一个Object类型的args,我还需要你这个对象是什么类型的啊?Class<T> 查询一条记录,返回对应的对象:
*
* @param clazz
* :是这个类型;
* @param sql
* @param args
* @return
*/
public <T> T get(Class<T> clazz, String sql, Object... args) {
// 声明这个对象;
T entity = null;
// 声明Connection:
Connection connection = null;
// 声明PreparedStatement:
PreparedStatement preparedStatement = null;
// 声明ResultSet:
ResultSet resultSet = null;
try {
/**
* 第一步:获取ResultSet:
*/
// 1.获取Connection:
connection = JdbcTools.getConnection();
// 2.获取PreparedStatement:
preparedStatement = connection.prepareStatement(sql);
// 3.填充占位符:
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
}
// 4.进行查询,得到ResultSet:
resultSet = preparedStatement.executeQuery();
// 5.若ResultSet中有记录,准备一个Map<String,Object> 键:存放列的别名,值:存放列的值:
if (resultSet.next()) {
Map<String, Object> map = new HashMap<String, Object>();
// 6.通过ResultSet对象得到ResultSetMetaData对象:
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
// 7.出来ResultSet,把指针移动一个单位(意味着我这个ResultSet是不是可以处理了啊,但是我有哪些列?不知道列名是什么?也不知道)
// 8.由ResultSetMetaData对象得到结果集中有多少列:
int columCount = resultSetMetaData.getColumnCount();
// 9.由ResultSetMetaData得到每一列的别名,由ResultSet得到具体每一列的值:
for (int i = 0; i < columCount; i++) {
String columLabel = resultSetMetaData.getColumnLabel(i + 1);
Object columValue = resultSet.getObject(i + 1);
// 10.填充Map<String,Object>对象:
map.put(columLabel, columValue);
// 11.用反射创建Class对应的对象:
entity = clazz.newInstance();
// 12.遍历Map<String,Object>对象,用反射填充对象的属性值:其中属性名为Map中的key,属性值为Map中的value:
for (Map.Entry<String, Object> entry : map.entrySet()) {
String fieldName = entry.getKey();
Object fieldValue = entry.getValue();
// ReflectionUtils.setFieldValue(entity, fieldName,
// fieldValue);
BeanUtils.setProperty(entity, fieldName, fieldValue);
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源:
JdbcTools.release_2(resultSet, preparedStatement, connection);
}
return entity;
}
/**
* 获取一个对象的升级版本:
*
* @param clazz
* @param sql
* @param args
* @return
*/
public <T> T newGet(Class<T> clazz, String sql, Object... args) {
// 我直接就得到一个List:
List<T> result = getForList(clazz, sql, args);
if (result.size() > 0) {
// 如果这个集合中有值的话;
return result.get(0);
// 我就把第一个返回不就行了吗;
}
return null;
}
/**
* 查询一组对象:再写一个,还可能是查一组对象吧?比方说我想 把信息全查出来,是不是用List<T>类型啊?
* 通常都是用List,我想全查出来,比方说参数都是一样的; 查询多条记录,返回对应对象的集合:
*
* @param clazz
* @param sql
* @param args
* @return
*/
public <T> List<T> getForList(Class<T> clazz, String sql, Object... args) {
// 创建一个List对象:用来装最后需要查询到的一组对象;
List<T> list = new ArrayList<>();
// 创建Connection:
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
// 写一个大的try……catch……finally:
try {
/**
* 第一步:得到结果集:
*/
// 获取连接:
connection = JdbcTools.getConnection();
// 获取PreparedStatement
preparedStatement = connection.prepareStatement(sql);
// 填充占位符:
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
}
// 获取结果集:
resultSet = preparedStatement.executeQuery();
/**
* 第二步:处理结果集,得到装着一个一个Map(Map里面装着的是一条记录,这个Map的 键是列的别名,值是列的值,
* 一个Map里面装了一条记录的键值对)的List;
*/
// 获得从结果集中得到别名和列的值组成的一条记录的map,然后将含有这条记录和别名的map放到List中的这个List对象;
List<Map<String, Object>> list2 = handleResultSetToMapList(resultSet);
/**
* 第三步:把装着Map的List转换为装着clazz对应类的对象的List:
* 其中:Map对应的Key:是clazz对应对象的属性名,也就是ResultSet中列的别名;
* Map对应的value:是clazz对应的属性值:也就是将ResultSet中列的值赋给的属性值;
*/
/**
* 将这个装着Map的List,与具体类的对象共同分解,并对这个对象的属性赋结果集中的值,然
* 后把每条记录封装成对象放到一个List中:将这个List对象返回; 从而达到返回一组对象的效果:
*/
list = transferMapListToBeanList(clazz, list2);
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcTools.release_2(resultSet, preparedStatement, connection);
}
return list;
}
/**
* 把结果集中的列的别名和每条记录的列的值封装成一个一个Map,然后把这个一个一个的Map装到一个List中:
* 处理结果集,得到一个Map的一个List,返回这个装着Map的List: 其中一个Map对象对应一条记录:
*
* @param resultSet
* @return
* @throws SQLException
*/
private List<Map<String, Object>> handleResultSetToMapList(
ResultSet resultSet) throws SQLException {
// 5.若ResultSet中有记录,准备List<Map<String,Object>>键:存放列的别名,值:存放列的值:
// 也就是把多个Map对象放在一个List里面,其中一个Map对象对应着一条记录;
List<Map<String, Object>> list2 = new ArrayList<>();
// 获取ResultSetMetaData:
// ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
List<String> columlabels = getColumLabels(resultSet);
// 声明一个Map:防止以后都要来创建Map,所以把声明放在外面:
Map<String, Object> map = null;
// 7.处理ResultSet,使用while循环:
while (resultSet.next()) {
// 先创建一个Map;
map = new HashMap<>();
// 每条记录都会创建一个Map;
// 如果要是有的话,我就要去进行for循环: 我要把这个别名都取出来;
// 8.由ResultSetMetaData对象得到结果集中有多少列:
for (String columLabel : columlabels) {// 因为后面通过columLabel来获取值,所以用增强的for循环:
// 获取数据表中列的 值:
Object columValue = resultSet.getObject(columLabel);
// 将别名和值放进Map中:
map.put(columLabel, columValue);
}
// 11.把一条记录的一个Map对象放入5.准备好的List中:
list2.add(map);
// 将每条记录创建的这个Map添加打牌List中;
}
// 返回这个装着记录条数个Map的List;
return list2;
}
/**
* 将属性被赋了值的对象放进List中,返回这个List:
*
* @param clazz
* @param list2
* @return
* @throws InstantiationException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
private <T> List<T> transferMapListToBeanList(Class<T> clazz,
List<Map<String, Object>> list2) throws InstantiationException,
IllegalAccessException, InvocationTargetException {
// 这是我需要返回的值:
List<T> result = new ArrayList<>();
// 声明对象:应该放在这,因为是不是一条记录是一个bean啊;
T bean = null;
/**
* 将装着记录条数个Map的List对象传进来,遍历这个List,用MapMap<String,
* Object>接收:得到的是一个一个的Map;
* 然后将遍历到的每个Map进行再次遍历,得到ResultSet中列的别名,和别名下对应的列的值,从而得到具体对象的属性名,并将列的
* 值赋值给具体对象的类的属性; 然后将这个属性被赋值了的具体的对象放到一个List集合中,然后返回这个List;
*/
// 12.判断List是否为空集合,若不为空,则遍历List,得到一个一个的Map对象,再把一个Map对象转为一个Class参数对应的Object对象:
if (list2.size() > 0) {
// 如果里面有值的话:
// 我对它进行遍历:
for (Map<String, Object> val : list2) {
// 然后我们每有条记录,都创建一个对象,这个对象声明我们放外面:
bean = clazz.newInstance();
// 然后再进行遍历:
for (Map.Entry<String, Object> entry : val.entrySet()) {
String fieldName = entry.getKey();
Object fieldValue = entry.getValue();
// 同时为属性赋值:
BeanUtils.setProperty(bean, fieldName, fieldValue);
}
// 13.把Object对象放入到List中:
// 将放入了一组查询结果对应的一组对象放到List中,返回这个List对象;
// 我把这个bean再放在最开始的那个list里面去:
result.add(bean);
}
}
return result;
}
/**
* 获取结果集的columLabel对应的List: 我为public <T> List<T> getForList(Class<T> clazz,
* String sql, Object... args) 方法写一个方法,以达到优化的效果:
* 我把别名都装到一个List里面,这个方法返回一个List 我用ResultSet来写,所以传个ResultSet作为参数进去
*
* @throws SQLException
*/
private List<String> getColumLabels(ResultSet resultSet)
throws SQLException {
// 创建一个List:
List<String> labels = new ArrayList<>();
// 得到ResultSetMetaData:
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
// 我往List里面放字符串:
for (int i = 0; i < resultSetMetaData.getColumnCount(); i++) {
labels.add(resultSetMetaData.getColumnLabel(i + 1));
}
return labels;
}
/**
* 还有可能返回某个对象的某一个属性值:里面放的东西呢?因为我返回的不是一个具体的对象,
* 而是某一个字符串、某一个基本数据类型,
* 返回某条记录的某一个字段的值,或一个统计的值(比方说:一共有多少条记录,等……)
* @param sql
* @param args
* @return
*/
public <E> E getForValue(String sql, Object... args) {
// 1.得到结果集:该结果集只有一行,且只有一列;
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
// 写一个大的try……catch……finally:
try {
/**
* 第一步:得到结果集:
*/
// 获取连接:
connection = JdbcTools.getConnection();
// 获取PreparedStatement
preparedStatement = connection.prepareStatement(sql);
// 填充占位符:
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
}
// 获取结果集:
resultSet = preparedStatement.executeQuery();
//一行一列,于是写if:
if(resultSet.next()){
//如果有的话:
//我直接返回这一列的值就可以了:
return (E) resultSet.getObject(1);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcTools.release_2(resultSet, preparedStatement, connection);
}
return null;
}
}
写一个测试DAO类里面的方法的测试的类:
package com.zhou.tools;
import static org.junit.Assert.fail;
import java.sql.Date;
import org.junit.Test;
public class DAOTest {
DAO dao = new DAO();
@Test
public void testUpdate() {
// 我来向customers数据表中插入一条记录:
String sql = "INSERT INTO customers(name,email,birthday)"
+ "VALUES(?,?,?)";
new Date(new java.util.Date().getTime()));
}
@Test
public void testGet() {
String sql = "SELECT flow_id flowId, type, id_card idCard,"
+ " exam_card examCard, student_name studentName,"
+ " location, grade FROM examstudent WHERE flow_id = ?";
System.out.println(dao.get(Student.class, sql, 3));
}
@Test
public void testNewGet() {
String sql = "SELECT flow_id flowId, type, id_card idCard,"
+ " exam_card examCard, student_name studentName,"
+ " location, grade FROM examstudent WHERE flow_id = ?";
System.out.println(dao.newGet(Student.class, sql, 3));
}
@Test
public void testGetForList() {
// 写一个sql:
String sql = "SELECT flow_id flowId, type, id_card idCard,"
+ " exam_card examCard, student_name studentName,"
+ " location, grade FROM examstudent";
// List<Student> list = dao.getForList(Student.class, sql);
// System.out.println(list);
System.out.println(dao.getForList(Student.class, sql));
}
@Test
public void testGetForValue() {
// 写一个sql:
String sql = "SELECT exam_Card FROM examstudent" + " WHERE flow_id = ?";
String examCard = dao.getForValue(sql, 3);
System.out.println(examCard);
//我还可以获得一条统计信息:
sql = "SELECT max(grade) FROM examstudent";
int grade = dao.getForValue(sql);
System.out.println(grade);
}
}
本文出自 “IT技术JAVA” 博客,转载请与作者联系!