【第42、43天】JDBC的使用(续)

本文详细介绍了JDBC中PreparedStatement的使用,对比了其与Statement的区别,重点讲解了DAO层的设计与实现,包括接口定义、实现及测试,涵盖了数据库操作如增删改查、时间处理、SQL预编译等关键内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 JDBC的使用(续)

1.1 dao层的编写、PreparedStatement执行器的使用

       dao层,(Data Access Object,数据实现对象)存放使用JDBC应用Java对数据库表进行增删改查的方法所封装成的类的集合。

  • PreparedStatement和Statement有什么区别?

    • 两者同为接口,但PreparedStatement是Statement的子接口
      (接口与接口之间是继承,接口与类之间是实现)
      源码中:
      public interface Statement extends Wrapper

      public interface PreparedStatement extends Statement

    • Statement只能执行静态语句,PreparedStatement可以执行IN参数的SQL语句(所谓IN参数是指,SQL语句可以进行字段等数据的更改,并不是一个固定的语句)。

      PreparedStatement存在一个强大缓存区,可以对SQL语句进行预编译。 在执行相同的SQL语句时,PreparedStatement将语句加载进缓存区,仅仅编译一次,当第二次执行此语句时不需要再次进行编译。也就是说相同的SQL语句仅仅编译一次,PreparedStatement仅对改动数据进行修改,而不再进行编译。而Statement只要语句发生了改变,则必须重新进行编译。

    • PreparedStatement支持对SQL语句使用“?”占位符,从而对SQL语句进行字段参数的修改,降低了开发难度。并且从根本上杜绝了SQL注入这个安全隐患。

    • 但如果SQL语句不需要多次执行,或者“?”过多,则效率可能比Statement低。

1.1.1 定义接口

       这里以我上一篇写的student表为关系表,创建接口,实现对student表的增删改查。

StudentIf.java

package com.test.dao;

import com.test.po.Student;

import java.util.List;

public interface StudentIf {

    //1:添加一个Student
    public boolean addStudent(Student student);

    //2:删除一个Student
    public boolean delStudentById(Integer id);

    //3:根据用户名删除
    public boolean delStudentByName(String name);

    //4:拿取全部数据
    public List<Student> queryAll();
    
    //5:根据用户名查询
    public boolean queryStudentByName(String name);

    //6:根据用户名和密码查询
    public Student queryStudentByNameAndPasswd(String name, String passwd);

    //7:拿取总记录数(包装类)若查询出错就为null
    //返回0表示没记录,使用包装类来避免歧义
    public Integer queryCount();

    //8:分页查询
    public List<Student> queryByPage(Integer startPage, Integer pageSize);

    //9:使用Statement执行器编辑用户
    public boolean updateStudentBySt(Student student);

    //10:使用PreparedStatement执行器编辑用户
    public boolean updateStudentByPst(Student student);

}
1.1.2 实现接口
  • 时间的处理:
    SimpleDateFormat类中,parse()是由字符串转为Date类型;format()由Date类型转为String类型。
    在向执行器setDate()存储数据到数据库时,需要将Date类型转为其子类型java.sql.Date类型,需要将java.util.Date类型的数值getTime()转换为前者;从数据库获取数据时的格式为java.sql.Date,因为java.sql.Date继承于java.util.Date,故直接获取到而不需要再转型。

  • 通常,Statement的sql是在最后执行execute***()时作为参数传入的,createStatement()时为空参;而PreparedStatement的参数是在prepareStatement()时作为参数传入,最后execute***()为空参。

StudentImpl.java

package com.test.dao;

import com.test.factory.Factory;
import com.test.po.Student;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

public class StudentImpl implements StudentIf{
    //与数据库的链接
    Connection connection;
    //执行器
    Statement statement;
    //执行器
    PreparedStatement preparedStatement;
    //当进行查询操作时的结果集
    ResultSet resultSet;
    //需要执行的SQL语句
    String sql;
    //接收结果集的集合
    List<Student> stuList = new ArrayList<>();

    //除更新的一个方法外,执行器使用PreparedStatement
    //具有异常处理的方法,返回值至少两个
    //1:添加一个Student
    @Override
    public boolean addStudent(Student student) {
        try {
            //获取连接
            connection = Factory.getConnection();
            //设置带有占位符?的sql语句
            //仅仅支持PreparedStatement这种执行器
            //JDBC的占位符索引值从1开始
            sql = "insert into student values (null, ?, ?, ?, ?)";
            //获取执行器,并且加载sql语句
            preparedStatement = connection.prepareStatement(sql);
            //填充占位符
            //从左到右的顺序从,列索引从1开始
            //先绑定上让preparedStatement有值
            preparedStatement.setString(1, student.getName());
            preparedStatement.setString(2, student.getPassWd());
            preparedStatement.setInt(3, student.getSalary());
            /*
             *   java.sql.Date与java.util.Date
             *   是一对父子,util为爸爸 sql为儿子
             *   爸爸:年月日小时分钟秒
             *   儿子:年月日
             *   通过getTime()方法可以将爸爸转换为儿子
             *
             *   使用java.sql接收时间类型,
             *   所以需要将前台的java.date-->java.sql
             * */
            //
            preparedStatement.setDate(4, new java.sql.Date(student.getBirthday().getTime()));
            System.out.println(sql);
            return preparedStatement.executeUpdate() == 1;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            Factory.close(null, preparedStatement, connection);
        }
    }

