目 录CONTENT

文章目录

JDBC框架

FatFish1
2025-01-18 / 0 评论 / 0 点赞 / 46 阅读 / 0 字 / 正在检测是否收录...

JDBC简介

JDBC是通过java代码操作关系型数据库的一套API,是一套标准接口,mysql、oracle、db2等分别做了实现类

  • jdbc本质上是官方提供的数据库操作规则,即接口

  • 使用java操作数据库只需要学习固定的jdbc规则,具体数据库的变化只需要安装不同的驱动(实现类-jar包)即可

JDBC开发思路

JDBC开发分为以下通用步骤:

  1. 注册驱动

  2. 获取连接

  3. 定义SQL

  4. 获取SQL执行对象Statement

  5. 执行SQL

  6. 处理结果

  7. 释放资源

public static void main(String[] args) throws ExecutionException, SQLException, ClassNotFoundException {
    // 1.注册驱动
    Class.forName("com.mysql.cj.jdbc.Driver");
    // 2.获取连接
    Connection conn = DriverManager.getConnection("jdbc:mysql://ip:prot", "username", "password");
    // 3.定义SQL
    String sql = "select * from t_test";
    conn.setAutoCommit(false);
    // 4.获取SQL执行对象Statement
    PreparedStatement ps = conn.prepareStatement(sql);
    // 5.执行SQL
    ResultSet resultSet = ps.executeQuery();
    conn.commit();
    // 6. 处理结果
    while (resultSet.next()) {
        System.out.println(resultSet.getString("id"));
    }
    // 7.释放资源
    resultSet.close();
    ps.close();
    conn.close();
}

JDBC API

JDBC API解构

按照层次,JDBC提供了四种核心API:

  • DataSource:数据源的抽象,必须提供DataSource#getConnection方法,可以获取连接。一个优秀的数据域的实现可以借助连接池避免每次获取连接都要新建连接,例如非常常用的DruidDataSource

  • Connection:连接的抽象,必须提供获取Statement的能力,包括Connection#createStatementConnection#prepareStatement ,同时还提供一些配置连接属性的能力,例如Connection#setAutoCommit

  • Statement:可执行对象,其实可以理解为语句+编译+执行能力包括Statement和PreparedStatement,以及不太常用的CallableStatement,不论那种Statement,必须具备执行能力,例如PreparedStatement#execute

    • Statement:一个普通的可执行对象,用于通用的查询,更适合一次性存取,日常开发中已经不太常用了

    • PreparedStatement:具备预编译能力的可执行对象,对于重复执行的sql,性能比Statement好一些,同时具备batch能力、占位符和参数化查询能力,可以组织SQL注入攻击,支持动态查询,在JDBC开发中是比较常用的

  • ResultSet:返回结果的抽象,是一个具有游标的,类似Iterable的,结果集封装

了解JDBC API层次的目的是为了更好地理解一些基于JDBC二次封装的组件,例如SpringJDBC、ShardingJDBC、Druid等,因为不管是mysql本身提供的jdbc,还是spring提供的jdbcTemplate都是基于这四个层次进行了或多或少的优化和重构。

例如:

spring的思路是不实现datasource和connection,仅对其进行IOC管理,对statement和preparedStatement进行能力封装进JdbcTemplate中,对ResultSet进行封装成为RowMapper

shardingJdbc的思路是对datasource封装成了shardingDatasource,将connection封装成了shardingConnection,将statement封装成了shardingStatement,在其核心能力的基础上补充了shardingRule作为获取Connection的依据

DataSource

它的作用是获取连接

在原生JDBC架构中,往往不使用DataSource,直接到Connection一级,是借助的JDBC提供的DriverManager类,从而适配MySQL、MariaDB等各种中间件提供的能力

// 2.获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://ip:prot", "username", "password");

url语法要加协议,不能直接iP:port,jdbc:mysql://127.0.0.1:3306/db1,如果连接的是本机且是默认3306端口,可以直接简写为jdbc:mysql:///db1,可以加参数,例如不用ssl的连接方式,jdbc:mysql://127.0.0.1:3306/db1?useSSL=false

