Java之路(十一):DAO优化

DAO的重构

1.PreparedStatement(预编译处理语句)

Statement接口只能实现静态SQL语句,我们使用PreparedStatement。

PreparedStatement是Statement子接口,表示预编译语句对象,通过占位符?来拼接SQL

创建预编译语句对象

    @Test
    public void testSaveByPreparedStatement() throws Exception{
        String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";//SQL模板
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        //设置占位符参数值
        ps.setString(1,"Dans");
        ps.setString(2,"9999999");
        ps.setString(3,"F");
        ps.setInt(4,25);
        ps.executeUpdate();//注意,没有参数
        JdbcUtil.close(conn,ps,null);
    }

具体实现DAO的代码:

public class StudentDAOImpl implements IStudentDAO {

    @Override
    public void save(Student stu) {
        String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            ps.setString(1,stu.getName());
            ps.setString(2,stu.getId());
            ps.setString(3,stu.getSex());
            ps.setInt(4,stu.getAge());
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,null);
        }
    }

    @Override
    public void delete(String id) {
        String sql = "DELETE FROM student WHERE Student_id = ?";
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            ps.setString(1,id);
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,null);
        }
    }

    @Override
    public void update(String id, Student newStu) {
        String sql = "UPDATE student SET Student_name = ?,sex = ?, age = ? WHERE Student_id = ?";
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            ps.setString(1,newStu.getName());
            ps.setString(2,newStu.getSex());
            ps.setInt(3,newStu.getAge());
            ps.setString(4,id);
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,null);
        }
    }

    @Override
    public Student get(String id) {
        String sql = "SELECT * FROM student WHERE Student_id = ?";
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            ps.setString(1,id);
            rs = ps.executeQuery();
            if (rs.next()) {
                Student stu = new Student();

                stu.setName(rs.getString("Student_name"));
                stu.setSex(rs.getString("sex"));
                stu.setAge(rs.getInt("age"));
                stu.setId(id);
                return stu;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,rs);
        }
        return null;
    }

    @Override
    public List<Student> listAll() {
        List<Student> list = new ArrayList<>();
        String sql = "SELECT * FROM student";
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            rs = ps.executeQuery();

            while (rs.next()) {
                Student stu = new Student();
                stu.setName(rs.getString("Student_name"));
                stu.setSex(rs.getString("sex"));
                stu.setAge(rs.getInt("age"));
                stu.setId(rs.getString("Student_id"));
                list.add(stu);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,rs);
        }
        return list;
    }
}

PreparedStatement原理

用预编译处理语句在预编译池中已经存在了相同的代码,那么1-3步可以省略不写,因此性能会更高。

但是MySQL不支持PreparedStatement,Oracle支持PreparedStatement.

防止SQL注入问题

如果我们使用Statement方式来进行DQL操作,

String sql = "SELECT * FROM student WHERE Student_name = 'admin' AND password = '1234'";

当我把填入的admin换成:' OR 1=1 OR '

即如下:

String sql = "SELECT * FROM student WHERE Student_name = '' OR 1=1 OR '' AND password = '1234'";

只要数据库中存在数据,那么查询就是成功的,这便是SQL注入。

而使用PrepareStatement便可以解决SQL注入问题。

String sql = "SELECT * FROM student WHERE Student_name = ? AND password = ?";

2.事务

我们用转账的例子来进行模拟。

       试想,我们在转账的时候,从我的账户转出1000元,然后服务器收到消息,再将收款方账户增加1000元,如果这两件事是独立的,那么若在我转出的同时,服务器停止工作(断电或者被黑了),那么收款方将无法收到转账消息,但是我的账户已经转出了1000元,那么我将白白损失1000元。

    @Test
    public void test1() throws Exception {
        Connection conn = JdbcUtil.getConnection();
        //--------------检查Bobbui的账户余额-------------------
        String sql = "SELECT * FROM account WHERE name = ? AND balance >= ?";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1, "Bobbui");
        ps.setInt(2, 1000);
        ResultSet rs = ps.executeQuery();
        if (!rs.next()) {
            throw new RuntimeException("余额不足!");
        }

        //---------------减少Bobbui账户1000元------------------
        sql = "UPDATE account SET balance = balance - ? WHERE name = ?";
        ps = conn.prepareStatement(sql);
        ps.setInt(1, 1000);
        ps.setString(2, "Bobbui");
        ps.executeUpdate();
        //使用异常模拟停电
        int a = 1 / 0;

        //--------------增加DANS1000元-------------------------
        sql = "UPDATE account SET balance = balance + ? WHERE name = ?";
        ps = conn.prepareStatement(sql);
        ps.setInt(1, 1000);
        ps.setString(2, "DANS");
        ps.executeUpdate();
        JdbcUtil.close(conn, ps, rs);
    }

