JDBC-DAO

JDBC-DAO

DAO:Data Access Object,数据访问对象

DAO就是将JAVA对表的操作封装成对象

一张表对应一个实体类,那么一张表的操作对应一个DAO对象

在Java操作数据库时,我们将对同一张表的操作封装起来,供业务层Service去调用,这个类就是DAO层

在dao目录下建立xxxDao接口

在dao目录下建立impl目录建立实现类

在dao目录下建立BaseDAO父类


BaseDAO

基本上每一个数据表都应该有一个对应的DAO接口及其实现类

我们发现所有表的CURD代码重复率很高,所以我们可以将这些重复的代码设计一个公共的父类

注:BaseDAO中是不写SQL代码的,而DAO的实现类是需要写具体的SQL功能的

DAO处理一共分为六步

  1. 注册驱动
  2. 获取链接
  3. 预编译SQL语句
  4. 为占位符赋值,执行SQL,接受返回结果
  5. 处理结果
  6. 释放资源

其中,1、2、6三部我们通过JDBCUtil完成,需要编写BaseDao提取出3、4、5的代码冗余部分

在这里,我们设计三个方法去完成公共部分的增删改查功能:

import com.xiaobai.schedule.util.JDBCUtilV2;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 此类是封装JDBC中 预编译Sql -> 为占位符赋值 -> 处理结果三个步骤的代码
 * 此类直接作为Dao实现类的父类,不重写代码直接继承封装好的方法
 */
public class BaseDAO {

    /**
     * 通用的增删改操作,形参使用可变长参数
     *
     * @param sql    传入增删改的sql语句
     * @param params 传入对应sql语句的参数
     * @return 返回数据库受操作改变的行数
     * @throws SQLException
     */
    public int executeUpdate(String sql, Object... params) throws SQLException {
        //获取连接(通过封装好的工具类JDBCUtilV2)
        Connection connection = JDBCUtilV2.getConnection();
        //预编译SQL语句,来源于形参
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //为SQL语句的占位符提供参数
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                //占位符从1开始,数组下表从0开始
                preparedStatement.setObject(i + 1, params[i]);//这里可以用setObject通配所有参数类型
            }
        }
        int i = preparedStatement.executeUpdate();
        //释放资源
        preparedStatement.close();
        JDBCUtilV2.close();
        //将返回来的行数,直接作为方法的返回值
        return i;
    }

    /**
     * 查询的返回的结果的类型不唯一,使用泛型方法
     * 结果的封装使用反射:告诉调用者告知BaseDao要封装对象的类对象
     *
     * @param clazz  传入实体类的class类(通过.class即可获取)
     * @param sql    传入查询的sql语句
     * @param params 传入对应sql语句的参数
     * @param <T>    结果集合(List)中参数的类型
     * @return 返回一个结果集合,这个结果集合是被查询的结果(单行单列、单行多列、多行多列)
     * @throws Exception
     */
    public <T> List<T> executeQuery(Class<T> clazz, String sql, Object... params) throws Exception {
        //获取连接(通过封装好的工具类JDBCUtilV2)
        Connection connection = JDBCUtilV2.getConnection();
        //预编译SQL语句,来源于形参
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //为SQL语句的占位符提供参数
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                //占位符从1开始,数组下表从0开始
                preparedStatement.setObject(i + 1, params[i]);//这里可以用setObject通配所有参数类型
            }
        }

        //执行SQL,处理结果
        ResultSet resultSet = preparedStatement.executeQuery();
        //处理结果的元数据对象(包含列的名称和数量)
        ResultSetMetaData metaData = resultSet.getMetaData();
        int columnCount = metaData.getColumnCount();//列的数量
        List<T> list = new ArrayList<T>();//建立一个集合存放遍历并返回
        //循环行
        while (resultSet.next()) {
            T t = clazz.newInstance();//每行都是一个T类型的对象t
            //遍历列(注意,结果集的属性从1开始)
            for (int i = 1; i <= columnCount; i++) {
                Object value = resultSet.getObject(i);//通过下标获取值
                String fieldName = metaData.getColumnLabel(i);//通过下标获取列的名称

                //以下代码使用反射,获取到T类的私有属性
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                field.set(t, value);
            }
            list.add(t);//每获取一行变量的对象,就放在集合中
        }

        //释放资源
        resultSet.close();
        preparedStatement.close();
        JDBCUtilV2.close();
        return list;
    }

    /**
     * 通用查询单行单列,单行多列的结果集处理。
     * 通过调用executeQuery方法获得List,对List进行处理
     *
     * @param clazz  传入实体类的class类(通过.class即可获取)
     * @param sql    传入查询的sql语句
     * @param params 传入对应sql语句的参数
     * @param <T>    结果集合(List)中参数的类型
     * @return 返回一个结果(单行单列、单行多列),这个结果不再是集合,而是一个实体类对象
     * @throws Exception
     */
    public <T> T executeQueryBean(Class<T> clazz, String sql, Object... params) throws Exception {
        List<T> list = executeQuery(clazz, sql, params);
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        return null;
    }
}

DAO实现类

在编写BaseDAO 查询的时候,我们注意到一个事情

当利用反射获取到该类的属性时,我们传入的参数时结果集中列的名字

但是Java中使用的是驼峰命名法,也就是说:类中属性名≠结果集列名

我们可以通过给结果集列名起别名的方法解决这个问题

package com.xiaobai.dao.impl;

import com.xiaobai.dao.BaseDAO;
import com.xiaobai.dao.UserDao;
import com.xiaobai.pojo.User;

import java.sql.SQLException;
import java.util.List;

public class UserDaoImpl extends BaseDAO implements UserDao {

    //查询所有
    @Override
    public List<User> selectAll() {
        try {
            String sql = "select u_id userId,u_name userName,u_salary userSalary,u_age userAge from user";//起别名解决反射问题
            return executeQuery(User.class, sql, null);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //根据id查询一条数据
    @Override
    public User selectByUserId(int id) {
        try {
            String sql = "select u_id userId,u_name userName,u_salary userSalary,u_age userAge from user where u_id = ?";
            return executeQueryBean(User.class, sql, id);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //插入一条数据
    @Override
    public int insert(User user) {
        try {
            String sql = "INSERT INTO user(u_name, u_salary, u_age) VALUES (?, ?, ?)";
            return executeUpdate(sql, user.getUserName(), user.getUserSalary(), user.getUserAge());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    //修改员工的薪资
    @Override
    public int update(User user) {
        try {
            String sql = "UPDATE user SET u_salary = ? WHERE u_id = ?";
            return executeUpdate(sql, user.getUserSalary(), user.getUserId());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    //根据id删除一条数据
    @Override
    public int delete(int id) {
        try {
            String sql = "DELETE FROM user WHERE u_id = ?";
            return executeUpdate(sql, id);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}