JDBC基础

JDBC基础用法

JDBC简介

  • JDBC(Java Database Connectivity) 是Java语言访问数据库的标准接口。它允许Java应用程序与各种数据库进行通信。

JDBC架构

  • JDBC API:
    • Java应用程序通过JDBC API与数据库进行交互。这个API定义了一系列接口和类,用于连接数据库、发送查询、处理结果等操作。
  • JDBC Driver Manager:
    • JDBC Driver Manager负责加载和管理数据库驱动程序。它是一个基本的服务,用于建立数据库连接。
  • JDBC Driver:
    • JDBC Driver是实现JDBC API的具体驱动程序。不同的数据库厂商提供不同的JDBC驱动程序,用于连接其特定的数据库系统。常见的JDBC驱动类型包括四种:JDBC-ODBC桥接器驱动、本地API驱动、网络协议驱动和本地协议驱动。
  • Connection:
    • Connection对象表示与数据库的连接。通过Connection对象,Java应用程序可以与数据库建立通信。
  • Statement:
    • Statement对象用于向数据库发送SQL语句。有三种类型的Statement:Statement、PreparedStatement和CallableStatement,分别用于不同类型的SQL操作。
  • ResultSet:
    • ResultSet对象代表数据库查询的结果集。通过ResultSet,Java应用程序可以获取查询结果并对其进行处理。
  • SQLException:
    • SQLException是JDBC中的异常类,用于处理与数据库交互过程中可能出现的异常情况。