因此我们引出事务(Transaction),事务的相关操作:

    @Test
    public void test2() {
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn.setAutoCommit(false);
            //--------------检查Bobbui的账户余额-------------------
            String sql = "SELECT * FROM account WHERE name = ? AND balance >= ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, "Bobbui");
            ps.setInt(2, 1000);
            rs = ps.executeQuery();
            if (!rs.next()) {
                throw new RuntimeException("余额不足!");
            }

            //---------------减少Bobbui账户1000元------------------
            sql = "UPDATE account SET balance = balance - ? WHERE name = ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, 1000);
            ps.setString(2, "Bobbui");
            ps.executeUpdate();
            //使用异常模拟停电
            //int a = 1 / 0;

            //--------------增加DANS1000元-------------------------
            sql = "UPDATE account SET balance = balance + ? WHERE name = ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, 1000);
            ps.setString(2, "DANS");
            ps.executeUpdate();

            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (Exception v) {
                v.printStackTrace();
            }
        } finally {
            JdbcUtil.close(conn, ps, rs);
        }

    }

这样就解决了上述问题。

3.批处理操作

    @Test
    public void testSaveByPreparedStatement_batch() throws Exception{
        String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";//SQL模板
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        for (int i = 0; i < 3000; i++) {
            ps.setString(1,"Dans");
            ps.setString(2,"9999999");
            ps.setString(3,"F");
            ps.setInt(4,25);
            
            ps.addBatch();//添加进批处理中
            if (i % 200 == 0) {
                ps.executeBatch();//执行批量操作
                ps.clearBatch();//清除缓存
                ps.clearParameters();//清除参数
            }
        }
    }

但是,MySQL服务器既不支持PrepareStatement的性能优化,也不支持JDBC中的批量操作,但是在新的JDBC驱动中,我们可以通过设置参数来进行优化:在URL后面缀上?rewriteBatchedStatements=true(在5.1.13版本后都支持)

url=jdbc:mysql:///studentinfo?rewriteBatchedStatements=true

4.BOLB和TEXT类型

    @Test
    public void test1() throws Exception {
        String sql = "INSERT INTO image (img) VALUES (?)";
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setBlob(1,new FileInputStream("D:/1.jpg"));
        ps.executeUpdate();
        JdbcUtil.close(conn,ps,null);
    }

    @Test
    public void test2() throws Exception {
        String sql = "SELECT * FROM image WHERE id = ?";
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setInt(1,1);
        ResultSet rs = ps.executeQuery();
        if (rs.next()) {
            Blob blob = rs.getBlob("img");
            InputStream in = blob.getBinaryStream();
            Files.copy(in, Paths.get("D:/1234.jpg"));
        }
        JdbcUtil.close(conn,ps,null);
    }

5.获取自动生成的主键

    @Test
    public void testStatement() throws Exception {
        String sql = "INSERT INTO pk (name,age) VALUES (?,?)";
        Connection conn = JdbcUtil.getConnection();
        //设置主键可获取
        PreparedStatement ps = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
        ps.setString(1,"ZZZ");
        ps.setInt(2,20);
        ps.executeUpdate();
        //获取主键结果集
        ResultSet rs = ps.getGeneratedKeys();
        if (rs.next()) {
            Integer id = rs.getInt(1);
            System.out.println(id);
        }
        JdbcUtil.close(conn,ps,rs);
    }

很简单,没什么好说的。

6.连接池

创建DataSource对象

使用DataSource需要导入commos-dbcp.jar和commons-pool.jar两个jar包

/**
 * 使用DBCP连接池
 */
public class DBCPTest {

