Java -- JDBC学习笔记7、DaoUtils

1、为什么要封装Dao层工具类?

在Dao层中,每次对数据库的增、删、改、查操作存在代码冗余,可以将共有代码抽取封装。

2、哪些代码需要封装?

  • 在修改、删除、添加等操作方法中,每次都需要获取连接对象、获取PreparedStatement、给占位符赋值,释放连接等等。
  • 在查询操作方法中(查询全部和查询所有),也需要获取连接对象,PreparedStatement、ResultSet等对象,还需要循环ResultSet,取值赋值等等。

3、DaoUtils具体实现

  • 修改、删除、新增使用一个通用方法来实现。
  • 查询单个和查询所有用一个通用方法来实现。

3.1、增删改通用方法commonsUpdate()

方法有两个参数,第一个是sql语句、第二个是参数列表。

  • 第二个变量里存放的值是不固定的,因为增删改语句中的参数数量不同,比如:修改需要整张表,删除只需要一个,新增也需要整张表,所以使用Object... args、也就是可变参数。
  • 调用者在调用通用函数、要事先将参数列表定义好,每一个值和sql语句中那一个占位符相对应。
/**
     * 增删改通用方法
     * @param sql 增删改sql语句
     * @param args 参数列表
     * @return 受影响行数
     */
    public int commonUpdate(String sql, Object... args)
    {
        Connection conn = null;
        PreparedStatement preparedStatement = null;
        try
        {
            conn = DBUtils.getConnection();
            preparedStatement = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++)
            {
                //第一个参数是设置的值下标,从1开始,第二个是从参数列表中取值的下标,所以从0开始
                preparedStatement.setObject(i + 1, args[i]);
            }
            int result = preparedStatement.executeUpdate();
            return result;
        }
        catch (SQLException sqlException)
        {
            sqlException.printStackTrace();
        }
        finally
        {
            DBUtils.closeDb(null, preparedStatement, null);
        }
        return 0;
    }
  • service层调用者、修改数据
public int update(Account account)
    {
        String sql = "update Account set pwd=?,balance=?,name=? where id=?";
        Object[] arr = {account.getPwd(), account.getBalance(), account.getName(), account.getId()};
        return daoUtils.commonUpdate(sql, arr);
    }

数组里的值和SQL语句中占位符一一对应。

3.2、查询通用方法commonsSelect()

查询通用方法相对比较麻烦,因为查询的是单个、还是多个不知道,取值时候也不清楚是哪张表、表中有哪些字段,取出来后赋值给谁、接收用什么类型接收等等。

  • 首先、对于查询多个还是单个,可以使用集合,查询单个、集合中就一条数据,查询多个集合中就多条数据。
  • 对于不知道是那张表、有哪些字段。遍历取值赋值给那个实体类,可以新建一个回调函数来赋值。
  • 回调函数接收的参数就是在通用方法里传来的ResultSet,取出里边的值,给泛型里的实体类赋值,回调函数里的泛型类型就是调用者接收的类型,也就是某个实体类。这样、在通用方法里每循环一次,就调用一次回调函数,而回调函数里知道是那张表,赋值完成后返回,通用函数里使用泛型类型接收后、再添加到list即可。
  • 回调函数要使用面向接口方式、定义接口、针对不同的类、实现接口,传入的泛型类型也是不同的实体类。

3.3、查询通用方法代码实现

  • 新建一个泛型接口、RowMapper
public interface RowMapper<T>
{
    public T getRow(ResultSet resultSet);
}
  • 实现该接口、要遍历那个类、就针对那个类传入对应的泛型类型,比如我这里要遍历Account表、所以:
public class AccountRowMapperImpl implements RowMapper<Account>
{
    @Override
    public Account getRow(ResultSet resultSet)
    {
        Account account = null;
        try
        {
            int id = resultSet.getInt(1);
            String pwd = resultSet.getString(2);
            Double balance = resultSet.getDouble(3);
            String name = resultSet.getString(4);
            account = new Account(id,pwd,balance,name);
            return account;
        }
        catch (SQLException sqlException)
        {
            sqlException.printStackTrace();
        }
        return null;
    }
}

如果是其它表、比如User,那么就再新建一个实现类,泛型参数是User、返回类型也是User。

  • 编写通用方法
/**
     * 查询通用方法
     * @param sql 查询sql语句
     * @param rowMapper 回调接口、调用者要传入接口实现类
     * @param args 参数列表
     * @return 返回值(集合)
     */
    public List<T> commomSelect(String sql, RowMapper<T> rowMapper, Object... args)
    {
        Connection conn = null;
        PreparedStatement preparedStatement = null;
        ResultSet rs = null;
        List<T> list = new ArrayList<>();
        try
        {
            conn = DBUtils.getConnection();
            preparedStatement = conn.prepareStatement(sql);
            if (args != null)
            {
                for (int i = 0; i < args.length; i++)
                {
                    preparedStatement.setObject(i + 1, args[i]);
                }
                rs = preparedStatement.executeQuery();
                while (rs.next())
                {
                    T t = rowMapper.getRow(rs);//回调 -->调用者提供的一个封装方法ORM
                    list.add(t);
                }
            }
        }
        catch (SQLException sqlException)
        {
            sqlException.printStackTrace();
        }
        finally
        {
            DBUtils.closeDb(null, preparedStatement, rs);
        }
        return list;
    }
  • service层调用者、因为这里是查询单个、所以直接list.get(0)即可。
public Account select(int id)
    {
        String sql = "select id,pwd,balance,name from Account where id=?";
        List<Account> list = daoUtils.commomSelect(sql, new AccountRowMapperImpl(), id);
        if (!list.isEmpty())
        {
            return list.get(0);
        }
        return null;
    }

概括来讲、增删改比较简单,返回的就只是受影响行数,而查询比较复杂,不知道查询的是那张表、有哪些字段、就更不知道用那个实体类来接收。添加一个回调函数、通过泛型传入相应的实体类,遍历赋值完成后返回给通用方法,通用方法只负责查询和返回,回调函数赋值取值和赋值。

原文地址:https://www.cnblogs.com/dcy521/p/14732220.html