JDBC基本使用

发布于:2024-06-18 ⋅ 阅读:(152) ⋅ 点赞:(0)

目录

JDBC

简介:

工作原理:

工作过程:

ResultSet接口

使用JDBC

SQL注入问题

PreparedStatement

和Statement对比

作用:

用法示例:

连接池

封装

Q&A


JDBC

简介:
  • javaDatabaseConnectivity:Java访问数据库的解决方案

  • 希望用相同的方式访问不同的数据库,以实现与具体数据库无关的Java操作界面

  • JDBC定义一套标准接口,即访问数据库的通用API,不同的数据库厂商根据各自数据库的特点去实现这些接口

工作原理:
  • JDBC定义接口

  • 数据库厂商实现接口

  • 程序员调用接口,实际调用的是底层数据库厂商的实现部分

工作过程:
  • 加载驱动,建立连接

  • 创建语句对象

  • 执行SQL语句

  • 处理结果集

  • 关闭连接

ResultSet接口
  • 执行查询SQL语句后返回的结果集,由ResultSet接口接收

  • 常用处理方式:遍历/判断是否有结果(登录)

使用JDBC
package util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBUtil {
    public static Connection getConnection(){
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            Connection connection= DriverManager.getConnection("jdbc:mysql://localhost:3306/jyz?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false",
                    "root","root");
            return connection;
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
​
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}
package jdbc;
​
import entity.Student;
import util.DBUtil;
​
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
​
public class MyJDBC {
​
//查询单条
    public static void selectById() throws SQLException {
        //2.创建Statement对象==>帮助我们执行sql语句
        Connection connection=DBUtil.getConnection();
        Statement statement= connection.createStatement();
​
        //根据主键查询信息
        String sql="select * from student where id=2";
        ResultSet resultSet=statement.executeQuery(sql);//执行查询语句,会返回一个结果集,结果集的数据就是查询出来的数据
​
        //遍历ResultSet
        /*
         * mysql     java
         *  int       int
         *  char      String
         *  varchar   String
         *  date      String
         * */
        while(resultSet.next()){
            int id=resultSet.getInt("id");
            String name=resultSet.getString("name");
            int age=resultSet.getInt("age");
            String gender=resultSet.getString("gender");
            String entryTime=resultSet.getString("entryTime");
//可以用String或LocalDateTime接受
            Student student=new Student();
            student.setId(id);
            student.setName(name);
            student.setAge(age);
            student.setGender(gender);
            student.setEntryTime(entryTime);
            System.out.println(student);
        }
            connection.close();//关闭数据库连接
    }
​
//查询多条数据
public static void selectByName() throws SQLException {
        Connection connection=DBUtil.getConnection();
        Statement statement= connection.createStatement();
        String sql="select * from student where name='tom3'";
        ResultSet resultSet=statement.executeQuery(sql);
        List<Student> list=new ArrayList<>();
        while(resultSet.next()){
            int id=resultSet.getInt("id");
            String name=resultSet.getString("name");
            int age=resultSet.getInt("age");
            String gender=resultSet.getString("gender");
            String entryTime=resultSet.getString("entryTime");
            
            Student student=new Student();
            student.setId(id);
            student.setName(name);
            student.setAge(age);
            student.setGender(gender);
            student.setEntryTime(entryTime);
            list.add(student);
​
        }
​
    System.out.println(list);
}
​
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        selectByName();
    }
}

SQL注入问题

使用原有Statement有以下问题:

  • 容易遭受注入式攻击

  • 拼写SQL繁琐和麻烦

使用PrepareStatement解决

PreparedStatement
和Statement对比

1、

  • tatement每执行一次都要对传入的SQL语句编译一次,效率较差

  • PreparedStatement实例包含已事先编译的SQL语句