    /**
     * 创建一个连接池对象
     * @return DataSource
     */
    public DataSource getDataSource() {
        //创建连接对象
        BasicDataSource ds = new BasicDataSource();
        //设置连接数据库的四要素
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setUrl("jdbc:mysql:///studentinfo");
        ds.setUsername("root");
        ds.setPassword("password");
        ds.setMaxWaitMillis(1000);//设置最大等待时间
        ds.setInitialSize(3);//设置初始连接数
        ds.setMaxIdle(3);//最大空闲连接数
        ds.setMinIdle(1);//最小空闲连接数
        ds.setMaxTotal(10);//最大连接数
        return ds;
    }

    @Test
    public void test1() throws Exception {
        DataSource ds = getDataSource();
        Connection conn = ds.getConnection();
        String sql = "SELECT * FROM student";
        PreparedStatement ps = conn.prepareStatement(sql);
        ResultSet rs = ps.executeQuery();
        while (rs.next()) {
            System.out.println(rs.getString("Student_id"));
        }
    }
}

使用properties文件解耦DBCP

public class DBCPUtil {
    
    private static DataSource ds = null;

    static {
        try {
            Properties p = new Properties();
            p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("dbcp.properties"));
            ds = BasicDataSourceFactory.createDataSource(p);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() {
        try {
            //从连接池中获取Connection对象
            return ds.getConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //释放资源
    public static void close(Connection conn, Statement st, ResultSet rs) {
        try {
            if (st != null) {
                st.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

 使用BasicDataSourceFactory很快获得properties文件中的值

    @Test
    public void test1() throws Exception {
        //DataSource ds = getDataSource();
        //Connection conn = ds.getConnection();
        Connection conn = DBCPUtil.getConnection();
        String sql = "SELECT * FROM student";
        PreparedStatement ps = conn.prepareStatement(sql);
        ResultSet rs = ps.executeQuery();
        while (rs.next()) {
            System.out.println(rs.getString("Student_id"));
        }
    }

7.Druid连接池

需要从阿里巴巴下载Druid的jar包

各数据库性能对比:

使用起来那就很简单了

    static {
        try {
            Properties p = new Properties();
            p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("druid.properties"));
            //基于DBCP
            //ds = BasicDataSourceFactory.createDataSource(p);
            //基于Druid
            ds = DruidDataSourceFactory.createDataSource(p);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

8.JDBC Template

我们观察DML的增删改操作,发现他们工作的流程有异曲同工之处:

     @Override
    public void save(Student stu) {
        String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            ps.setString(1,stu.getName());
            ps.setString(2,stu.getId());
            ps.setString(3,stu.getSex());
            ps.setInt(4,stu.getAge());
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,null);
        }
    }

    @Override
    public void delete(String id) {
        String sql = "DELETE FROM student WHERE Student_id = ?";
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            ps.setString(1,id);
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,null);
        }
    }

他们的工作原理都是一样的,我们考虑将他们相同的部分抽取出来,重构出一个Template类,然后用循环来设置占位符参数

    /**
     * DML操作(增删改)模板
     *
     * @param sql    DML造作的SQL模板
     * @param params SQL模板中?对应参数的值
     * @return 受影响的行数
     */
    public static int update(String sql, Object... params) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            //设置占位符参数
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            return ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn, ps, null);
        }
        return 0;
    }

实现类修改为:

    @Override
    public void save(Student stu) {
        String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";
        Object[] params = new Object[]{stu.getStudent_name(), stu.getStudent_id(), stu.getSex(), stu.getAge()};
        JdbcTemplate.update(sql, params);
    }

    @Override
    public void delete(String id) {
        JdbcTemplate.update("DELETE FROM student WHERE Student_id = ?", id);
    }

    @Override
    public void update(String id, Student newStu) {
        String sql = "UPDATE student SET Student_name = ?,sex = ?, age = ? WHERE Student_id = ?";
        Object[] params = new Object[]{newStu.getStudent_name(), newStu.getSex(), newStu.getAge(), id};
        JdbcTemplate.update(sql, params);
    }

对于DQL操作来说,也是有很多相同的地方,将其抽取出来,形成DQL模板

