⛳ 一个通用的数据库操作类
该数据库操作类较为偏向底层,除基本的JDBC
的操作外,还结合了 反射
、泛型
的知识,里边编写了对数据库的更新、删除、插入、普通查询、复杂查询等操作,是一个通用的数据库操作类。
🎁 代码:
package asia.mrgu.myssm.basedao;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public abstract class BaseDao<T> {
public final String DRIVER = "com.mysql.cj.jdbc.Driver";
public final String URL = "jdbc:mysql://localhost:3306/fruitdb?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC";
public final String USER = "root";
public final String PWD = "1234";
protected Connection conn;
protected PreparedStatement ps;
protected ResultSet rs;
// T的Class对象
private Class entityClass;
public BaseDao() {
// getClass() 获取Class对象,当前我们执行的是new FruitDaoImpl() ,创建的是FruitDaoImpl的实例
// 那么子类构造方法内部首先会调用父类 (BaseDao)的无参构造方法
// 因此此处的getClass() 会被执行,但是getClass获取的是FruitDaoImpl的Class
// 所以getGenericSuperclass()获取到的是BaseDao的class
/**
* getClass().getGenericSuperclass() 是一个 Java 反射 API,用于获取当前类的直接超类的 Type。它返回一个表示当前类的直接超类的 Type 对象。如果当前类没有超类,则返回 Object.class。需要注意的是,如果当前类是接口,则该方法返回的 Type 对象表示该接口的直接超接口。
*/
Type generictype = getClass().getGenericSuperclass();
// ParameterType 参数化类型
Type[] actualTypeArguments = ((ParameterizedType) generictype).getActualTypeArguments();
// 获取到的 <T> 中的 T 的真实的类型
Type actualType = actualTypeArguments[0];
try {
entityClass = Class.forName(actualType.getTypeName());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
protected Connection getConn() {
try {
// 1、加载驱动
Class.forName(DRIVER);
// 2、通过驱动管理器获取链接对象
return DriverManager.getConnection(URL, USER, PWD);
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
protected void close(ResultSet rs, PreparedStatement ps, Connection conn) {
try {
if (rs != null) {
rs.close();
}
if (ps != null) {
ps.close();
}
if (conn != null && !conn.isClosed()) {
conn.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
// 给预处理命令对象设置参数
private void setParams(PreparedStatement ps, Object... params) throws SQLException {
if (params != null && params.length > 0) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
}
}
// 执行更新,返回影响行数
protected int executeUpdate(String sql, Object... params) {
boolean insertFlag = false;
/**
* trim() 方法可以去除字符串两端的空格,返回一个新的字符串。
* toUpperCase() 方法可以将字符串全部转换为大写字母,返回一个新的字符串。
* startsWith("INSERT") 方法可以检查当前字符串是否以 "INSERT" 开头,返回一个布尔值。
*/
insertFlag = sql.trim().toUpperCase().startsWith("INSERT");
try {
conn = getConn();
if (insertFlag) {
/**
* 创建一个PreparedStatement对象,并指定了一个参数Statement.RETURN_GENERATED_KEYS
* ,表示希望在执行SQL语句后返回自动生成的主键值。
* 在执行插入操作时,如果表中的主键是自动生成的(例如使用 AUTO_INCREMENT 关键字),
* 则可以使用 Statement.RETURN_GENERATED_KEYS 参数来获取插入后自动生成的主键值。
*/
ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
} else {
ps = conn.prepareStatement(sql);
}
setParams(ps, params);
int count = ps.executeUpdate();
if (insertFlag) {
/**
* 插入成功后,可以通过 ps.getGeneratedKeys() 方法获取自动生成的主键值的结果集,
* 然后通过 rs.getInt(1) 方法获取主键值。
*/
rs = ps.getGeneratedKeys();
if (rs.next()) {
return ((Long) rs.getLong(1)).intValue();
}
}
return count;
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
close(rs, ps, conn);
}
}
// 通过反射技术给obj对象的property属性赋propertyValue值
private void setValue(Object obj, String property, Object propertyValue) {
Class<?> aClass = obj.getClass();
try {
// 获取property这个字符串对应的属性名,比如,"fid" 去找obj对象中的fid属性
/**
* 调用clazz.getDeclaredField(property)方法获取到属性名为property的Field对象,如果该属性不存在则返回null。
*/
Field field = aClass.getDeclaredField(property);
if (field != null) {
/**
* 调用field.setAccessible(true)方法将该Field对象设置为可访问状态,以便在后续操作中可以访问该属性。
* 如果属性的访问权限为private,需要通过setAccessible方法将其设置为可访问状态才能够进行操作。
*/
field.setAccessible(true);
/**
* 调用field.set(obj, propertyValue)方法将obj对象的属性值设置为propertyValue。
*/
field.set(obj, propertyValue);
}
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
// 执行复杂查询,返回例如统计结果
protected Object[] executeComplexQuery(String sql, Object... params) {
try {
conn = getConn();
ps = conn.prepareStatement(sql);
setParams(ps,params);
rs = ps.executeQuery();
// 通过rs可以获取结果集的元数据
// 元数据:描述结果集数据的数据,简单讲,就是这个结果集有哪些列,什么类型等
ResultSetMetaData metaData = rs.getMetaData();
// 获取结果集的列数
int columnCount = metaData.getColumnCount();
/**
* 定义了一个 Object 类型的数组 columnValueArr,数组的长度为 columnCount。该数组用于存储数据库表中一行数据的所有列的值,每个列的值都是一个 Object 类型的对象。通常在使用 JDBC 连接数据库时,通过查询 ResultSet 对象获取到一行数据的所有列的值,然后将这些值存储到一个数组中方便后续处理。
*/
Object[] columnValueArr = new Object[columnCount];
// 解析rs
if (rs.next()){
for (int i = 0; i < columnCount; i++) {
/**
* rs.getObject方法的列索引是从1开始的,而不是从0开始。
*/
Object columnValue = rs.getObject(i + 1);
columnValueArr[i] = columnValue;
}
return columnValueArr;
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
close(rs,ps,conn);
}
return null;
}
// 执行查询,返回单个实体对象
protected T load(String sql , Object... params){
try {
conn = getConn();
ps = conn.prepareStatement(sql);
setParams(ps,params);
rs = ps.executeQuery();
// 通过rs可以获取结果集的元数据
// 元数据:描述结果集数据的数据,简单讲,就是这个结果集有哪些列,什么类型等
ResultSetMetaData metaData = rs.getMetaData();
// 获取结果集的列数
int columnCount = metaData.getColumnCount();
// 解析rs
if (rs.next()){
T entity = (T)entityClass.newInstance();
for (int i = 0; i < columnCount; i++) {
String columnName = metaData.getColumnName(i + 1);
Object columnValue = rs.getObject(i + 1);
setValue(entity,columnName,columnValue);
}
return entity;
}
} catch (SQLException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return null;
}
// 执行查询,返回 List
protected List<T> executeQuery(String sql, Object... params){
List<T> list = new ArrayList<>();
try {
conn = getConn();
ps = conn.prepareStatement(sql);
setParams(ps,params);
rs = ps.executeQuery();
//通过rs可以获取结果集的元数据
//元数据:描述结果集数据的数据 , 简单讲,就是这个结果集有哪些列,什么类型等等
ResultSetMetaData metaData = rs.getMetaData();
// 获取结果集的列数
int columnCount = metaData.getColumnCount();
// 解析rs
while (rs.next()){
T entity = (T)entityClass.newInstance();
for (int i = 0; i < columnCount; i++) {
String columnName = metaData.getColumnName(i + 1);
Object columnValue = rs.getObject(i + 1);
setValue(entity,columnName,columnValue);
}
list.add(entity);
}
} catch (SQLException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return list;
}
}