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

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

使用PreparedStatement好处:

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

2、可以操作Blob的数据

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


前提

准备配置文件jdbc.properties

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

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

准备软件包util

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

package util;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**封装数据库连接和关闭*/
public class JDBCUtils {
    public static Connection getConnection() throws Exception{
        //读取配置文件基本信息
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
        Properties pros = new Properties();
        pros.load(is);

        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        String url = pros.getProperty("url");
        String driverClass = pros.getProperty("driverClass");
        //加载驱动
        Class.forName(driverClass);//执行静止代码块
        //获取连接
        Connection conn = DriverManager.getConnection(url,user,password);
        return conn;
    }
    public static void closeResource(Connection conn, Statement ps, ResultSet rs){
        try {
            if(ps != null)
                ps.close();
        }catch (SQLException e){
            e.printStackTrace();
        }
        try {
            if(conn != null)
                conn.close();
        }catch (SQLException e){
            e.printStackTrace();
        }
        try {
            if(rs!=null)
                rs.close();
        }catch (SQLException e){
            e.printStackTrace();
        }
    }
}

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

1、实例化

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

2、连接数据库

调用前面封装的方法。

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

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

executeQuery方法执行;

resultSet保存执行的结果集;

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

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

 4、处理结果集

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

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

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

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

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

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

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

                 //方式一
//                System.out.println("id = "+id+",name = "+name+",email = "+email+",birth = "+birth);
                //方式二
//                Object[] data = new Object[]{id,name,email,birth};
                //方式三(推荐)
                Customer customer = new Customer(id,name,email,birth);
                System.out.println(customer);
            }
        }

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

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

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

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

package bean;
import java.util.Date;
/**
 ORM编程思想(object relational mapping)
 一个数据表对应一个java类
 表中的一条记录对应java类的一个对象
 表中的一个字段对应java类的一个属性
 */
public class Customer {
    private int id;
    private String name;
    private String email;
    private Date birth;
    public Customer(){
        super();
    }

    public Customer(int id,String name,String email,Date birth) {
        super();
        this.id = id;
        this.name = name;
        this.email = email;
        this.birth = birth;
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", birth=" + birth +
                '}';
    }
}

5、关闭资源

调用前面封装的方法。

catch (Exception e){
            e.printStackTrace();
        }finally {
            //关闭资源
            JDBCUtils.closeResource(conn,ps,resultSet);
        }
    }

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

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

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

1、设置方法、参数

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

2、实例化

   Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
       

 3、连接数据库

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

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

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

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

            //预编译
            ps = conn.prepareStatement(sql);
            //填充占位符
            for(int i = 0;i<args.length;i++){
                ps.setObject(i+1,args[i]);
            }
            //执行并返回结果集
            rs = ps.executeQuery();
            //获取结果集的元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取结果集中的列数
            int columnCount = rsmd.getColumnCount();
          

5、处理结果集

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

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

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

将cust中的columnName属性赋值为columnValue

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

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

6、关闭资源

catch (Exception e){
            e.printStackTrace();
        }finally {
            JDBCUtils.closeResource(conn,ps,rs);
        }
        return null;
    }

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

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

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

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

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

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

2、实例化

        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
       

3、连接数据库

         try {
            conn = JDBCUtils.getConnection();
           

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

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

5、处理结果集

利用泛型动态创建对象

             if (rs.next()) {
                T t = clazz.newInstance();//创建对象,弱类型效率低,只能无参
                for(int i = 0;i<columnCount;i++){
                    Object columValue = rs.getObject(i+1);
                    String columnLabel = rsmd.getColumnLabel(i+1);
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columValue);
                }
                return t;
            }
        }

6、关闭资源

catch (Exception e){
            e.printStackTrace();
        }finally {
            JDBCUtils.closeResource(conn,ps,rs);
        }
        return null;
    }

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

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

调用时循环输出。

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

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

2、实例化

        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
       

3、连接数据库

         try {
            conn = JDBCUtils.getConnection();
           

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

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

5、处理结果集

             //创建集合对象
            ArrayList<T> list = new ArrayList<T>();
            while(rs.next()) {
                T t = clazz.newInstance();//创建对象,弱类型效率低,只能无参
                for(int i = 0;i<columnCount;i++){
                    Object columValue = rs.getObject(i+1);
                    String columnLabel = rsmd.getColumnLabel(i+1);
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columValue);
                }
                list.add(t);
            }
            return list;
        }

6、关闭资源

    catch (Exception e){
            e.printStackTrace();
        }finally {
            JDBCUtils.closeResource(conn,ps,rs);
        }
        return null;
    }

调用示例

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


总结

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

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

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

JDBC结果集的元数据:ResultSetMetaData

获取列数:getColumnCount()

获取列的别名:getColumnLabel()

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

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

四月天行健

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值