    @Override
    public Student get(String id) {
        List<Student> list = new ArrayList<>();
        String sql = "SELECT * FROM student WHERE Student_id = ?";
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            ps.setString(1,id);
            rs = ps.executeQuery();
            while (rs.next()) {
                Student stu = new Student();
                stu.setName(rs.getString("Student_name"));
                stu.setSex(rs.getString("sex"));
                stu.setAge(rs.getInt("age"));
                stu.setId(id);
                list.add(stu);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,rs);
        }
        return list.size() == 1 ? list.get(0) : null;
    }
   public static List<Student> query(String sql, Object... params) {
        List<Student> list = new ArrayList<>();
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            rs = ps.executeQuery();
            while (rs.next()) {
                //处理结果集
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn, ps, rs);
        }
        return list;
    }

实现类修改为:

    @Override
    public Student get(String id) {
        String sql = "SELECT * FROM student WHERE Student_id = ?";
        List<Student> list = JdbcTemplate.query(sql,id);
        return list.size() == 1 ? list.get(0) : null;
    }

9.Template优化

       上述的Template中,我们只能将数据库的结果集每一行封装成固定的Student对象,在开发中我们需要操作N张表,便有N个对象,上述方法显然是不合理的。我们将模板类命名为JDBCTemplate,那么该类应该适用于任何情况。

       因此,我们考虑将处理结果集的行为交给每个对象的DAO实现类来做。

       为了避免不同的DAO实现类心意的处理结果集的方法名字不同,我们用一个接口来指定规范:

//结果集处理器,规范处理结果集的方法名称
public interface IResultSetHandler {

    //处理结果集
    List handle(ResultSet rs) throws Exception;
}

但是我们考虑到,不一定每一个结果集都需要用List来存,所以这里我们用泛型:

public interface IResultSetHandler<T> {

    T handle(ResultSet rs) throws Exception;
}

把结果集每一行数据封装成Student对象的实现:

class StudentResultSetHandle implements IResultSetHandle<List<Student>> {

    @Override
    public List<Student> handle(ResultSet rs) throws Exception {
        List list = new ArrayList();
        while (rs.next()) {
            Student stu = new Student();
            stu.setName(rs.getString("Student_name"));
            stu.setSex(rs.getString("sex"));
            stu.setAge(rs.getInt("age"));
            stu.setId(rs.getString("Student_id"));
            list.add(stu);
        }
        return list;
    }
}

但是我们发现,若存在N个DAO实现类,那么便会有N个实现Handler类,而这些类的结构完全相同:

  1. 创建一个对象
  2. 取出结果集中当前光标所在行的某一列数据
  3. 调用该对象的setter方法,把某一列的数据设置进去(可以使用Java的内省机制)

因此可以将其抽取出来形成结果集处理器,但是要注意:

  1. 规定表中的列名必须和对象中的属性名相同
  2. 规定表中的列名的类型必须和Java中的类型要匹配

结果集处理器BeanHandler代码如下:

/**
 * new BeanHandle(Student.class)
 * new BeanHandle(Teacher.class)
 *
 * @param <T>
 */
//表示把结果集中的一行数据封装成一个对象,专门针对结果集中只有一行数据的情况
public class BeanHandler<T> implements IResultSetHandler<T> {

    private Class<T> classType;//把结果集中的一行数据封装成什么类型的对象

    public BeanHandler(Class<T> classType) {
        this.classType = classType;
    }

    /**
     * 规定表中的列名称必须和对象中的属性名相同;
     * 规定表中的列名的类型必须和Java中的类型要匹配
     * --------------##使用内省机制##--------------
     *
     * @param rs
     * @return
     * @throws Exception
     */
    @Override
    public T handle(ResultSet rs) throws Exception {
        //1.创建对应类的一个对象
        T obj = classType.newInstance();
        //2.取出结果集中当前光宝所在行的某一列的数据
        BeanInfo beanInfo = Introspector.getBeanInfo(classType, Object.class);
        PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
        if (rs.next()) {
            for (PropertyDescriptor pd : pds) {
                String columnName = pd.getName();//获取对象的属性名,属性和列名相同
                Object val = rs.getObject(columnName);
                //System.out.println(columnName + "," + val);
                //3.调用该对象的setter方法,把某一列的数据设置进去
               pd.getWriteMethod().invoke(obj,val);
            }
        }
        return obj;
    }
}

BeanListHandler同理如下:

public class BeanListHandler<T> implements IResultSetHandler<List<T>> {

