以水果库存管理系统为例,实现通用的JDBC增删改查方法。
1、实体类和表
ORM(Object/Relation Mapping,对象/关系数据库映射)是一种描述对象与关系数据库之间映射的规范。
a、bean
创建fruit类。
package com.lucky.fruit01.pojo;
public class Fruit {
private Integer fid;
private String name;
private Integer price;
private Integer fcount;
private String remark;
public Fruit() {
}
public Integer getFid() {
return fid;
}
public void setFid(Integer fid) {
this.fid = fid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
public Integer getFcount() {
return fcount;
}
public void setFcount(Integer fcount) {
this.fcount = fcount;
}
public Fruit(Integer fid, String name, Integer price, Integer fcount, String remark) {
this.fid = fid;
this.name = name;
this.price = price;
this.fcount = fcount;
this.remark = remark;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
@Override
public String toString() {
return fid + "\t\t" + name + "\t\t" + price + "\t\t" + fcount + "\t\t" + remark;
}
}
b、表结构和属性
数据库中创建t_fruit表格,表格每个列和bean中每个属性一一对应,表里面每条数据代表了fruit bean的一个实例。
2、连接数据库
获取数据库连接,对数据库中数据进行操作,因为不管增删改查中的哪个操作,都首先需要连接数据库。操作完成之后,需要释放资源,因此将数据库连接和释放资源部分代码放到工具类JDBCUtils中。
package com.lucky.fruit01.utils.jdbc;
import java.lang.reflect.ParameterizedType;
import java.sql.*;
import java.util.Objects;
/**
* 数据库连接工具
*/
public class JDBCUtils {
/**
* 获取数据库连接对象
*
* @param driver driver
* @param user user
* @param pwd pwd
* @return 数据库连接对象
*/
public static Connection getConnection(String driver, String url, String user, String pwd) {
try {
Class.forName(driver);
return DriverManager.getConnection(url, user, pwd);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return null;
}
/**
* 关闭连接
*
* @param resultSet resultSet
* @param preparedStatement preparedStatement
* @param connection connection
*/
public static void close(ResultSet resultSet, PreparedStatement preparedStatement, Connection connection) {
try {
if (Objects.nonNull(resultSet)) {
resultSet.close();
}
if (Objects.nonNull(preparedStatement)) {
preparedStatement.close();
}
if (Objects.nonNull(connection)) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3、通用DAO层代码
a、增删改通用代码
对于增删改操作,其操作流程可以总结为
提取函数:
输入参数:
1、sql
2、需要填充的参数,因为不知道需要填充参数的类型和数量,所以这个参数设置为object类型的可变参数。
输出参数:
更新和删除操作为影响行数;
增加操作为数据库主键。
/**
* 提取公共方法,执行更新,删除,和增加
*/
protected int executeUpdate(String sql, Object ... params) {
int count = 0;
boolean isInsert;
isInsert = sql.trim().toUpperCase(Locale.ROOT).equals("INSERT");
try {
connection = JDBCUtils.getConnection(DRIVER, URL, USER, PWD) ;
// sql 是传入进来的
// 创建预处理命令对象, PreparedStatement 承载java程序和数据库之间的数据运输,如果Connection是路,那么PreparedStatement就是路上的车
if (isInsert) {
// 返回创建后的主键, 在利用外键创建关联关系时很有用
preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
} else {
preparedStatement = connection.prepareStatement(sql);
}
// 填充参数
setParam(preparedStatement, params);
count = preparedStatement.executeUpdate();
// 获取执行结果
resultSet = preparedStatement.getResultSet();
// 如果执行结果不为空,说明是插入操作,返回自增主键
if (resultSet != null) {
return ((Long) resultSet.getLong(0)).intValue();
}
return count;
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(resultSet, preparedStatement, connection);
}
return count;
}
b、查询通用代码
查询操作返回的是表格对应的bean。也就是说查询老师表格返回Teacher bean, 查询学生表格返回Student bean。怎样获取查询表格对应的bean类型呢,这里可以利用反射。
定义抽象类BaseDao,并在构造函数中获取子类类型。
/**
* 抽取增删改查的公共方法,使得其可以应用于不同表的增删改查, 定义成抽象类
* T 在具体某个类继承抽象类的时候就会指定
*/
public abstract class BaseDao<T> {
protected Connection connection;
protected String sql;
protected PreparedStatement preparedStatement;
protected ResultSet resultSet;
/**
* 查询时具体用到的实例对象, 也就是T
*/
private Class entityClass;
public BaseDao() {
//getClass() 获取Class对象,当前我们执行的是new FruitDAOImpl() , 创建的是FruitDAOImpl的实例
//那么子类构造方法内部首先会调用父类(BaseDAO)的无参构造方法
//因此此处的getClass()会被执行,但是getClass获取的是FruitDAOImpl的Class
//所以getGenericSuperclass()获取到的是BaseDAO的Class
Type type = getClass().getGenericSuperclass();
// ParameterizedType 参数化类型,继承的子类对T传进来的具体值
Type[] actualTypeArguments = ((ParameterizedType)type).getActualTypeArguments();
// 这里主要是为了获取执行添加操作时, 获取返回对象的具体类型
Type actualType = actualTypeArguments[0];
try {
entityClass = Class.forName(actualType.getTypeName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
子类继承抽象类,并指定类型。
public class FruitDAOImpl extends BaseDao<Fruit> implements FruitDao
怎样将查出来的列,放到bean的每一个属性上,这里也是利用反射
获取元数据列数,循环获取列名和列值,放入对象属性中。
**注意:**当列名和属性名不对应时,这个方法就不适用了,也就是MyBatis里面为什么需要通过mapper标签创建对应关系的原因了。
/**
* 查询公共方法
*
* @param sql sql
* @param params params
* @return 查询结果
*/
protected List<T> executeQuery(String sql, Object... params) {
List<T> result = new ArrayList<>();
try {
Class.forName(DRIVER);
connection = JDBCUtils.getConnection(DRIVER, URL, USER, PWD) ;
preparedStatement = connection.prepareStatement(sql);
setParam(preparedStatement, params);
resultSet = preparedStatement.executeQuery();
// 怎样解析类
while (resultSet.next()) {
try {
// 获取具体实体类类型
T entity = (T) entityClass.newInstance();
// 获取元数据
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
// 获取结果集列数
int count = resultSetMetaData.getColumnCount();
for (int i = 0; i < count; i++) {
String colName = resultSetMetaData.getColumnName(i + 1);
Object colValue = resultSet.getObject(i + 1);
setValue(entity, colName, colValue);
}
result.add(entity);
} catch (InstantiationException | IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(resultSet, preparedStatement, connection);
}
return result;
}
抽象类代码
package com.lucky.fruit01.dao.base;
import com.lucky.fruit01.pojo.Fruit;
import com.lucky.fruit01.utils.jdbc.JDBCUtils;
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;
import java.util.Locale;
import java.util.Objects;
/**
* 抽取增删改查的公共方法,使得其可以应用于不同表的增删改查, 定义成抽象类
* T 在具体某个类继承抽象类的时候就会指定
*/
public abstract class BaseDao<T> {
public String DRIVER = "com.mysql.jdbc.Driver";
public String URL = "jdbc:mysql://localhost:3306/fruitdb?useUnicode=true&characterEncoding=utf-8";
public String USER = "root";
public String PWD = "123456";
protected Connection connection;
protected String sql;
protected PreparedStatement preparedStatement;
protected ResultSet resultSet;
/**
* 查询时具体用到的实例对象, 也就是T
*/
private Class entityClass;
public BaseDao() {
//getClass() 获取Class对象,当前我们执行的是new FruitDAOImpl() , 创建的是FruitDAOImpl的实例
//那么子类构造方法内部首先会调用父类(BaseDAO)的无参构造方法
//因此此处的getClass()会被执行,但是getClass获取的是FruitDAOImpl的Class
//所以getGenericSuperclass()获取到的是BaseDAO的Class
Type type = getClass().getGenericSuperclass();
// ParameterizedType 参数化类型,继承的子类对T传进来的具体值
Type[] actualTypeArguments = ((ParameterizedType)type).getActualTypeArguments();
// 这里主要是为了获取执行添加操作时, 获取返回对象的具体类型
Type actualType = actualTypeArguments[0];
try {
entityClass = Class.forName(actualType.getTypeName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 提取公共方法,执行更新,删除,和增加
*/
protected int executeUpdate(String sql, Object ... params) {
int count = 0;
boolean isInsert;
isInsert = sql.trim().toUpperCase(Locale.ROOT).equals("INSERT");
try {
connection = JDBCUtils.getConnection(DRIVER, URL, USER, PWD) ;
// sql 是传入进来的
// 创建预处理命令对象, PreparedStatement 承载java程序和数据库之间的数据运输,如果Connection是路,那么PreparedStatement就是路上的车
if (isInsert) {
// 返回创建后的主键, 在利用外键创建关联关系时很有用
preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
} else {
preparedStatement = connection.prepareStatement(sql);
}
// 填充参数
setParam(preparedStatement, params);
count = preparedStatement.executeUpdate();
// 获取执行结果
resultSet = preparedStatement.getResultSet();
// 如果执行结果不为空,说明是插入操作,返回自增主键
if (resultSet != null) {
return ((Long) resultSet.getLong(0)).intValue();
}
return count;
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(resultSet, preparedStatement, connection);
}
return count;
}
/**
* 查询公共方法
*
* @param sql sql
* @param params params
* @return 查询结果
*/
protected List<T> executeQuery(String sql, Object... params) {
List<T> result = new ArrayList<>();
try {
Class.forName(DRIVER);
connection = JDBCUtils.getConnection(DRIVER, URL, USER, PWD) ;
preparedStatement = connection.prepareStatement(sql);
setParam(preparedStatement, params);
resultSet = preparedStatement.executeQuery();
// 怎样解析类
while (resultSet.next()) {
try {
// 获取具体实体类类型
T entity = (T) entityClass.newInstance();
// 获取元数据
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
// 获取结果集列数
int count = resultSetMetaData.getColumnCount();
for (int i = 0; i < count; i++) {
String colName = resultSetMetaData.getColumnName(i + 1);
Object colValue = resultSet.getObject(i + 1);
setValue(entity, colName, colValue);
}
result.add(entity);
} catch (InstantiationException | IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(resultSet, preparedStatement, connection);
}
return result;
}
//执行复杂查询,返回例如统计结果
protected Object[] executeComplexQuery(String sql , Object... params){
try {
connection = JDBCUtils.getConnection(DRIVER, URL, USER, PWD) ;
preparedStatement = connection.prepareStatement(sql);
setParam(preparedStatement,params);
resultSet = preparedStatement.executeQuery();
//通过rs可以获取结果集的元数据
//元数据:描述结果集数据的数据 , 简单讲,就是这个结果集有哪些列,什么类型等等
ResultSetMetaData rsmd = resultSet.getMetaData();
//获取结果集的列数
int columnCount = rsmd.getColumnCount();
Object[] columnValueArr = new Object[columnCount];
//6.解析rs
if(resultSet.next()){
for(int i = 0 ; i<columnCount;i++){
Object columnValue = resultSet.getObject(i+1);
columnValueArr[i]=columnValue;
}
return columnValueArr ;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(resultSet, preparedStatement, connection);
}
return null ;
}
private void setParam(PreparedStatement preparedStatement, Object ... params) throws SQLException {
if (Objects.nonNull(params) && params.length > 0) {
for (int i = 0; i < params.length; i++) {
preparedStatement.setObject(i+1, params[i]);
}
}
}
/**
* 通过反射技术给object对象的属性property属性赋值
*
* @param object object
* @param property 属性名称
* @param propertyValue 属性值
*/
private void setValue(Object object, String property, Object propertyValue) throws NoSuchFieldException {
Class clazz = object.getClass();
Field field = clazz.getDeclaredField(property);
field.setAccessible(true); // 防止属性私有
try {
field.set(object, propertyValue);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
实现类例子
package com.lucky.fruit01.dao.impl;
import com.lucky.fruit01.dao.FruitDao;
import com.lucky.fruit01.dao.base.BaseDao;
import com.lucky.fruit01.pojo.Fruit;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class FruitDAOImpl extends BaseDao<Fruit> implements FruitDao {
@Override
public int addFruit(Fruit fruit) {
sql = "insert into t_fruit values(0, ?, ?, ?, ?)";
return super.executeUpdate(sql, fruit.getName(), fruit.getPrice(), fruit.getFcount(), fruit.getRemark());
}
@Override
public int deleteFruitByName(String name) {
sql = "delete from t_fruit where fname=?";
return super.executeUpdate(sql, name);
}
@Override
public int updateFruit(Fruit fruit) {
sql = "update t_fruit set fcount=? where fname=?";
return super.executeUpdate(sql, fruit.getFcount(), fruit.getName());
}
@Override
public Fruit getFruit(String name) {
sql = "select * from t_fruit where fname=?";
List<Fruit> fruits = super.executeQuery(sql, name);
if (fruits.size() == 0) {
return null;
}
return fruits.get(0);
}
@Override
public List<Fruit> getList() {
sql = "select * from t_fruit";
return super.executeQuery(sql);
}
}
实现类中接口
package com.lucky.fruit01.dao;
import com.lucky.fruit01.dao.base.BaseDao;
import com.lucky.fruit01.pojo.Fruit;
import java.util.List;
/**
* DAO data access object,
* DAO 层方法是单精度的,就是每一个方法只完成一件事情
*/
public interface FruitDao {
/**
* 增加库存
*
* @param fruit fruit
* @return 添加是否成功
*/
int addFruit(Fruit fruit);
/**
* 删除水果库存
*
* @param name name
* @return 删除是否成功
*/
int deleteFruitByName(String name);
/** 修改某种水果记录
*
* @param fruit fruit
* @return 是否更新成功
*/
int updateFruit(Fruit fruit);
/**
* 根据名称查询水果
*
* @param name name
* @return 查询结果
*/
Fruit getFruit(String name);
/**
*
* @return List<Fruit>
*/
List<Fruit> getList();
}
版权声明:本文为Javaer000原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。