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语句
- int executeUpdate()
-
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使用的连接池组件
- 阿里巴巴-德鲁伊Druid连接池
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
-
来源:https://www.cnblogs.com/OnlyOnYourself-lzw/p/16471683.html
本站部分图文来源于网络,如有侵权请联系删除。