JDBC通用增删改查方法实现

以水果库存管理系统为例,实现通用的JDBC增删改查方法。

1、实体类和表

ORMObject/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版权协议,转载请附上原文出处链接和本声明。