前言
把最统一的查询和修改(统一增删更新)操作封装到BaseDAO抽象类中,其它表操作可以通过继承该抽象类来实现自定义的CRUD操作。
一、BaseDAO抽象类
package com.xhu.java.jdbc.dao;
import com.xhu.java.jdbc.JDBCUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* @function 通用功能,如查询一个或多个,修改操作,其它操作如聚集函数
*/
public abstract class BaseDAO<T> {
private Class<T> c = null;
//获取BaseDAO子类所继承父类BaseDAO时所给的实际泛型类型。毕竟只能new BaseDAO的子类,这里的this必然指BaseDAO子类对象。这个抽象类无法new出对象
{
Type genericSuperclass = this.getClass().getGenericSuperclass();
ParameterizedType paramT = (ParameterizedType) genericSuperclass;
c = (Class<T>) paramT.getActualTypeArguments()[0];
}
/**
* @param conn 一个事务的连接
* @param sql
* @param args
* @return
* @throws Exception
* @function 获取多条记录, 通用与所有类型
*/
public List<T> getMultiInstance(Connection conn, String sql, Object... args) {
//1.初始化
PreparedStatement ps = null;
ResultSet res = null;
//返回结果
List<T> resList = new ArrayList<>();
try {
//2.获取预处理SQL对象
ps = conn.prepareStatement(sql);
//3.占位符赋值
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//4.执行SQL
res = ps.executeQuery();
//5.处理结果集
/**
* @Reflect
* res将查询出数据的字段数据即元数据信息封装在Meta-Data中
* 获取元数据,再获取查询的列数以及字段名
* 通过res获取字段的对应值
* 通过反射+字段名把对应值动态注入进去
*/
while (res.next()) {
ResultSetMetaData metaData = res.getMetaData();
int cCount = metaData.getColumnCount();
T t = c.getConstructor(null).newInstance();
//反射注入需要的class对象
Class cusC = t.getClass();
for (int i = 0; i < cCount; i++) {
//获取字段名
String columnName = metaData.getColumnLabel(i + 1);//获取别名对应POJO类,如果没有别名就是原名
Object columnValue = res.getObject(i + 1);
//反射实现动态注入数据
//获取任意权限的字段
Field field = cusC.getDeclaredField(columnName);
//获取private的权限
field.setAccessible(true);
//设置属性值
field.set(t, columnValue);
}
resList.add(t);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.关闭资源,用于事务,不能关闭连接资源,让所有SQL处理串在一个连接里面。
JDBCUtils.closeQuery(ps, null, res);
}
//7.返回查询结果
return resList;
}
/**
* @param conn 一个事务的连接
* @param sql
* @param args
* @return T
* @throws Exception
* @function 各种类的通用查询
*/
public T getInstance(Connection conn, String sql, Object... args) {
//1.不用获取对象,传入一个事务的conn
//2.获取预处理SQL对象
PreparedStatement ps = null;
ResultSet res = null;
T t = null;
try {
ps = conn.prepareStatement(sql);
//3.占位符赋值
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//4.执行SQL
res = ps.executeQuery();
//5.处理结果集
/**
* @Reflect
* res将查询出数据的字段数据即元数据信息封装在Meta-Data中
* 获取元数据,再获取查询的列数以及字段名
* 通过res获取字段的对应值
* 通过反射+字段名把对应值动态注入进去
*/
if (res.next()) {
ResultSetMetaData metaData = res.getMetaData();
int cCount = metaData.getColumnCount();
t = c.getConstructor(null).newInstance();
//反射注入需要的class对象
Class cusC = t.getClass();
for (int i = 0; i < cCount; i++) {
//获取字段名
String columnName = metaData.getColumnLabel(i + 1);//获取别名对应POJO类,如果没有别名就是原名
Object columnValue = res.getObject(i + 1);
//反射实现动态注入数据
String setMethodName = "set" + (char) (columnName.charAt(0) - 32) + columnName.substring(1);
String getMethodName = "get" + (char) (columnName.charAt(0) - 32) + columnName.substring(1);
Method method = cusC.getDeclaredMethod(setMethodName, cusC.getDeclaredMethod(getMethodName, null).getReturnType());
method.invoke(t, columnValue);
/**
* 方式二
* //获取任意权限的字段
* Field field = cusC.getDeclaredField(columnName);
* //获取private的权限
* field.setAccessible(true);
* //设置属性值
* field.set(cus, columnName);
*/
}
}
} catch (Exception throwables) {
throwables.printStackTrace();
} finally {
//6.关闭资源,将一个事务的SQL串在一个连接里,不能关闭SQL
JDBCUtils.closeQuery(ps, null, res);
}
//7.返回查询结果
return t;
}
/**
* @param conn 一个事务的连接
* @param sql 要执行的SQL语句
* @param args SQL中要填充的参数
* @function 增删改统一操作
*/
public void update(Connection conn, String sql, Object... args) {
//1.初始化
PreparedStatement ps = null;
try {
//2.通过连接+sql得到一个把SQL语句预编译的操作SQL对象PreparedStatement
ps = conn.prepareStatement(sql);
//3.为预编译的SQL中占位符设置数值
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//4.执行SQL
ps.execute();
} catch (Exception e) {
System.out.println("插入异常");
e.printStackTrace();
} finally {
//5.关闭连接,连接资源不可关闭,将所有SQL串在一个连接中。
JDBCUtils.close(ps, null);
}
}
/**
* @param conn
* @param sql
* @param args
* @param <T>
* @return
* @function 用于获取聚集函数的值
*/
public <T> T getValue(Connection conn, String sql, Object... args) {
PreparedStatement ps = null;
ResultSet res = null;
try {
//1.获取操作SQL对象
ps = conn.prepareStatement(sql);
//2.设置参数
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//3.执行SQL并获取结果集
res = ps.executeQuery();
//4.处理结果集
if (res.next()) {
return (T) res.getObject(1);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//关闭资源
JDBCUtils.closeQuery(ps, null, res);
}
return null;
}
}
二、自定义CRUD
1、Customer
package com.xhu.java.jdbc.pojo;
import java.sql.Date;
/*
* ORM编程思想 (object relational mapping)
* 一个数据表对应一个java类
* 表中的一条记录对应java类的一个对象
* 表中的一个字段对应java类的一个属性
*
*/
public class Customer {
private int id;
private String name;
private String email;
private Date birth;
public Customer() {
super();
}
public Customer(int id, String name, String email, Date birth) {
super();
this.id = id;
this.name = name;
this.email = email;
this.birth = birth;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Customer [id=" + id + ", name=" + name + ", email=" + email + ", birth=" + birth + "]";
}
}
2、CustomerDAO接口
package com.xhu.java.jdbc.dao;
import com.xhu.java.jdbc.pojo.Customer;
import java.sql.Connection;
import java.sql.Date;
import java.util.List;
/**
* @definition 定义Customer表的一组规范。
*/
public interface CustomerDAO {
/**
* @param conn
* @param cus
* @definition 定义增加
*/
void insert(Connection conn, Customer cus);
/**
* @param conn
* @param id
* @dfinition 定义删除操作,根据id删除一条记录
*/
void delete(Connection conn, int id);
/**
* @param conn
* @param cus
* @definition 定义修改操作,根据cus中的id来修改一条记录。
*/
void update(Connection conn, Customer cus);
/**
* @param conn
* @param id
* @return
* @definition 定义获取一个指定的Customer,根据id来获取
*/
Customer getCustomerById(Connection conn, int id);
/**
* @param conn
* @return
* @definition 定义获取所有Customer
*/
List<Customer> getAll(Connection conn);
/**
* @param conn
* @return
* @function 获取一共多少条记录
*/
long getCount(Connection conn);
/**
* @param conn
* @return
* @function 获取谁最晚出生
*/
Date getMaxbirth(Connection conn);
}
3、DAO实现
package com.xhu.java.jdbc.dao.impl;
import com.xhu.java.jdbc.dao.BaseDAO;
import com.xhu.java.jdbc.dao.CustomerDAO;
import com.xhu.java.jdbc.pojo.Customer;
import java.sql.Connection;
import java.sql.Date;
import java.util.List;
public class CustomerDAOImpl extends BaseDAO<Customer> implements CustomerDAO {
@Override
public void insert(Connection conn, Customer cus) {
String sql = "insert into customers(`name`,email,birth) values(?,?,?)";
update(conn, sql, cus.getName(), cus.getEmail(), cus.getBirth());
}
@Override
public void delete(Connection conn, int id) {
String sql = "delete from customers where id = ?";
update(conn, sql, id);
}
@Override
public void update(Connection conn, Customer cus) {
String sql = "update customers set `name` = ?,email = ?,birth = ? where id = ?";
update(conn, sql, cus.getName(), cus.getEmail(), cus.getBirth(), cus.getId());
}
@Override
public Customer getCustomerById(Connection conn, int id) {
String sql = "select id,`name`,email,birth from customers where id = ?";
return getInstance(conn, sql, id);
}
@Override
public List<Customer> getAll(Connection conn) {
String sql = "select id,`name`,email,birth from customers";
return getMultiInstance(conn, sql);
}
@Override
public long getCount(Connection conn) {
String sql = "select count(1) from customers";
return getValue(conn, sql);
}
@Override
public Date getMaxbirth(Connection conn) {
String sql = "select max(birth) from customers";
return getValue(conn, sql);
}
}
4、Junit4测试
package test.com.xhu.java.jdbc.dao.impl;
import com.xhu.java.jdbc.JDBCUtils;
import com.xhu.java.jdbc.dao.impl.CustomerDAOImpl;
import com.xhu.java.jdbc.pojo.Customer;
import org.junit.Test;
import org.junit.Before;
import org.junit.After;
import java.sql.Connection;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.List;
/**
* CustomerDAOImpl Tester.
*
* @author <Authors name>
* @version 1.0
* @since <pre>11 29, 2021</pre>
*/
public class CustomerDAOImplTest {
private CustomerDAOImpl dao = new CustomerDAOImpl();
private Connection conn = null;
@Before
public void before() throws Exception {
}
@After
public void after() throws Exception {
}
/**
* Method: insert(Connection conn, Customer cus)
*/
@Test
public void testInsert() throws Exception {
conn = JDBCUtils.getConn();
dao.insert(conn, new Customer(1, "李某", "lm@qq.com", new Date(new SimpleDateFormat("yyyy-MM-dd").parse("1998-9-9").getTime())));
JDBCUtils.close(null, conn);
}
/**
* Method: delete(Connection conn, int id)
*/
@Test
public void testDelete() throws Exception {
conn = JDBCUtils.getConn();
dao.delete(conn, 20);
JDBCUtils.close(null, conn);
}
/**
* Method: update(Connection conn, Customer cus)
*/
@Test
public void testUpdate() throws Exception {
conn = JDBCUtils.getConn();
dao.update(conn, new Customer(20, "王某", "wm@qq.com", new Date(new SimpleDateFormat("yyyy-MM-dd").parse("1998-9-9").getTime())));
JDBCUtils.close(null, conn);
}
/**
* Method: getCustomerById(Connection conn, int id)
*/
@Test
public void testGetCustomerById() throws Exception {
conn = JDBCUtils.getConn();
Customer cus = dao.getCustomerById(conn, 1);
System.out.println(cus);
JDBCUtils.close(null, conn);
}
/**
* Method: getAll(Connection conn)
*/
@Test
public void testGetAll() throws Exception {
conn = JDBCUtils.getConn();
List<Customer> cus = dao.getAll(conn);
cus.forEach(System.out::println);
JDBCUtils.close(null, conn);
}
/**
* Method: getCount(Connection conn)
*/
@Test
public void testGetCount() throws Exception {
conn = JDBCUtils.getConn();
long count = dao.getCount(conn);
System.out.println(count);
JDBCUtils.close(null, conn);
}
/**
* Method: getMaxbirth(Connection conn)
*/
@Test
public void testGetMaxbirth() throws Exception {
conn = JDBCUtils.getConn();
Date date = dao.getMaxbirth(conn);
System.out.println(date);
JDBCUtils.close(null, conn);
}
}
总结
1)BaseDAO抽象类封装公共操作
2)各个表的DAO自定义CRUD
3)Junnit4测试,setting中plugins install JunitGuide,然后右键类generate Test类。
参考文献
[1] JDBC 尚硅谷(含SQL文件和各种资料)
[2] 自动生成测试类 百度知道
[3] Idea 添加Junit4的三种方法