JDBC连接步骤

  • 加载数据库驱动程序:
    • 使用Class.forName()方法加载数据库驱动程序,例如Class.forName(“com.mysql.cj.jdbc.Driver”);。
  • 建立数据库连接:
    • 使用DriverManager.getConnection()方法建立与数据库的连接,传入数据库的URL、用户名和密码,
    • 如Connection connection = DriverManager.getConnection(“jdbc:mysql://localhost:3306/mydatabase”, “username”, “password”);。
  • 创建Statement对象:
    • 通过连接对象创建Statement对象,用于执行SQL语句,
    • 例如Statement statement = connection.createStatement();。
  • 执行SQL查询:
    • 使用Statement对象的executeQuery()方法执行查询语句,
    • 如ResultSet resultSet = statement.executeQuery(“SELECT * FROM mytable”);。
  • 处理查询结果:
    • 遍历ResultSet对象获取查询结果,可以使用while(resultSet.next())来逐行处理结果集。
  • 执行更新操作:
    • 如果需要执行更新操作(如插入、更新、删除数据),可以使用Statement对象的executeUpdate()方法,
    • 例如int rowsAffected = statement.executeUpdate(“INSERT INTO mytable VALUES (1, ‘John Doe’)”);。
  • 关闭连接:
    • 在完成数据库操作后,需要关闭ResultSet、Statement和Connection对象,以释放资源,顺序为先关闭ResultSet,然后是Statement,最后是Connection,
    • 如resultSet.close(); statement.close(); connection.close();。

JDBC常用接口和类

  • Connection:表示与特定数据库的连接。
  • Statement:用于执行静态SQL语句。
  • PreparedStatement:继承自Statement,用于执行预编译的SQL语句。
  • ResultSet:表示数据库结果集的数据表。

预编译语句和防止SQL注入

  • 使用PreparedStatement可以预编译SQL语句,提高性能并防止SQL注入攻击。

事务管理

  • 使用Connection对象可以管理事务,通过commit()和rollback()方法提交或回滚事务。

批处理

  • 使用addBatch()和executeBatch()可以执行批处理操作,提高效率。

元数据

  • 使用DatabaseMetaData和ResultSetMetaData可以获取数据库和结果集的元数据信息。

异常处理

  • 在JDBC编程中,需要适当处理SQL异常,以确保程序的稳定性。

statement的详细使用

模拟登录

public class StatementUserLoginPaart {

    public static void main(String[] args) throws SQLException {
        Scanner sc = new Scanner(System.in);
        System.out.println("Enter your username: ");
        String username = sc.nextLine();
        System.out.println("Enter your password: ");
        String password = sc.nextLine();
        sc.close();
        /**
         * 1.注册驱动
         * TODO:
         *     注册驱动
         *     依赖:驱动版本8+ com.mysql.cj.jdbc.Driver
         *     驱动版本5+ com.mysql.jdbc.Driver
         *
         * 此代码看会注册两次驱动:DriverManager.registerDriver(new Driver());
         * 1.DriverManager.registerDriver()方法本身会被注册一次
         * 2.Driver.static{DriverManager.registerDriver()}静态代码块也会注册一次
         *
         * 解决方法:只触发静态代码块即可
         *          在进行类加载的时候会触发静态代码块
         * 触发类加载:
         *      1.new
         *      2.调用静态方法
         *      3。调用静态属性
         *      4.接口1.8default默认实现
         *      5.反射
         *      6.子类触发父类
         *      7.程序入口
         *
         */
        //方案1
        // DriverManager.registerDriver(new Driver());

        //方案2:mysql新版本驱动,换成Oracle就会出现问题
        //new Driver

        //方案三,利用反射;字符串->提取到外部的配置文件->可以在不改变代码的情况下实现数据库的切换
        Class.forName("com.mysql.cj.jdbc.Driver");//触发类加载,触发静态代码块的调用

        /**
         * 2.获取连接
         * TODO:
         *       java程序,连接数据库也是调用某个方法,方法也需要填入连接数据库的基本信息
         *       数据库ip:
         *       数据库端口号:
         *       账号:
         *       密码:
         *       连接数据库的名称:
         */

        String url = "jdbc:mysql://localhost:3306/dbtest";
        String user = "root";
        String pass = "root";
        Connection conn = DriverManager.getConnection(url, user, pass);

        //创建发生SQL语句的statement对象
        Statement stmt = conn.createStatement();

        //发送SQL语句
        //这种拼接字符串的方式会出现注入攻击的可能,比如直接使用逻辑或进行短接
        String sql = "SELECT * FROM user WHERE username ='"+username+"' AND password ='"+password+"';";

        //用于执行会修改数据的操作,返回受到影响的数据条数
        // int i = stmt.executeUpdate(sql);

        //用于查询语句,返回查询到的结果集
        ResultSet resultSet = stmt.executeQuery(sql);

        //查询结果集进行解析
        /**
         * resultSet -> 逐行获取数据->每行数据为一个记录
         * resultSet内部包含一个游标,指定当前行数据
         * 默认游标指定的是第一行数据之前的
         * 我们可以调用next()向后移动一行
         * 
         * 我们可以使用while(next)来访问每一个数据
         * boolean = next() true:有更多数据,并向下移动一行
         *                  false:没有更多行数据
         *
         * resultSet.get数据类型(列名)来获取数据类型对应的列
         */

        if (resultSet.next()){
            int id = resultSet.getInt("id");
            String name = resultSet.getString("username");
            System.out.println("ID: "+id+" | Name: "+name+"login susess");
        }else {
            System.out.println("No such user");
        }

        //关闭资源
        //从里到外关闭
        resultSet.close();
        stmt.close();
        conn.close();
    }

}

PreparedStatement的详细使用

在使用上与statement的使用对比

  • Statement
    • 先创建了发送SQL语句的statement对象,
    • 再将SQL语句用字符串拼接的方式传送给对应的方法
    • 由于该方法不是预编译的,JDBC不知道所要执行的SQL语句类型故要指定执行的方法
  • PreparedStatement
    • 先编写sql语句并用问号对动态值进行替代,
    • 再创建PreparedStatement对象,
    • 再使用set数据类型(index,obj)方法来对替代部分进行赋值。
    • 最后使用ps.executeQuery()或者ps.executeUpdate()执行sql语句
      • 前者用于查询语句返回的是ResultSet对象,后者用于数据修改,返回影响数据条数
PreparedStatement
        //编写sql语句并用问号对动态值进行替代
        String sql = "select * from users where username=? and password=?";

        //创建预编译statement并设置SQL语句结果
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //单独的占位符进行赋值
        /**
         * 参数1:index 占位符的位置,从左到右从1开始
         * 参数2:object 占位符的值,可以设置任何数据类型的数据
         */
        preparedStatement.setString(1,username);
        preparedStatement.setString(2,password);
        
        //发送SQL语句并获取返回结果
        ResultSet resultSet = preparedStatement.executeQuery();
Statement
        //创建发生SQL语句的statement对象
        Statement stmt = conn.createStatement();

        //发送SQL语句
        //这种拼接字符串的方式会出现注入攻击的可能,比如直接使用逻辑或进行短接
        String sql = "SELECT * FROM user WHERE username ='"+username+"' AND password ='"+password+"';";

        //用于执行会修改数据的操作,返回受到影响的数据条数
        // int i = stmt.executeUpdate(sql);

        //用于查询语句,返回查询到的结果集
        ResultSet resultSet = stmt.executeQuery(sql);

利用List<Map>来封装查询数据

resultSet

  • 之前的查询再获取结果集的时候使用的是ResultSet
  • 其内部有一个游标,默认指向数据的第一行之前
  • 这种方法再获取数据的时候是获取的一行一行的数据
  • 我们可以使用next()方法来移动光标指向数据行
  • 通过get数据类型(列名)的方法来获取光标指向的行内的某列数据
手动的方法(便于理解)
  • 此种方法由于是将列名固定的写在代码里面,可移植性不好
List<Map> list = new ArrayList<>();

while(resultSet.next()){
  Map map = new HashMap();

  map.put("id",resultSet.getInt("id"));
  map.put("username",resultSet.getString("username"));
  map.put("password",resultSet.getString("password"));
  list.add(map);
}

ResultSetMetaData

  • 该接口提供了一些方法来获取关于ResultSet对象中表的列名及相关信息
  • 可以看作是对于ResultSet所对应表的定义信息的抽象表示
  • getColumnCount(): 返回 ResultSet 中的列数。
  • getColumnName(int column): 返回指定列的名称。
  • getColumnType(int column): 返回指定列的 SQL 类型。
  • getColumnTypeName(int column): 返回指定列的数据库特定类型名称。
  • isNullable(int column): 指示指定列是否允许 NULL 值。
通过ResultSetMetaData自动遍历
//发送SQL语句并获取返回结果
ResultSet resultSet = preparedStatement.executeQuery();

//通过 ResultSetMetaData 对象获取有关查询结果的元数据信息
//表的元数据即为表的属性,包含了列名,数据类型,长度等信息
ResultSetMetaData metaData = resultSet.getMetaData();

//用于水平遍历,得到列数
int columnCount = metaData.getColumnCount();

List<Map> list = new ArrayList<>();

//垂直遍历,利用光标指向每行数据
while(resultSet.next()){
  Map map = new HashMap();

  //水平遍历得到光标指向行的列名和对应数据
  for(int i=1;i<=columnCount;i++>){
    //获取指定列下标的值
    Object value = resultSet.getObject(i);
    //获取指定列下角标的列的名称
    String columnLabel = metaData.getColumnLabel(i);
    map.put(columnLabel,value);
  }
  list.add(map);

}

使用总结

//1.注册驱动
//方案1:调用静态方法,但是会注册2次
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
//方案2:反射触发
Class.forName("com.mysql.cj.jdbc.Driver");

//2.获取连接
Connection connection = DriverManager.getConnection(url,user,password);

//3.创建statement
//静态
Statement statement = connection.createStatement();
//预编译
PreparedStatement preparedStatement = connection.prepareStatement(sql);

//4.占位符赋值
preparedStatement.setObject(?的位置 从左到右 从1开始,值);

//5.发送sql语句获取结果
int rows = executeUpdate();//执行非DQL
Resultset = executeQuery();//DQL

//6.查询结果集解析
//移动光标指向行数据 next();if(next());while(next());
//获取列的数据 get类型(int 列的下标 从1开始 | int 列的label(别名或列名))
//获取列的信息 getMetadata();ResultSetMetaData对象 包含的就是列的信息

//7.关闭资源
//从内到外关闭
close()

JDBC扩展用法

主键回显

public class Main {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/mydatabase";
        String username = "root";
        String password = "password";

        try (Connection conn = DriverManager.getConnection(url, username, password)) {
            String sql = "INSERT INTO my_table (name) VALUES (?)";
            PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            pstmt.setString(1, "John Doe");
            pstmt.executeUpdate();

            ResultSet rs = pstmt.getGeneratedKeys();
            if (rs.next()) {
                int generatedKey = rs.getInt(1);
                System.out.println("Generated Key: " + generatedKey);
            }else{
              pstmt.close();
              conn.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

优化批量插入

使用批处理操作

  • addBatch():
    • 当调用addBatch()方法时,将当前的 SQL 语句添加到批处理命令列表中。这个方法并不立即执行 SQL 语句,而是将其暂时存储在内存中,等待一次性执行。
  • executeBatch():
    • 当调用executeBatch()方法时,JDBC 驱动程序会将存储在批处理命令列表中的所有 SQL 语句一次性发送给数据库执行。数据库会尽可能地将这些 SQL 语句作为一个批处理进行处理,以提高执行效率。
Connection conn = DriverManager.getConnection(url, user, password);
conn.setAutoCommit(false); // 关闭自动提交

String sql = "INSERT INTO table_name (column1, column2) VALUES (?, ?)";
PreparedStatement pstmt = conn.prepareStatement(sql);

for (int i = 0; i < 1000; i++) {
    pstmt.setString(1, "value1");
    pstmt.setString(2, "value2");
    pstmt.addBatch(); // 添加批处理操作
}

int[] result = pstmt.executeBatch(); // 执行批处理

conn.commit(); // 提交事务

pstmt.close();
conn.close();

实现事务

  • 事务必须是同一个数据库连接
import java.sql.*;

public class TransactionExample {
    public static void main(String[] args) {
        try {
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/database_name", "username", "password");
            connection.setAutoCommit(false);

            Statement statement = connection.createStatement();
            statement.executeUpdate("INSERT INTO table_name (column1, column2) VALUES ('value1', 'value2')");
            statement.executeUpdate("UPDATE table_name SET column1 = 'new_value' WHERE column2 = 'value2'");

            connection.commit();
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
            // 如果出现异常,回滚事务
            try {
                if (connection != null) {
                    connection.rollback();
                    connection.close();
                }
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值