2、

  • Statement主要用于执行静态SQL语句,即内容固定不变的SQL语句

  • PreparedStatement的SQL语句可有一个或多个IN参数

    IN参数的值在SQL语句创建时未被指定。该语句为每个IN参数保留一个问号(“?”)作为占位符

    每个问号的值必须在该语句执行之前,通过适当的setInt或者setString等方法提供。

Statement每执行一次都要对传入的SQL语句编译一次,效率较差

由于PreparedStatement对象已预编译过,所以其执行速度要快于Statement对象。因此,多次执行的SQL语句经常创建为PreparedStatement对象,以提高效率。

作用:
  • 提高效率

  • 批量处理

  • 预防sql注入攻击

用法示例:
##增:
    //批量插入
    public static void insertBatch1(){
        long begin=System.currentTimeMillis();
        Connection connection=DBUtil.getConnection();
        String sql="insert into student(name,age,gender,entryTime) values(?,?,?,?)";
        try {
            PreparedStatement pstm=connection.prepareStatement(sql);
            for (int i = 1; i <=5000; i++) {
                pstm.addBatch();
                if(i%500==0){//缓冲区每500条则执行sql
                    pstm.executeBatch();
                    pstm.clearBatch();
                }
            }
            pstm.executeBatch();//批量执行
            pstm.clearBatch();
            long end=System.currentTimeMillis();
            System.out.println("耗时:"+(end-begin));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
​
##删:
    Connection connection=DBUtil.getConnection();
    String sql="delete from student where id=?";
        PreparedStatement pstm=connection.prepareStatement(sql);
        pstm.setObject(1,id);
        if(pstm.executeUpdate()>0){
            System.out.println("删除成功");
        }
​
##改:
    Connection connection=DBUtil.getConnection();
    String sql="update student set name=?,age=?,gender=? where id=?";
        PreparedStatement pstm=connection.prepareStatement(sql);
        pstm.setObject(1,"hello2");
        pstm.setObject(2,15);
        pstm.setObject(3,"男");
        pstm.setObject(4,2);
​
        if(pstm.executeUpdate()>0){
            System.out.println("修改成功");
        }
​
##查:
    Connection connection=DBUtil.getConnection();
    String sql="select * from student where name=? and age=? and gender=?";
        PreparedStatement pstm=connection.prepareStatement(sql);
        pstm.setObject(1,"tom3");
        pstm.setObject(2,21);
        pstm.setObject(3,"男");
        ResultSet resultSet=pstm.executeQuery();
        while(resultSet.next()){}

连接池

1.导入相关jar包

2.DBUtil配置如下:

public class DBUtil {
    static BasicDataSource dataSource = new BasicDataSource();
​
    static{
        //使用properties加载属性文件
        Properties prop = new Properties();
        try {
            InputStream is = DBUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
            prop.load(is);
  
            //获取属性文件中的driverClassName,url,user,password到连接池
             dataSource.setDriverClassName(prop.getProperty("driverClassName"));
             dataSource.setUrl(prop.getProperty("url"));
             dataSource.setUsername(prop.getProperty("user"));
             dataSource.setPassword(prop.getProperty("password"));
​
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
​
​
    //获取数据库连接
    public static Connection getConnection(){
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
        } catch ( SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
}

封装

public class CURDUtil {
    static  Connection connection=DBUtil.getConnection();
    //封装CURD操作,适用于所有表
​
    //插入
    public static <T> void insertOne(T t){
        String tableName=t.getClass().getSimpleName();
        Field[] fields=t.getClass().getDeclaredFields();
        StringJoiner filedsList=new StringJoiner(",");
        StringJoiner paramsList=new StringJoiner(",");
​
        for (int i = 1; i < fields.length; i++) {
            filedsList.add(fields[i].getName());
            paramsList.add("?");
        }
​
        String sql="insert into "+tableName+"("+filedsList+") values("+paramsList+")" ;
        try {
            PreparedStatement pstm= connection.prepareStatement(sql);
            for (int i = 1; i <fields.length ; i++) {
                fields[i].setAccessible(true);
                pstm.setObject(i,fields[i].get(t));
                fields[i].setAccessible(false);
            }
            if(pstm.executeUpdate()>0){
                System.out.println(tableName.toLowerCase()+"表数据添加成功!");
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
​
​
    //根据主键查询一条数据
    public static <T>  T selectOne(Integer id,Class<T> clazz){
        T t=null;
        String tableName=clazz.getSimpleName();
        String primaryName=clazz.getDeclaredFields()[0].getName();
        String sql="select * from "+tableName+" where "+primaryName+"=?";
        System.out.println(sql);
        try {
            t=clazz.newInstance();
            PreparedStatement pstm= connection.prepareStatement(sql);
            pstm.setObject(1,id);
           ResultSet resultSet= pstm.executeQuery();
           while(resultSet.next()){
                Field[] fields=clazz.getDeclaredFields();
               for (int i = 0; i < fields.length; i++) {
                    fields[i].setAccessible(true);
                    fields[i].set(t,resultSet.getObject(i+1));
                    fields[i].setAccessible(false);
               }
           }
​
            System.out.println(t);
           return t;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
​
    public static void main(String[] args) {
      //  insertOne(new Student("测试",20,"一班"));
      // insertOne(new User("tom5","123456","1234@qq.com", LocalDateTime.now(),"231123"));
      selectOne(2,User.class);
    }
}

Q&A

题1 JDBC连接数据库的步骤

  • 加载驱动,建立连接

  • 创建语句对象

  • 执行SQL语句

  • 处理结果集

  • 关闭连接

题2 ResultSet接口的作用

  • 执行查询SQL语句后返回的结果集,由ResultSet接口接收

题3 什么是SQL注入问题,如何避免

  • SQL注入是一种安全漏洞,允许攻击者利用应用程序对数据库的输入不加过滤或转义的特性来执行恶意的SQL语句。攻击者可以通过在输入中插入恶意的SQL代码,来执行未经授权的数据库操作,导致数据泄露

  • 避免:可以使用PreparedStatement等预编译的SQL语句,将用户提供的输入作为参数传递给SQL语句,而不是直接拼接到SQL语句中。这样可以防止用户输入被解释为SQL代码的一部分

题4 数据库连接池的作用是什么

  1. 提高性能:数据库连接是一种资源消耗较大的资源,频繁地创建和销毁数据库连接会增加系统的开销。使用连接池可以在系统启动时创建一定数量的数据库连接,并在需要时将连接分配给客户端,避免了频繁地创建和销毁连接,从而提高了系统的性能和吞吐量。

  2. 减少资源消耗:连接池通过复用数据库连接,减少了连接的创建和销毁次数,降低了系统的资源消耗,提高了系统的资源利用率。

  3. 提高并发性能:连接池可以限制并发连接的数量,防止系统因为连接过多而出现资源竞争和性能下降的情况,从而提高了系统的并发性能。

  4. 优化数据库连接:连接池可以对数据库连接进行管理和优化,如检测空闲连接、超时连接的释放、连接的有效性检测等,保证连接的可靠性和稳定性。

题5 使用连接池的具体步骤

  1. 导入连接池库:首先需要在项目中导入连接池的库

  2. 配置连接池:在项目中配置连接池的参数,包括数据库连接的URL、用户名、密码、连接池大小、最大连接数、连接超时时间等。

  3. 获取连接:在需要访问数据库的地方,通过连接池获取数据库连接。如DataSource.getConnection()方法。

  4. 执行数据库操作:获取到数据库连接后,可以通过该连接执行数据库操作,包括查询、更新、插入等。

  5. 释放连接:在使用完数据库连接后,需要及时释放连接,以便连接池可以重新利用该连接。通常是通过调用连接对象的close()方法来释放连接,而不是直接关闭连接。


网站公告

今日签到

点亮在社区的每一天
去签到