Connection

它的作用是获取Sql可执行对象,同时管理事务

通过JDBC的Connection管理事务与之间写法是存在差异的:

mysql中对事务的管理写法:

# 开启事务
BEGIN;  /  START TRANSACTION;
# 提交事务
COMMIT;
# 回滚事务
ROLLBACK;

使用JDBC管理事务的写法:

// 开启事务
setAutoCommit(false);
// 提交事务
commit();
// 回滚事务
rollbcak();

另外就是connection提供了三种可执行对象的获取方法:

  • 普通执行SQL对象:Statement createStatement()

  • 预编译SQL的执行对象,防止sql注入:PreparedStatement preparedStatenment(sql)

  • 执行存储过程的对象:CallableStatement prepareCall(sql)

Statement

它的作用就是执行语句,因此它叫可执行对象

最普通的Statement有两个核心方法:

  • executeUpdate(sql):返回DML语句影响的行数,DDL语句的话执行结果可能是0

  • executeQuery:返回ResultSet结果集对象

PreparedStatement

PreparedStatement的优势

与Statement对比:

  • PreparedStatement实例包含已编译的SQL语句。通过“?”做为保留参数(IN参数)占位符,通过PreparedStatement#setIntPreparedStatement#setString等方法配置保留参数。因此,多次执⾏的SQL语句经常创建为PreparedStatement对象,以提⾼效率

  • PreparedStatement继承了Statement的所有功能。另外,它还添加了⼀整套⽅法,⽤于设置发送给数据库以取代IN参数占位符的值。同时,三种⽅法execute、 executeQuery和executeUpdate已被更改以使之不再需要参数。这些⽅法的Statement形式(接受SQL语句参数的形式)不应该⽤于PreparedStatement对象

PreparedStatement参数占位符案例

// 带有占位符的sql语句
String sql = "select * from t_test where username = ? and age = ? and password = ?";
// 获取PreparedStatement对象
PreparedStatement pstmt = conn.prepareStatement(sql);
// 设置参数值,index从1开始
pstmt.setString(1, "name");
pstmt.setInt(2, 1);
pstmt.setString(3, "password")
// 调用preparedStatement提供的接口
ResultSet rs = pstmt.executeQuery()

如果是date类型,可以使用sql支持的TimeStamp,将java的时间转为

new TimeStamp(Date.getDate());

PreparedStatement批量执行优化

如果sql数量较多,可以使用addBatch和executeBatch进行批次提交和缓冲。

String sql = "select * from t_test where username = ? and age = ? and password = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
for (User user : users) {
pstmt.setString(1, user.getUserName());
pstmt.setInt(2, user.getAge());
pstmt.setString(3, user.getPassword())
    pstmt.addBatch();
}
pstmt.executeBatch()

与executeBatch类似的还有executeUpdate、executeQuery。其中update兼顾更新、插入、删除操作,返回的是修改行数;executeQuery针对的是查询语句,返回结果集。executeBatch执行的是Vector中的n个SQL语句,即批量处理。

想要清空Batch的话可以执行clearBatch()

此外,配合事务管理还可以实现多批事务化执行

PreparedStatement防止sql注入

为了防止sql注入,例如

String sql = “select * from student where password = "'"+ password + "'";

如果语句是这么写的,password通过前端传过来,就有可能产生sql注入,因为如果在密码处输入’or ‘1’=‘1,拼出来就是一个or语句,一定返回true,查出来就是全部数据

而使用PreparedStatement占位符会将占位符识预编译成一个待替换的参数,也不运行替换这种表达式

ResultSet

它的作用是封装查询语句的结果,是一个带游标且可以迭代的包装

两个核心方法:

  • boolean next():游标移动

  • getString/getInt...(String columnName) : 获取数据,参数是列名,根据列名取出指定类型的数据

while (resultSet.next()) {
    int id = resultSet.getInt(“Id”);
    String name = resultSet.getString(“name”);
    double balance = resultSet.getDouble(“balance”);
    int age = resultSet.getInt(“age”);
}

0

评论区