03_JDBC

文章目录

  • 数据库的访问流程
  • JDBC
    • JDBC实现流程
    • 使用JDBC进行增删改查
    • 重要的API
      • DriverManager
      • Connection
      • Statement
      • ResultSet
    • JDBC实现流程的优化
    • 数据库注入问题
    • 批处理
      • for循环逐条插入
      • statement批处理
      • preparedStatement批处理
    • 数据库的事务
      • 事务的步骤
      • 事务的API
      • 事务的特性
      • 事务的隔离级别
      • 演示隔离级别

数据库的访问流程

在这里插入图片描述

JDBC

  • Java Database Connection,Java数据库连接。其实就是在Java中,帮助我们去连接数据库的一个“东西”。
  • 具体指的就是 Java的一套标准的连接数据库的接口
  • 标准的接口具体指:java.sqljavax.sqlrt.jar内部的

在这里插入图片描述


JDBC实现流程

  1. 新建项目
  2. 下载jar包,下载网址:https://mvnrepository.com/
  3. 把下载好的jar包拷贝到项目中,再右键jar包选择add as library这个选项
  4. 写代码
      1. 注册驱动
      • DriverManager.registerDriver(new Driver());,这里的Driver()一定是mysql驱动包里面的。
      1. 获取连接
      • 传三个参数:urlusernamepassword
      1. 获取statement对象,用它来执行SQL
      1. 执行SQL
      1. 解析结果
      1. 断开连接
public class Demo {public static void main(String[] args) throws SQLException {// 1. 注册驱动DriverManager.registerDriver(new Driver());// 2.获取连接/*url包括:1. 协议2. 域名/ip+端口3. 服务器内部路径4. 参数*/String url = "jdbc:mysql://localhost:3306/test_20240301?useSSL=false&characterEncoding=utf8";// 如果要中文编码,必须要设置:characterEncoding=utf8// useSSL=false用来解决warning问题String username = "root";String password = "123456";Connection connection = DriverManager.getConnection(url, username, password);// 3. 获取statement对象Statement statement = connection.createStatement();// 4. 执行SQL// 增删改都是在update方法里面int affectedRows = statement.executeUpdate("insert into t_staff values (126, 'Jack', '教授', '数据挖掘')");// 5. 解析结果System.out.println(affectedRows);// 6. 断开连接statement.close();connection.close();}
}

使用JDBC进行增删改查

增、删、改都是一样的,都是使用 statement.executeUpdate(String sql) 来执行SQL语句,返回的结果也是一样的,都是影响的行数。

eg:

// 增
statement.executeUpdate("insert into student values (1, 'zs', 25)");// 删
statement.executeUpdate("delete from student where id = 1");// 改
statement.executeUpdate("update student set name = 'ls' where id = 1");

通过statment.executeQuery()方法

eg:

public class Demo2 {public static void main(String[] args) throws SQLException {// 1.注册驱动DriverManager.registerDriver(new Driver());// 2. 建立连接String url = "jdbc:mysql://localhost:3306" +"/test_20240301?characterEncoding=utf8&useSSL=false";String username = "root";String password = "123456";Connection connection = DriverManager.getConnection(url, username, password);// 3. 获取statementStatement statement = connection.createStatement();// 4. 执行SQL// 查询方法 executeQueryResultSet resultSet = statement.executeQuery("select * from t_staff");// 刚刚经过的有没有元素boolean next = resultSet.next();// 现在通过resultSet对象获取的就是刚刚经过的那一行的数据int stfid = resultSet.getInt("stfid");String name = resultSet.getString("name");String title = resultSet.getString("title");String direction = resultSet.getString("direction");System.out.println(stfid + " -- " + name + " -- " + title + " -- " + direction);// 想获取resultSet里面的所有数据while(resultSet.next()){int stfid1 = resultSet.getInt("stfid");String name1 = resultSet.getString("name");String title1 = resultSet.getString("title");String direction1 = resultSet.getString("direction");System.out.println(stfid1 + " -- " + name1 + " -- " + title1 + " -- " + direction1);}// 5. 解析结果// 6. 关闭连接statement.close();connection.close();

或者:

public class Demo {public static void main(String[] args) throws SQLException, ClassNotFoundException {Connection connection = JdbcUtil.getConnection();Statement statement = connection.createStatement();ResultSet resultSet = statement.executeQuery("select * from t_staff");// 解析结果,用到了getMetaDataResultSetMetaData metaData = resultSet.getMetaData();// 可以从元数据里面拿到 返回多少列int columnCount = metaData.getColumnCount();List<String> columns = new ArrayList<>();for (int i = 1; i <= columnCount; i++) {String columnName = metaData.getColumnName(i);columns.add(columnName);}System.out.println(columns);while (resultSet.next()) {// 数据库里的数据从1开始List<String> list = new ArrayList<>();for (int i = 1; i <= columnCount; i++) {String value = resultSet.getString(i);list.add(value);}System.out.println(list);}JdbcUtil.closeConnection(connection, statement, resultSet);}
}--->[stfid, name, title, direction]
[123, 邹华, 教授, 机器识别]
[124, 李克, 讲师, 机器识别]
[125, 王益, 讲师, 人工智能]
[126, 李华, 教授, 数据挖掘]

重要的API

DriverManager

驱动管理器。可以帮助我们管理驱动,获取连接

// 注册驱动
DriverManager.registerDriver(new Driver);// 获取连接
// 获取到的连接对象实际上是 JDBC4Connection 对象
Connection conn = DriverManager.getConnection(String url,String username,String password);

Connection

指代连接对象
在JDBC中是一个接口,在MySQL运行过程中,实际上实现类是 com.mysql.jdbc.JDBC4Connection 对象。

// 获取statement
Statement stat = connection.createStatement();// 关闭连接
connection.close();// 事务相关的API
connection.commit();
connection.rollback();
connection.setAutoCommit(false);

Statement

statement对象其实就是用来去执行SQL语句,并且返回这个SQL语句产生的结果集
实际上我们在使用的时候,其实是用的Statement接口的实现类 com.mysql.jdbc.StatementImpl

// 增删改的方法
statement.executeUpdate("sql语句");// 查询方法
statement.executeQuery("sql语句");// execute方法会返回一个boolean值,boolean值其实是代表了是查询还是增删改
// 如果为true,代表是查询,则可以通过statement.getResultSet()来获取查询的结果
// 如果为false,代表是增删改,则可以通过statement.getUpdateCount()来获取影响的行数
statement.execute("sql语句");eg:
// 执行sql语句
Boolean ret = statement.execute(String sql);// 如果 ret == true,那么说明执行的是查询语句
// 如果 ret == false,那么说明执行的是增删改语句获取影响的行数: statement.getUpdateCount();
int affactedRows = statement.getUpdateCount();
System.out.println(affactedRows);获取返回的结果集:statement.getResultSet();

ResultSet

