ToB企服应用市场:ToB评测及商务社交产业平台
标题:
03-JDBC
[打印本页]
作者:
飞不高
时间:
2022-8-26 02:23
标题:
03-JDBC
1、JDBC简介
1.1、客户端操作MySQL数据库的方式
使用DOS命令行方式
使用第三方客户端来访问MySQL:SQLyog、Navicat、....
通过程序来访问MySQL数据库
而通过Java来访问MySQL数据库,就是JDBC的概念
1.2、JDBC的概念
什么是JDBC
Java Data Base Connectivity:Java数据库连接
JDBC作用
通过JDBC可以让Java程序操作数据库
JDBC本质
官方(SUN)公司定义的一套操作所有关系型数据库的规则,即接口(API)
各个数据库厂商去实现这套接口,提供数据库驱动jar包
我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类
JDBC的好处
只需要会调用JDBC接口中的方法即可,使用简单
使用同一套Java代码,进行少量的修改就可以访问其他JDBC支持的数据库
2、JDBC API详解
2.1、JDBC四个核心对象
JDBC的使用步骤
注册驱动
获取数据库连接
获取SQL语句对象
执行SQL语句并返回结果
处理结果
释放资源
JDBC四个核心对象
JDBC交互图
2.2、JDBC注册驱动
Java程序需要通过数据库驱动才能连接到数据库,因此需要注册驱动
在注册驱动之前需要先导入驱动的Jar包
JDBC注册驱动
java.sql.DriverManager类用于注册驱动。提供如下方法注册驱动
static void registerDriver(Driver driver) // 向DriverManager 注册给定驱动程序
复制代码
示例代码
public class Demo01 {
public static void main(String[] args) throws Exception {
// 注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
}
}
复制代码
提示
MySQL 5 之后的驱动包,可以省略注册驱动的步骤
自动加载jar包中META-INF/services/java.sql.Driver文件中的驱动类
2.3、获取Connection连接
Connection 介绍
表示Java程序与数据库之间的连接,只有拿到Connection才能操作数据库
DriverManager类中的静态方法描述static Connection getConnection(String url, String user, String password)连接到给定数据库URL,并返回连接
参数说明
String url:连接数据库的URL,用于说明数据库的位置
String user:数据库的账号
String password:数据库的密码
连接数据库的URL地址格式
协议名:子协议://服务器名或IP地址:端口号/数据库名
MySQL写法
jdbc:mysql://localhost:3306/day03
如果是本地服务器,端口号是默认的3306,则可以简写
jdbc:mysql:///day03
2.4、获取Statement对象
在java.sql.Connection接口中有如下方法获取到Statement对象
Statement createStatement() // 创建一个Statement对象来将SQL语句发送到数据库
复制代码
代码案例
// 1.注册驱动
// 2.获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql:///day03", "root", "123");
// 3.获取Statement
Statement stmt = conn.createStatement();
复制代码
3、JDBC实现对单表数据增加、删除、修改
我们要对数据库进行增、删、改、查,需要使用Statement对象来执行SQL语句
Statement的API介绍
ResultSet executeQuery(String sql)
// 用于执行查询语句; 返回查询到的结果集
int executeUpdate(String sql)
// 用于执行除查询外的SQL; 返回影响的行数
复制代码
4、JDBC获取数据(Result类)
4.1、ResultSet的原理
ResultSet用于保存执行查询SQL语句的结果。我们不能一次性去除所有的数据,需要一行一行的去除
ResultSet内部有一个指针,记录获取到哪行数据
获取查询结果
boolean next():
/*
(1) 将光标从当前位置向前移动一行
(2)判断当前行是否为有效行
返回值:
true:有效行,当前行有数据
false:无效行,当前行没有数据
*/
复制代码
应用案例
while (rs.next()) {
rs.getXxx(字段名); // 取出数据
}
复制代码
4.2、ResultSet获取数据的API
ResultSet获取数据的API是有规律的get后面加数据类型。我们统称getXXX()
方法名说明boolean getBoolean(String columnLabel)获取boolean值byte getByte(String columnLabel)获取byte值double getDouble(String columnLabel)获取double值int getInt(String columnLabel)获取int值long getLong(String columnLabel)获取long值String getString(String columnLabel)获取String值
4.3、ResultSet的getXXX方法与MySQL中数据类型的对应
注意
这只是一个建议,不按这个表的对应关系也可以,只要数据类型可以自动转换。如:int类型,使用String去取,也是可以的。但如果是String类型,使用int去取就不行
5、JDBC事务处理
JDBC操作银行转账的事务
Connection接口中与事务有关的方法
使用步骤
1.注册驱动
2.获取连接
3.开启事务
4.获取Statement对象
5.执行SQL语句
6.提交或者回滚事务
7.关闭资源
demo
package _02MySQL.Day03_JDBC.demo05_事务处理_重点;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* JDBC事务处理
*
* 数据准备:
* CREATE TABLE tb_account (
* id INT PRIMARY KEY AUTO_INCREMENT,
* NAME VARCHAR(10),
* balance DOUBLE
* );
*
*-- 添加数据
* INSERT INTO tb_account (NAME, balance) VALUES ('张三', 1000), ('李四', 1000);
*/
public class Demo05 {
public static void main(String[] args) throws SQLException{
// 1. 注册驱动
Connection connection = null;
try {
// 2. 获取数据库连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/day03", "root", "123");
// 3. 开启事务
connection.setAutoCommit(false); // 关闭自动提交
// 4. 获取Statement对象
Statement statement = connection.createStatement();
// 5. 执行SQL
String sql1 = "update tb_account set balance = balance - 500 where name = '张三'";
String sql2 = "update tb_account set balance = balance + 500 where name = '李四'";
statement.executeUpdate(sql1);
statement.executeUpdate(sql2);
// 模拟失败
// ...
// 6. 提交事务
System.out.println("提交事务!");
connection.commit();
} catch (Exception e) {
// 6. 有异常,事务回滚
if (connection != null) {
connection.rollback();
}
}finally {
// 7. 关闭资源
if (connection != null) {
connection.close();
}
}
}
}
复制代码
6、JDBC实现用户登录
模拟用户输出账号和密码登录网站
案例分析
1.使用数据库中保存用户的账号和密码
2.让用户输入账号和密码
3.使用SQL根据用户的账号和密码去数据库查询数据
4.如果查询到数据,说明登录成功
5.如果查询不到数据,说明登录失败
Demo
package com.itheima.demo06_JDBC实现用户登录;
import java.sql.*;
import java.util.Scanner;
/**
* JDBC实现用户登录案例
*/
public class Demo06 {
public static void main(String[] args) throws SQLException {
Scanner scanner = new Scanner(System.in);
String userName = null;
String password = null;
// 1. 接收用户名
while (true) {
System.out.println("请输入用户名:");
String line = scanner.nextLine();
if ("".equals(line)) {
continue;
}
userName = line;
break;
}
// 2.接收用户密码
while (true) {
System.out.println("请输入密码:");
String line = scanner.nextLine();
if ("".equals(line)) {
continue;
}
password = line;
break;
}
// 3.根据用户名和密码,查询用户
String sql = "SELECT * FROM USER WHERE NAME = '" + userName + "' AND PASSWORD = '" + password + "';";
String jdbcUrl = "jdbc:mysql://localhost:3306/day03";
Connection conn = DriverManager.getConnection(jdbcUrl, "root", "root");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
// 4.根据查询结果,判断是否登录成功
// 4.1 如果查询到数据,显示登录成功
if (rs.next()) {
System.out.println("登录成功!欢迎您," + userName);
} else {
// 4.2 如果查询不到数据,显示登录失败
System.out.println("登录失败!用户名或密码错误...");
}
}
}
复制代码
7、SQL注入攻击
7.1、SQL注入问题
在我们前面JDBC实现登录案例中,当我们输入以下密码的时候,可以发现账号和密码都不对竟然登录成功了!
请输入用户名:
hehe
请输入密码:
a'or'1'='1
复制代码
字符串拼接,把输入的字符串全都将其视为sql语句,导致statement对象查询直接为真,条件不起作用!
问题分析
"SELECT * FROM user WHERE name='" + name + "' AND password='" + password + "';";
// 将用户输入的账号密码拼接后
"SELECT * FROM user WHERE name='hehe' AND password='a'or'1'='1';"
复制代码
SQL注入攻击的原理
按照正常道理来说,在密码处输入的所有内容,都应该认为是密码的组成
但是现在Statement对象在执行sql语句时,将密码的一部分内容当作查询条件来执行了。
7.2、解决SQL注入
PreparedStatement预编译执行者对象
预编译:SQL语句子在执行前就已经编译好了,执行速度更快
安全性更高:没有字符串拼接的SQL语句,所以避免SQL注入的问题
代码的可读性更好,是因为没有字符串拼接
PreparedStatement使用
SQL语句中的参数使用?作为参为辐
给?占位符赋值
设置参数
setXxx(参数1,参数2):Xxx代表数据类型
参数1:第几个?(编号从1开始)
参数2:?的实际参数
执行SQL语句
int executeUpdate()
执行insert、update、delete语句
ResultSet executeQuery()
执行select语句
demo
String sql = "SELECT * FROM USER WHERE NAME=? AND PASSWORD=?;";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, “zhangsan”);
pstmt.setString(2, “6666”);
复制代码
7.3、使用PreparedStatement改写登录案例
package _02MySQL.Day03_JDBC.demo08_PreparedStatement改写登录案例;
import java.sql.*;
import java.util.Scanner;
/**
* PreparedStatement改写登录案例
*/
public class Demo08 {
public static void main(String[] args) throws SQLException {
// 1. 注册驱动
// 2. 获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/day03", "root", "123");
// 3. 获取PreparedStatement对象
String sql = "select * from day03.user where phoneNumber = ? and password = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 准备参数
Scanner scanner = new Scanner(System.in);
String phoneNumber = null;
String password = null;
do {
System.out.print("请输入账号:");
phoneNumber = scanner.nextLine();
} while ("".equals(phoneNumber));
do {
System.out.print("请输入密码: ");
password = scanner.nextLine();
} while ("".equals(password));
// 4. 执行SQL语句
preparedStatement.setString(1, phoneNumber);
preparedStatement.setString(2, password);
ResultSet resultSet = preparedStatement.executeQuery();
// 5. 处理结果
if (resultSet.next()) {
System.out.println("欢迎您,尊敬的" + phoneNumber + "用户");
}else {
System.out.println("账号或密码错误!");
}
// 6. 释放资源
connection.close();
}
}
复制代码
8、JDBC连接池
8.1、常规数据库连接的问题
JDBC访问数据库的步骤
创建数据库连接
运行SQL语句
关闭连接
上述动作每次数据库访问都会执行这样的重复动作
而每次创建数据库连接的问题
获取数据库连接需要消耗比较多的资源,而每次操作都要重新获取新的连接对象,执行一次操作就把连接关闭,而数据库创建连接通常需要消耗相对较多的资源。这样数据库连接对象的使用率低
8.2、数据库连接池简介
现实生活中每日三餐。我们并不会吃一餐饭就将碗丢掉,而是吃完饭后将碗放到碗柜中,下一餐接着使用。目的是重复利用碗。数据库连接也可以重复使用,可以减少数据库连接的创建次数。提高数据库连接对象的使用率
连接池的概念
连接池就是一个容器,连接池中保存了一些数据库连接,这些连接是可以重复使用的
连接池的原理
1.启动连接池,连接池就会初始化一些连接
2.当用户需要使用数据库连接,直接从连接池中取出
3.当用户使用完连接,会将连接重新放回连接池中
连接池的好处
连接池中保存一些连接,这些连接可以重复使用,降低数据资源的消耗
8.3、常用连接池的介绍
javax.sql.DataSource表示数据库连接池,也是JDK中提供的一个接口,没有具体的实现,它的实现由连接池的厂商去实现。我们只需要学习这个工具如何使用
public interface DataSource{
Connection getConnection();
...
}
复制代码
常用的连接池实现组件有以下这些
阿里巴巴-德鲁伊Druid连接池
Druid是阿里巴巴开源平台上的一个项目
C3P0是一个开源的连接池,目前使用它的开源项目有Hibernate,Spring等
DBCP(DataBase Connection Pool)数据库连接池,是Tomcat使用的连接池组件
8.3、Druid连接池简介
Druid是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是目前最好的数据库连接池。在功能、性能、扩展性方面,都超过了其他数据连接池,同时加入了日志监控,可以很好的监控数据库连接池和SQL的执行情况。
Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产大环境部署的严苛考验
Druid常用的配置参数
方法名说明initialSize刚启动连接池时,连接池中包含连接的数量maxActive连接池中最多可以放多少个连接maxWait获取连接时最大等待时间,单位毫秒
com.alibaba.druid.pool.DruidDataSourceFactory类创建连接池的方法
public static DataSource createDataSource(Properties properties) // 创建一个连接池,连接池的参数使用properties中的数据
复制代码
我们可以看到Druid连接池在创建的时候需要一个Properties对象来设置参数,所以我们使用properties文件来保存对应的参数。Druid连接池的配置文件名称随便,放到src目录下面方便加载
druid.properties文件内容
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/day17
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000
复制代码
8.4、Druid连接池使用步骤
1.导入druid-1.0.0.jar的jar包
2.赋值druid.properties文件到src下,并设置对应参数
3.加载properties文件的内容到Properties对象中
4.创建Druid连接池,使用配置文件中的参数
5.从Druid连接池中取出连接
6.执行SQL语句
7.关闭资源
9、JDBC案例(在Java程序中实现对数据库的增删改查)
Demo类
package _02MySQL.Day03_JDBC.demo10_JDBC增删改查练习;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* JDBC增删改查练习---- 完成商品品牌数据的增删改查操作
* 查询数据:查询所有数据
* 添加数据:添加品牌
* 修改数据:根据id修改
* 删除数据:根据id删除
*/
public class Demo10 {
/*
数据准备:
创建一个商品品牌的数据库表 tb_brand
创建一个Brand实体类
*/
public static void main(String[] args) throws Exception {
// 1. 增加数据
// addBrand();
// 2. 删除数据
deleteBrand();
// 3. 修改数据
editBrand();
// 4. 查询数据
seekBrand();
}
public static void addBrand() throws SQLException {
// 获取连接
Connection connection = DataSourceUtils.getConnection();
// 获取preparedStatement对象
String sql = "insert into day03.tb_brand (brand, description, headquarters) values" +
"(?, ?, ?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "格力空调");
preparedStatement.setString(2, "美好生活格力造");
preparedStatement.setString(3, "China");
// 得到statement对象返回的影响行数
int i = preparedStatement.executeUpdate();
System.out.println(i);
// 将连接返回给连接池
connection.close();
}
// 根据id删除数据
public static void deleteBrand() throws SQLException {
// 获取连接
Connection connection = DataSourceUtils.getConnection();
// 编写删除数据的SQL, 并获取preparedStatement对象
String sql = "delete from day03.tb_brand where id = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 5);
// 执行SQL, 返回影响的行数
int i = preparedStatement.executeUpdate();
System.out.println(i);
// 将连接返回给连接池
connection.close();
}
// 根据id编辑数据
public static void editBrand() throws SQLException {
// 获取连接
Connection connection = DataSourceUtils.getConnection();
// 编写sql,并获取preparedStatement对象
String sql = "update day03.tb_brand set brand = ?, description = ?, headquarters = ? where id = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 补全SQL
preparedStatement.setString(1, "华强北");
preparedStatement.setString(2, "天上地下我华强北说第二,谁敢说第一");
preparedStatement.setString(3, "中国深圳");
preparedStatement.setInt(4, 1);
// 执行SQL语句, 返回影响的行数
int i = preparedStatement.executeUpdate();
System.out.println(i);
// 关闭连接,将连接返回给连接池
connection.close();
}
public static void seekBrand() throws SQLException {
// 获取连接
Connection connection = DataSourceUtils.getConnection();
// 获取Statement对象
String sql = "select * from day03.tb_brand";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 得到数据集合
ResultSet resultSet = preparedStatement.executeQuery();
// 处理数据
List<Brand> brandList = new ArrayList<>(); // 存储数据
while (resultSet.next()) {
int id = resultSet.getInt("id");
String brand = resultSet.getString("brand");
String description = resultSet.getString("description");
String headquarters = resultSet.getString("headquarters");
brandList.add(new Brand(id, brand, description, headquarters));
}
brandList.forEach((brand) -> System.out.println("data = " + brand));
// 将连接返回给连接池
connection.close();
}
}
复制代码
DataSourceUtils工具类(获取Datasource)
package _02MySQL.Day03_JDBC.demo10_JDBC增删改查练习;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* 工具类
* -- 创建Druid连接池
*/
public class DataSourceUtils {
private static DataSource dataSource;
static { // 利用静态代码块加载创建连接池的操作
try {
// 1. 载入配置文件
// 获取properties配置文件
InputStream inputStream = DataSourceUtils.class.getResourceAsStream("brand.properties");
Properties properties = new Properties();
properties.load(inputStream);
// 2. 创建连接
dataSource = DruidDataSourceFactory.createDataSource(properties);
}catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
复制代码
Brand实体类
package _02MySQL.Day03_JDBC.demo10_JDBC增删改查练习;
public class Brand {
private int id;
private String brand;
private String description;
private String headquarters;
public Brand(int id, String brand, String description, String headquarters) {
this.id = id;
this.brand = brand;
this.description = description;
this.headquarters = headquarters;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getHeadquarters() {
return headquarters;
}
public void setHeadquarters(String headquarters) {
this.headquarters = headquarters;
}
@Override
public String toString() {
return "Brand{" +
"id=" + id +
", brand='" + brand + '\'' +
", description='" + description + '\'' +
", headquarters='" + headquarters + '\'' +
'}';
}
}
复制代码
brand.properties配置文件
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/day03
username=root
password=123
initialSize=5
maxActive=10
maxWait=3000
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4