    //2:删除一个Student
    @Override
    public boolean delStudentById(Integer id) {
        try {
            //获取连接
            connection = Factory.getConnection();
            sql = "delete from student where id = ?";
            //获取执行器的同时加载sql语句
            preparedStatement = connection.prepareStatement(sql);
            //填充占位符
            preparedStatement.setInt(1, id);
            System.out.println(sql);
            //执行dml操作
            return preparedStatement.executeUpdate() == 1;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            Factory.close(null, preparedStatement, connection);
        }
    }

    //3:根据用户名删除,删除符合条件的全部数据
    @Override
    public boolean delStudentByName(String name) {
        try {
            connection = Factory.getConnection();
            sql = "delete from student where name = ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, name);
            System.out.println(sql);
            return preparedStatement.executeUpdate() == 1;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            Factory.close(null, preparedStatement, connection);
        }
    }

    //4:拿取全部数据
    @Override
    public List<Student> queryAll() {
        try {
            connection = Factory.getConnection();
            sql = "select * from student";
            preparedStatement = connection.prepareStatement(sql);
            //获取结果集
            resultSet = preparedStatement.executeQuery();
            //获取到一条数据用if,获取多条数据使用while
            while(resultSet.next()){
                stuList.add(new Student(resultSet.getInt(1),
                        resultSet.getString(2),
                        resultSet.getString(3),
                        resultSet.getInt(4),
                        //获取时的格式为Date,因为java.sql.Date继承于java.util.Date,故可以接到
                        resultSet.getDate(5)));
            }
            System.out.println(sql);
            return stuList;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            Factory.close(resultSet, preparedStatement, connection);
        }
    }

    //5:根据用户名查询
    @Override
    public boolean queryStudentByName(String name) {
        try {
            connection = Factory.getConnection();
            sql = "select * from student where name = ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, name);
            resultSet = preparedStatement.executeQuery();
            System.out.println(sql);
            return resultSet.next();
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            Factory.close(resultSet, preparedStatement, connection);
        }
    }

    //6:根据用户名和密码查询
    @Override
    public Student queryStudentByNameAndPasswd(String name, String passwd) {
        try {
            connection = Factory.getConnection();
            String sql = "select * from student where name = ? and passwd = ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, name);
            preparedStatement.setString(2, passwd);
            resultSet = preparedStatement.executeQuery();
            System.out.println(sql);
            //获取到一条数据用if,获取多条数据使用while
            if(resultSet.next()){
                return new Student(resultSet.getInt(1),
                        resultSet.getString(2),
                        resultSet.getString(3),
                        resultSet.getInt(4),
                        resultSet.getDate(5));
            }
            return null;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            Factory.close(resultSet, preparedStatement, connection);
        }
    }

    //7:拿取总记录数(包装类)
    @Override
    public Integer queryCount() {
        try {
            connection = Factory.getConnection();
            sql = "select count(*) from student";
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();
            //这个结果集是一个两行一列的表格
            //使用next()指针向下即指向记录数
            /*
             *   ----------
             *   |count(*)|
             *   ----------
             *   | 记录数  |
             *   ----------
             * */
            resultSet.next();
            System.out.println(sql);
            return resultSet.getInt(1);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            Factory.close(resultSet, preparedStatement, connection);
        }
    }

    //8:分页查询
    //起始页从0开始,表示第一条数据
    @Override
    public List<Student> queryByPage(Integer startPage, Integer pageSize) {
        try {
            connection = Factory.getConnection();
            sql = "select * from student limit ?, ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1, startPage);
            preparedStatement.setInt(2, pageSize);
            resultSet = preparedStatement.executeQuery();
            System.out.println(sql);
            //获取到一条数据用if,获取多条数据使用while
            while(resultSet.next()){
                stuList.add(new Student(
                        resultSet.getInt(1),
                        resultSet.getString(2),
                        resultSet.getString(3),
                        resultSet.getInt(4),
                        resultSet.getDate(5)
                ));
            }
            return stuList;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            Factory.close(resultSet, preparedStatement, connection);
        }
    }

    //9:编辑用户
    @Override
    public boolean updateStudentBySt(Student student) {
        try {
            connection = Factory.getConnection();
            sql = "update student set ";
            if(student.getName() != null) sql += "name = '" + student.getName() + "', ";
            if(student.getPassWd() != null) sql += "passwd = '" + student.getPassWd() + "', ";
            if(student.getSalary() != null) sql += "salary = " + student.getSalary().toString() + ", ";
            if(student.getBirthday() != null) sql += "birthday = '" + new SimpleDateFormat("yyyy-MM-dd").format(student.getBirthday()) + "', ";
            //去掉最后一位的逗号,因为拼接时后面多出一个空格,所以-2
            sql = sql.substring(0, sql.length() - 2);
            sql += " where id = " + student.getId().toString() + ";";
            statement = connection.createStatement();
            System.out.println(sql);
            return statement.executeUpdate(sql) == 1;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            Factory.close(null, statement, connection);
        }
    }
    
    //10:使用PreparedStatement执行器编辑用户
    @Override
    public boolean updateStudentByPst(Student student) {
        try {
            connection = Factory.getConnection();
            String sql = "update student set name = ?, passwd = ?, salary = ?, birthday = ? where id = ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, student.getName());
            preparedStatement.setString(2, student.getPassWd());
            preparedStatement.setInt(3, student.getSalary());
            preparedStatement.setDate(4, new java.sql.Date(student.getBirthday().getTime()));
            preparedStatement.setInt(5, student.getId());
            return preparedStatement.executeUpdate() == 1;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            Factory.close(null, statement, connection);
        }
    }
}
1.1.3 测试

       编写主方法调用dao实现类中的方法来测试方法编写是否正确,建议每写一个之后就对其测试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值