  • 表示查询的结果集
  • 在查询的结果集中,有一个游标,游标可以移动,移动的时候会扫描一些行,那么对于这些扫描到的行,我们就可以获取对应的列的值
// 移动游标方法// 向下移动
Boolean ret = resultSet.next();// 向上移动
Boolean ret = resultSet.previous();// 定位到第一行之前
resultSet.beforeFirst();// 定义到最后一行之后
resultSet.afterLast();// 获取值的方法
resultSet.getInt(String columnName);
resultSet.getString(String columnName);
resultSet.getDate(String columnName);

JDBC实现流程的优化

  • 注册驱动的方式抽取成一个方法
    • 之前是强依赖MySQL驱动包
    • 现在换成反射的方式,只有实际运行的时候才需要
JdbcUtils.javapublic class JdbcUtil {static String url;static String username;static String password;static {Properties properties = new Properties();try {properties.load(new FileInputStream("jdbc.properties"));} catch (IOException e) {throw new RuntimeException(e);}url = properties.getProperty("url");username = properties.getProperty("username");password = properties.getProperty("password");}public static Connection getConnection() throws ClassNotFoundException, SQLException {// 1.注册驱动// 用到mysql包中的类,不要直接依赖mysql的包// DriverManager.registerDriver(new Driver());Class.forName("com.mysql.jdbc.Driver");// 2.获取连接return DriverManager.getConnection(url, username, password);}
}
  • 获取连接的方式换成一个properties文件
    • properties文件不要加" "
url=jdbc:mysql://localhost:3306/test_20240301?useSSL=false&characterEncoding=utf8
username=root
password=123456
  • main函数
public class Demo {public static void main(String[] args) throws SQLException, ClassNotFoundException {Connection connection = JdbcUtil.getConnection();// 3. 获取statementStatement statement = connection.createStatement();// 4. 执行SQL// 查询方法 executeQueryboolean execute = statement.execute("update t_staff set name = '李华' where stfid = 126");// 5. 解析结果System.out.println("execute = " + execute);// 6. 关闭连接statement.close();connection.close();}
}

但是有bug,不知道怎么解决

在这里插入图片描述

优化的好处

  1. 注册驱动的时候,改成了反射,运行的时候才需要
  2. 把获取连接,抽成了一个工具类
  3. 数据库的配置信息,写成了配置文件
  4. 优化了关闭连接的方法

数据库注入问题


产生的原因
因为SQL语句是通过字符串拼接的,这个时候用户可能输入一些字符,这些字符中包含有SQL语句中的关键字,那么通过字符串拼接SQL语句之后,可能会改变SQL语句的格式,进而引发安全性的问题
eg:select * from user where name = 'xxx' and password = 'xxx' or '1=1'

根本的原因:MySQL把用户输入的参数当做关键字来解析了

解决SQL注入:最关键是把用户输入的东西当作一个纯字符串

预编译

  • 首先先写SQL的架子,预编译的过程,我才会把给的SQL当SQL关键字解析
  • 设置参数,这个时候设置的参数一律当普通字符串处理
  • 这个时候可以防止SQL注入

eg:

public class Demo {public static void main(String[] args) throws SQLException, ClassNotFoundException {boolean loginSuccess = login("admin", "admin");if(loginSuccess){System.out.println("登录成功");}else{System.out.println("登录失败");}}private static boolean login(String username, String password) throws SQLException, ClassNotFoundException {Connection connection = JdbcUtil.getConnection();Statement statement = connection.createStatement();// 要用到预编译// 预编译:就是首先把SQL的架子写好// 后面填充的数据一律按普通字符串处理// 使用 ? 作为占位符,预编译结束后,再填充参数PreparedStatement preparedStatement = connection.prepareStatement("select * from user where name = ? and password = ?");// 对preparedStatement设置参数// 把index为1的username值填到第一个?// 把index为2的password值填到第二个?preparedStatement.setString(1,username);preparedStatement.setString(2,password);ResultSet resultSet = preparedStatement.executeQuery();if(resultSet.next()){return true;}else{return false;}}
}

普通Statement的执行流程

在这里插入图片描述
prepareStatement的执行流程

在这里插入图片描述


批处理

是批量的处理SQL语句,典型的业务场景就是一次插入大量的数据

for循环逐条插入

发送SQL10次,编译SQL10次,执行10次

public void forInsert() throws SQLException {Statement statement = connection.createStatement();for (int i = 0; i < 10; i++) {// "insert into t values ("+ i + ",'for')"这里是字符串的拼接statement.executeUpdate("insert into t values ("+ i + ",'for')");}statement.close();
}

statement批处理

  • 发送SQL1次,编译SQL10次,执行10次
  • 先通过addBatch()往集合里面插入,等到executeBatch()时候再一起提交
public void statementInsert() throws SQLException {Statement statement = connection.createStatement();for (int i = 10; i < 20; i++) {String sql = "insert into t values ("+i+",'batchUseStatement')";// statement的addBatch是先把sql存起来statement.addBatch(sql);}statement.executeBatch();statement.close();
}

preparedStatement批处理

  • 需要在数据库的url后面加上配置:rewriteBatchedStatements=true,表示开启批处理
    • 因为开了这个配置会把SQL转换一下,时间会显著加快

eg:

insert into t values(1, 'pre');
insert into t values(2, 'pre');
insert into t values(3, 'pre');---->
会把这个SQL转换一下
insert into t values(1, 'pre'),(2, 'pre'),(3, 'pre');
  • 发送SQL1次,编译SQL1次,执行10次
  • 先给preparedStatement设置值,存起来,最后再一次性提交
// 使用prepareStatement 的批处理方法public void prepareStatementBatch() throws SQLException {PreparedStatement preparedStatement = connection.prepareStatement("insert into t values (?,'pre')");for (int i = 20; i < 30; i++) {preparedStatement.setInt(1, i);preparedStatement.addBatch();}preparedStatement.executeBatch();preparedStatement.close();}

假如批处理N条数据的对比图

通信次数编译次数执行次数时间
for循环nnn最长
Statement1nn次之
PreparedStatement21n最短

数据库的事务

  • 数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。
  • 事务由事务开始与事务结束之间执行的全部数据库操作组成。
  • 事务就是要保证一组数据库操作,要么全部成功,要么全部失败
  • 应用:转账

事务的步骤

  1. 开启事务
  2. 提交事务
  3. 回滚事务

事务的API

开启事务:
connection.setAutoCommit(false);提交事务:
connection.commit();回滚事务:
connection.rollback();

如果用cmd命令行操作

# 开始事务
start transaction;# 提交事务
commit;# 回滚事务
rollback;

eg:(演示转账的操作

    1. 需要从转账方扣钱 update account set money = money - ? where name = ? and money > ?
    • 扣转账方的钱,并且保证钱一定是够的
    1. 给转入方加钱 update account set money = money + ? where name = ?
public class Demo {public static void main(String[] args) throws SQLException, ClassNotFoundException {transfer("zs", "ls", 1000);}private static void transfer(String fromName, String toName, int money)throws SQLException, ClassNotFoundException {Connection connection = JdbcUtil.getConnection();// 开启事务connection.setAutoCommit(false);try {// 建立一个account表// 扣A的钱PreparedStatement preparedStatement = connection.prepareStatement("update account set money = money - ? " +"where name = ? and money > ?");preparedStatement.setInt(1, money);preparedStatement.setString(2, fromName);preparedStatement.setInt(3, money);int affactRows1 = preparedStatement.executeUpdate();if(affactRows1 != 1){// 没找到人throw new RuntimeException("fromName not found people or money is not enough." + affactRows1);}// 给B加钱PreparedStatement preparedStatement1 = connection.prepareStatement("update account set money = money + ? " +"where name = ? ");preparedStatement1.setInt(1, money);preparedStatement1.setString(2, toName);int affactRows2 = preparedStatement1.executeUpdate();if(affactRows2 != 1){// 没找到人throw new RuntimeException("fromName not found people or money is not enough." + affactRows2);}connection.commit();} catch (Exception e) {// 回滚connection.rollback();e.printStackTrace();}}
}

事务的特性

  • ACID

  • 原子性(Atomicity)

    • 事务是一个不可分割的操作单元,事务中的操作要么就都成功,要么就都不成功
  • 一致性(Consistency)

    • 事务必须使数据库从一个一致性状态到另外一个一致性状态

    • 在转账案例中,一致性是指在转账前和转账后,(无论怎么转账),钱的总金额是前后一致的,不变的

  • 隔离性(Isolation)

    • 事务与事务之间是互相隔离的,互不影响的。

    • 数据库有为隔离性设置不同的隔离级别。不同的隔离级别对于隔离性的影响是不一样的。

  • 持久性(Durability)

    • 一个事务一旦生效,那么对数据库的改变是永久的,不可逆转的。意思就是提交了事务之后,就已经对数据库产生的变化,那么后续再回滚就回滚不了了

事务的隔离级别


当数据库有多个事务同时执行的时候,可能会出现问题

  • 脏读
    • 一个事务读取到了另外一个事务没有提交的数据。
      在这里插入图片描述
  • 不可重复读
    • 在同一个事务中,读取同一个数据,前后读取的数据不一致

    • 通常指的是,在一个事务中,读取到了另外一个事务已经提交的数据
      在这里插入图片描述

  • 幻读
    • 指在同一个事务中,读取同一个表数据,前后读取的数量不一致

    • 通常指的是,在一个事务,读取到了另外一个事务插入或者删除的数据
      在这里插入图片描述

事务的隔离级别

  • 读未提交

    • 一个事务还没提交时,它做的变更就能被别的事务看到,会产生脏读
    • 在这里插入图片描述
  • 读已提交

    • 一个事务提交之后,它做的变更才会被其他事务看到。
    • 可以避免脏读,但是没有避免不可重复读
    • 在这里插入图片描述
  • 可重复读

    • 这个是MySQL默认的隔离级别:可重复读
    • 一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。
    • 在这里插入图片描述
  • 串行化

    • 对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
    • 都可以避免
    • 但是效率太低,工作中的使用率低
    • 在这里插入图片描述

eg:
在这里插入图片描述

演示隔离级别

直接在cmd命令行中演示


一些隔离级别的API

# 获取当前数据库的隔离级别
select @@transaction_isolation;
select @@tx_isolation;
# 设置隔离级别
set global transaction isolation level 隔离级别;隔离级别:
read uncommitted;
read committed;
repeatable read
serializable;# 注意。设置了隔离级别。必须要重新启动一下客户端,才能生效。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/522923.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机组成原理之机器:存储器之高速缓冲存储器

计算机组成原理之机器&#xff1a;存储器之高速缓冲存储器 笔记来源&#xff1a;哈尔滨工业大学计算机组成原理&#xff08;哈工大刘宏伟&#xff09; Chapter3&#xff1a;存储器之高速缓冲存储器 3.1 概述 3.1.1 为什么用cache&#xff1f; 角度一&#xff1a;I/O设备向…

Layer1 隐私安全项目 Partisia Blockchain 空投计划邀你瓜分 2500W 枚 MPC 奖励!

&#x1f6f0;️ Partisia 及基金会介绍 作为一个以 Web3 安全为技术方向的 Layer1 区块链&#xff0c;Partisia Blockchain 自 2021 年诞生之日起已完成了 3 项主要的技术创新。为了创建更安全、快速的数字基础设施&#xff0c;Partisia Blockchain 实现了 1 秒以内的快速交易…

macos docker baota 宝塔 搭建 ,新增端口映射

拉取镜像仅拉取镜像保存到本地&#xff0c;不部署容器&#xff0c;仅需拉取一次&#xff0c;永久存储到本地镜像列表 docker pull akaishuichi/baota-m1:lnmp 其他可参考&#xff1a;宝塔面板7.9.2docker镜像发布-集成LN/AMP支持m1/m2 mac版本 - Linux面板 - 宝塔面板论坛 运行…

CDN(内容分发网络):加速网站加载与优化用户体验

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Linux服务器安装jdk

背景: 安装JDK是我们java程序在服务器运行的必要条件,下面描述几个简单的命令就可再服务器上成功安装jdk 命令总览: yum update -y yum list | grep jdk yum -y install java-1.8.0-openjdk java -version 1.查看可安装版本 yum list | grep jdk 2.如果查不到可先进行 yum upd…

VBA中类的解读及应用第十讲:限制文本框的输入,使其只能输入数值(上)

《VBA中类的解读及应用》教程【10165646】是我推出的第五套教程&#xff0c;目前已经是第一版修订了。这套教程定位于最高级&#xff0c;是学完初级&#xff0c;中级后的教程。 类&#xff0c;是非常抽象的&#xff0c;更具研究的价值。随着我们学习、应用VBA的深入&#xff0…

供应链管理系统(SCM):得供应链得天下不是空话。

2023-08-26 15:51贝格前端工场 Hi&#xff0c;我是贝格前端工场&#xff0c;优化升级各类管理系统的界面和体验&#xff0c;是我们核心业务之一&#xff0c;欢迎老铁们评论点赞互动&#xff0c;有需求可以私信我们 一、供应链对于企业的重要性 供应链对企业经营的重要性不可…

二叉树入门

这篇博客通过手动创建的一个简单二叉树&#xff0c;实现二叉树遍历&#xff0c;返回节点&#xff0c;叶子个数&#xff0c;查找结点等相关操作。 1. 二叉树的概念 二叉树不为空时&#xff0c;由根节点&#xff0c;左/右子树组成&#xff0c;逻辑结构如下&#xff0c;当二叉树…

智慧农业5G融合方案(2)

应用场景 农业航空 农业农村部资料显示,植保无人机具有机动灵活、喷施效率高、施药效果好等特点,能够克服复杂地形条件下地面喷雾机具进地难的问题。目前我国有400多家企业从事植保无人机研发、生产、销售等全产业链业务。主要机型以电动多旋翼为主。2018年作业面积约2.67亿…

sign加密方法生成

1. 引入包的问题 2. 原因 .pycrypto、pycrytodome和crypto是一个东西&#xff0c;crypto在python上面的名字是pycrypto&#xff0c;它是一个第三方库&#xff0c;但是已经停止更新 3. 解决方法 --直接安装&#xff1a;pip install pycryptodome 3.但是&#xff0c;在使用的时…

日韩媒体宣传案例分析:CloudNEO 为您提供海外媒体宣传最佳途径

近年来&#xff0c;随着互联网的迅速发展和全球化的加速推进&#xff0c;海外市场对于企业的重要性日益凸显。尤其是在亚洲地区&#xff0c;日本和韩国作为亚洲最具活力和潜力的市场之一&#xff0c;成为众多企业争相开拓的目标。在这个过程中&#xff0c;媒体宣传不仅是企业推…

教你如何在公众号商城与小程序商城间做出明智之选

随着数字化时代的快速发展&#xff0c;微信公众号和小程序商城这两种电商解决方案各自展现了不同的魅力和挑战。对于计划自主搭建电商平台的用户而言&#xff0c;如何在这两者间作出明智的抉择&#xff0c;无疑是一个值得深思的问题。本文旨在剖析公众号商城和小程序商城的优劣…