    private Class<T> classType;//把结果集中的一行数据封装成什么类型的对象

    public BeanListHandler(Class<T> classType) {
        this.classType = classType;
    }

    @Override
    public List<T> handle(ResultSet rs) throws Exception {
        List<T> list = new ArrayList<>();
        while (rs.next()) {
            T obj = classType.newInstance();
            list.add(obj);
            BeanInfo beanInfo = Introspector.getBeanInfo(classType, Object.class);
            PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor pd : pds) {
                String columnName = pd.getName();//获取对象的属性名,属性和列名相同
                Object val = rs.getObject(columnName);
                pd.getWriteMethod().invoke(obj,val);
            }
        }
        return list;
    }
}

此时JdbcTemplate模板类如下:

//JDBC操作模板类
public class JdbcTemplate {

    /**
     * DQL(查询)操作模板
     * 如果查询多个学生,返回一个List<Student>
     * 单个学生也可以返回一个List<Student>
     *
     * @param sql    DQL操作的SQL模板
     * @param params SQL中?对应的参数值
     * @return List集合
     */
    public static <T>T query(String sql, IResultSetHandler<T> rsh, Object... params) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            rs = ps.executeQuery();
            return rsh.handle(rs);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn, ps, rs);
        }
        throw new RuntimeException("查询错误");
    }

    /**
     * DML操作(增删改)模板
     *
     * @param sql    DML造作的SQL模板
     * @param params SQL模板中?对应参数的值
     * @return 受影响的行数
     */
    public static int update(String sql, Object... params) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            //设置占位符参数
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            return ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn, ps, null);
        }
        return 0;
    }
}

实现类如下:

public class StudentDAOImpl implements IStudentDAO {

    @Override
    public void save(Student stu) {
        String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";
        Object[] params = new Object[]{stu.getStudent_name(), stu.getStudent_id(), stu.getSex(), stu.getAge()};
        JdbcTemplate.update(sql, params);
    }

    @Override
    public void delete(String id) {
        JdbcTemplate.update("DELETE FROM student WHERE Student_id = ?", id);
    }

    @Override
    public void update(String id, Student newStu) {
        String sql = "UPDATE student SET Student_name = ?,sex = ?, age = ? WHERE Student_id = ?";
        Object[] params = new Object[]{newStu.getStudent_name(), newStu.getSex(), newStu.getAge(), id};
        JdbcTemplate.update(sql, params);
    }

    @Override
    public Student get(String id) {
        String sql = "SELECT * FROM student WHERE Student_id = ?";
        return JdbcTemplate.query(sql,new BeanHandler<>(Student.class), id);
    }

    @Override
    public List<Student> listAll() {
        return JdbcTemplate.query("SELECT * FROM student",new BeanListHandler<>(Student.class));

    }
}

显得非常简洁,从原来的将近两百行优化到了四十行。

这些就是数据库操作的一些模板,若给他人使用,我们可以将其打成jar包,可以应付绝大部分的数据库操作。但是还是有一些小问题,我的处理结果集不一定是某个类的对象,可以是某一个值,比如我使用了数据库的聚合函数。那么我们可以用一个匿名内部类来重写IResultSetHandler接口:

    @Test
    public void testGetCount() {
        String sql = "SELECT COUNT(Student_id) FROM student";
        int totalCount = JdbcTemplate.query(sql, new IResultSetHandler<Integer>() {
            @Override
            public Integer handle(ResultSet rs) throws Exception {
                if (rs.next()) {
                    return rs.getInt(1);
                }
                return 0;
            }
        });
        System.out.println(totalCount);
    }

当然可以使用lambda表达式:

    @Test
    public void testGetCount() {
        String sql = "SELECT COUNT(Student_id) FROM student";
        int totalCount = JdbcTemplate.query(sql, rs -> {
            if (rs.next()) {
                return rs.getInt(1);
            }
            return 0;
        });
        System.out.println(totalCount);
    }

简洁明了。

       整个DAO就是一个封装的思想,目的就是实现代码的高内聚和低耦合,便于后期维护。我们抽象出来的模板类和Handler类,就是把数据库操作中相同的或者类似的部分封装起来,然后只修改SQL语句,实现对数据库的操作,这样操作人员只需要修改Impl实现类就可以了。

       Java真有意思(/斜眼笑)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值