• 【JDBC】-- PreparedStatement实现数据库查询操作


    前面已经完成数据库的增删改操作,查询操作需要在前面的基础上使用一个结果集接收执行返回的结果。

    使用PreparedStatement好处:

    1、解决Statement拼串、sql注入问题

    2、可以操作Blob的数据

    3、可以实现更高效的批量操作


    前提

    准备配置文件jdbc.properties

    封装连接数据库的基本信息

    1. user=root
    2. password=ad
    3. url=jdbc:mysql://localhost:3306/jdbc
    4. driverClass=com.mysql.cj.jdbc.Driver

    准备软件包util

    封装连接数据库、关闭资源方法

    1. package util;
    2. import java.io.InputStream;
    3. import java.sql.*;
    4. import java.util.Properties;
    5. /**封装数据库连接和关闭*/
    6. public class JDBCUtils {
    7. public static Connection getConnection() throws Exception{
    8. //读取配置文件基本信息
    9. InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
    10. Properties pros = new Properties();
    11. pros.load(is);
    12. String user = pros.getProperty("user");
    13. String password = pros.getProperty("password");
    14. String url = pros.getProperty("url");
    15. String driverClass = pros.getProperty("driverClass");
    16. //加载驱动
    17. Class.forName(driverClass);//执行静止代码块
    18. //获取连接
    19. Connection conn = DriverManager.getConnection(url,user,password);
    20. return conn;
    21. }
    22. public static void closeResource(Connection conn, Statement ps, ResultSet rs){
    23. try {
    24. if(ps != null)
    25. ps.close();
    26. }catch (SQLException e){
    27. e.printStackTrace();
    28. }
    29. try {
    30. if(conn != null)
    31. conn.close();
    32. }catch (SQLException e){
    33. e.printStackTrace();
    34. }
    35. try {
    36. if(rs!=null)
    37. rs.close();
    38. }catch (SQLException e){
    39. e.printStackTrace();
    40. }
    41. }
    42. }

    一、针对一个SQL语句的查询操作

    1、实例化

    1. public static void testQuery1(){
    2. Connection conn = null;//连接
    3. PreparedStatement ps = null;//预编译
    4. ResultSet resultSet = null;//执行后,返回结果集

    2、连接数据库

    调用前面封装的方法。

    1. try {
    2. //连接数据库
    3. conn = JDBCUtils.getConnection();

    3、预编译、填充占位符、执行并返回结果集

    executeQuery方法执行;

    resultSet保存执行的结果集;

    到此,数据库已经执行了传入的sql语句,接下来需要得到数据库执行的结果。

    1. //预编译
    2. String sql = "select id,name,email,birth from customers where id = ?";
    3. ps = conn.prepareStatement(sql);
    4. //填充占位符
    5. ps.setObject(1,1);
    6. //执行并返回结果集
    7. resultSet = ps.executeQuery();

     4、处理结果集

    resultSet指向的是结果的第一行即列名;

    只有一条数据所以使用if即可;

    next()方法判断下一条是否有数据,有则返回true,指针指向下一个;

    有数据则获取当前数据的各个字段值。

    1. //处理结果集
    2. if(resultSet.next()){//next判断下一条是否有数据,有则返回true,指针指向下一个
    3. //获取当前数据的各个字段值
    4. int id = resultSet.getInt(1);
    5. String name = resultSet.getString(2);
    6. String email = resultSet.getString(3);
    7. Date birth = resultSet.getDate(4);

    有三种方式处理获取的各个字段值;

    一般使用第三种方式:将一个数据表封装成一个类。

    1. //方式一
    2. // System.out.println("id = "+id+",name = "+name+",email = "+email+",birth = "+birth);
    3. //方式二
    4. // Object[] data = new Object[]{id,name,email,birth};
    5. //方式三(推荐)
    6. Customer customer = new Customer(id,name,email,birth);
    7. System.out.println(customer);
    8. }
    9. }

    创建软件包bean,软件包下创建Customer类

    这里使用ORM编程思想:一个数据表对应一个java类;

                                            表中一条记录对应java类的一个对象;

                                            表中的一个字段对应java类的一个属性。

    1. package bean;
    2. import java.util.Date;
    3. /**
    4. ORM编程思想(object relational mapping)
    5. 一个数据表对应一个java类
    6. 表中的一条记录对应java类的一个对象
    7. 表中的一个字段对应java类的一个属性
    8. */
    9. public class Customer {
    10. private int id;
    11. private String name;
    12. private String email;
    13. private Date birth;
    14. public Customer(){
    15. super();
    16. }
    17. public Customer(int id,String name,String email,Date birth) {
    18. super();
    19. this.id = id;
    20. this.name = name;
    21. this.email = email;
    22. this.birth = birth;
    23. }
    24. public int getId() {
    25. return id;
    26. }
    27. public void setId(int id) {
    28. this.id = id;
    29. }
    30. public String getName() {
    31. return name;
    32. }
    33. public void setName(String name) {
    34. this.name = name;
    35. }
    36. public String getEmail() {
    37. return email;
    38. }
    39. public void setEmail(String email) {
    40. this.email = email;
    41. }
    42. public Date getBirth() {
    43. return birth;
    44. }
    45. public void setBirth(Date birth) {
    46. this.birth = birth;
    47. }
    48. @Override
    49. public String toString() {
    50. return "Customer{" +
    51. "id=" + id +
    52. ", name='" + name + '\'' +
    53. ", email='" + email + '\'' +
    54. ", birth=" + birth +
    55. '}';
    56. }
    57. }

    5、关闭资源

    调用前面封装的方法。

    1. catch (Exception e){
    2. e.printStackTrace();
    3. }finally {
    4. //关闭资源
    5. JDBCUtils.closeResource(conn,ps,resultSet);
    6. }
    7. }

     二、针对一个表的通用的查询操作

    与前面不同的是需要传入sql语句和值;

    针对Customer表查询,用到前面创建的Customer类。

    1、设置方法、参数

    1. /**针对一个表的通用查询*/
    2. public static Customer queryForCustomers(String sql,Object...args){

    2、实例化

    1. Connection conn = null;
    2. PreparedStatement ps = null;
    3. ResultSet rs = null;

     3、连接数据库

    1. try {
    2. //连接
    3. conn = JDBCUtils.getConnection();

    4、预编译、填充占位符、执行返回结果集

    与针对一条sql语句不同的是需要获取元数据、列数;

    由于后面需要每个列的列值和列名,所以需要获取元数据,在获取总列数。

    1. //预编译
    2. ps = conn.prepareStatement(sql);
    3. //填充占位符
    4. for(int i = 0;i
    5. ps.setObject(i+1,args[i]);
    6. }
    7. //执行并返回结果集
    8. rs = ps.executeQuery();
    9. //获取结果集的元数据
    10. ResultSetMetaData rsmd = rs.getMetaData();
    11. //获取结果集中的列数
    12. int columnCount = rsmd.getColumnCount();

    5、处理结果集

    由于传入的参数不同,需要通过反射给cust对象指定columnName属性,赋值为columnValue;

    通过Customer.class.getDeclaredField(columnName)获取columnName列名的属性;

    属性可能是私有,需要setAccessible()方法确保属性可访问;

    将cust中的columnName属性赋值为columnValue

    (注意:Customer中的属性名如果与数据库中的字段名不同,则查询时可以给字段名起别名,别名为Customer中的属性名)

    1. //处理结果集
    2. if(rs.next()){
    3. Customer cust = new Customer();
    4. //处理结果集一行数据的每一列
    5. for(int i = 0;i
    6. Object columValue = rs.getObject(i+1);//获取列值
    7. String columnName = rsmd.getColumnLabel(i+1);//获取每个列的别名,没有别名则获取原名
    8. //给cust对象指定columnName属性,赋值为columValue,反射
    9. Field field = Customer.class.getDeclaredField(columnName);
    10. field.setAccessible(true);
    11. field.set(cust,columValue);
    12. }
    13. return cust;
    14. }
    15. }

    6、关闭资源

    1. catch (Exception e){
    2. e.printStackTrace();
    3. }finally {
    4. JDBCUtils.closeResource(conn,ps,rs);
    5. }
    6. return null;
    7. }

    三、针对不同表的通用的查询操作(返回一条记录)

    与上一个不同的是需要传入不同的类;

    使用泛型T表示传入的类;

    使用时,传入类、sql语句、和对应值即可。

    1、创建泛型方法,设置参数

        public static  T getInstance(Class clazz,String sql,Object... args){
          

    2、实例化

    1. Connection conn = null;
    2. PreparedStatement ps = null;
    3. ResultSet rs = null;

    3、连接数据库

    1. try {
    2. conn = JDBCUtils.getConnection();

    4、预编译、填充占位符、执行返回结果集

    1. ps = conn.prepareStatement(sql);
    2. for(int i = 0;i < args.length;i++){
    3. ps.setObject(i+1,args[i]);
    4. }
    5. rs = ps.executeQuery();
    6. ResultSetMetaData rsmd = rs.getMetaData();
    7. int columnCount = rsmd.getColumnCount();

    5、处理结果集

    利用泛型动态创建对象

    1. if (rs.next()) {
    2. T t = clazz.newInstance();//创建对象,弱类型效率低,只能无参
    3. for(int i = 0;i
    4. Object columValue = rs.getObject(i+1);
    5. String columnLabel = rsmd.getColumnLabel(i+1);
    6. Field field = clazz.getDeclaredField(columnLabel);
    7. field.setAccessible(true);
    8. field.set(t,columValue);
    9. }
    10. return t;
    11. }
    12. }

    6、关闭资源

    1. catch (Exception e){
    2. e.printStackTrace();
    3. }finally {
    4. JDBCUtils.closeResource(conn,ps,rs);
    5. }
    6. return null;
    7. }

    四、针对不同表的通用的查询操作(返回多条记录)

    与上一个不同的是使用while创建对象,将对象对存入一个集合里,再将集合返回;

    调用时循环输出。

    1、创建泛型方法,设置参数

     public static  List getInstance2(Class clazz, String sql, Object... args){
           

    2、实例化

    1. Connection conn = null;
    2. PreparedStatement ps = null;
    3. ResultSet rs = null;

    3、连接数据库

    1. try {
    2. conn = JDBCUtils.getConnection();

    4、 预编译、填充占位符、执行返回结果集

    1. ps = conn.prepareStatement(sql);
    2. for(int i = 0;i < args.length;i++){
    3. ps.setObject(i+1,args[i]);
    4. }
    5. rs = ps.executeQuery();
    6. ResultSetMetaData rsmd = rs.getMetaData();
    7. int columnCount = rsmd.getColumnCount();

    5、处理结果集

    1. //创建集合对象
    2. ArrayList list = new ArrayList();
    3. while(rs.next()) {
    4. T t = clazz.newInstance();//创建对象,弱类型效率低,只能无参
    5. for(int i = 0;i
    6. Object columValue = rs.getObject(i+1);
    7. String columnLabel = rsmd.getColumnLabel(i+1);
    8. Field field = clazz.getDeclaredField(columnLabel);
    9. field.setAccessible(true);
    10. field.set(t,columValue);
    11. }
    12. list.add(t);
    13. }
    14. return list;
    15. }

    6、关闭资源

    1. catch (Exception e){
    2. e.printStackTrace();
    3. }finally {
    4. JDBCUtils.closeResource(conn,ps,rs);
    5. }
    6. return null;
    7. }

    调用示例

    1. String sql1 = "select id,name,birth,email from customers where id < ?";
    2. List list = getInstance2(Customer.class,sql1,12);
    3. list.forEach(System.out::println);


    总结

    ORM思想(object relational mapping):1、一个数据表对应一个java类

                                                                      2、表中一条记录对应java类的一个对象

                                                                      3、表中一个字段对应java类的一个属性

    JDBC结果集的元数据:ResultSetMetaData

    获取列数:getColumnCount()

    获取列的别名:getColumnLabel()

    反射:通过反射,创建指定类的对象,获取指定的属性并赋值

  • 相关阅读:
    element plus tree树形复选框
    基于Java毕业设计星星电影购票网站源码+系统+mysql+lw文档+部署软件
    linux信息查询
    Java 接口的学习笔记
    HTML 实现 点击按钮切换 整张界面 && 点击按钮切换局部界面
    低代码源代码交付的平台有哪些?
    参数估计的均方误差(MSE),偏置(Bias)与方差(Variance)分解,无偏估计
    每日学到 42 - JavaScript内置对象
    Python与MySQL数据库的交互
    p5.js 到底怎么设置背景图?
  • 原文地址:https://blog.csdn.net/Tir_zhang/article/details/125889997