1. 概述
本笔记是学习尚硅谷教育的23版jdbc的课后笔记
1.1 JDBC概念和理解
1. jdbc是(Java Database Connectivity)单词的缩写,翻译为java连接数据库
2. jdbc是java程序连接数据库的技术统称
3. jdbc由java语言的规范(接口)和各个数据库厂商的实现驱动(jar)组成
4. jdbc是一种典型的面向接口编程
5. jdbc优势
1. 只需要学习jdbc规范接口的方法,即可操作所有的数据库软件
2. 项目中期切换数据库软件,只需要更换对应的数据库驱动jar包,不需要更改代码
1.2 jdbc核心api和使用路线
1 jdbc技术组成
1. jdk下jdbc规范接口, 存储在java.sql和javax.sql包中的api
为了项目代码的可移植性,可维护性,SUN公司从最初就制定了Java程序连接各种数据库的统一接口规范。这样的话,不管是连接哪一种DBMS软件,Java代码可以保持一致性。
2. 各个数据库厂商提供的驱动jar包
因为各个数据库厂商的DBMS软件各有不同,那么内部如何通过sql实现增、删、改、查等管理数据,只有这个数据库厂商自己更清楚,因此把接口规范的实现交给各个数据库厂商自己实现。
jar包是什么?
java程序打成的一种压缩包格式,你可以将这些jar包引入你的项目中,然后你可以使用这个java程序中类和方法以及属性了!
2 涉及具体核心类和接口
3 jdbc的api使用路线
1.3 为什么选择8+版本mysql-jdbc驱动
2. 核心API
2.1 引入mysql-jdbc驱动jar
驱动选择
导入jar包
2.2 jdbc使用步骤
1. 注册驱动 (导入jar包)
2. 获取连接 (建立connection)
3. 创建发送sql语句对象 (statement)
4. 发送sql语句,并获取返回结果 (statement对象发送sql语句到数据库 并且获取返回结果resultset)
5. 结果集解析 (resultset解析)
6. 资源关闭 (释放connection 释放statement 释放resultset)
2.3 基于statement演示查询
在数据库创建如下数据
CREATE DATABASE testdatabase;
USE testdatabase;
CREATE TABLE t_user(
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户主键',
account VARCHAR(20) NOT NULL UNIQUE COMMENT '账号',
PASSWORD VARCHAR(64) NOT NULL COMMENT '密码',
nickname VARCHAR(20) NOT NULL COMMENT '昵称');
INSERT INTO t_user(account,PASSWORD,nickname) VALUES
('root','123456','经理'),('admin','666666','管理员');
查询全部用户信息进行控制台输出
基于statement实现查询
public class StatementQueryPart {
/**
* TODO:
* 注册驱动
* 依赖:驱动版本8+ com.mysql.cj.jdbc.Driver
* 驱动版本5+ com.mysql.cj.Driver
*/
public static void main(String[] args) throws SQLException {
//1.注册驱动
/**
* TODO: 注意
* Driver -> com.mysql.cj.jdbc.Driver
*/
DriverManager.registerDriver(new Driver());
//2.获取连接
/**
* TODO: 注意
* 面向接口编程
* java.sql 接口 = 实现类
* connection 使用java.sql.Connection接口接收
* url格式
* jdbc:数据库厂商://ip地址:port/数据库名称
* jdbc:mysql://localhost:3306/testdatabase
*/
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/testdatabase",
"root",
"你的密码");
//3.创建小车
Statement statement = connection.createStatement();
//4.发送SQL语句
String sql = "select id,account,password,nickname from t_user ;";
ResultSet resultSet = statement.executeQuery(sql);
//5.结果集解析
//按行读取,没有则为false
while (resultSet.next()){
int id = resultSet.getInt("id");
String account = resultSet.getString("account");
String password = resultSet.getString("password");
String nickname = resultSet.getString("nickname");
System.out.println(id+"::"+account+"::"+password+"::"+nickname);
}
//6.关闭资源 【先开后关】
resultSet.close();
statement.close();
connection.close();
}
}
2.4 基于statement方式的问题
/**
* ClassName: StatementUserLoginPart
* Description:
* 模拟用户登录
* 1.明确jdbc使用流程和详细讲解内部设计api
* 2.发现问题 引出prepared Statement
* TODO:
* 输入账号密码
* 进行数据库信息查询(t_user)
* 反馈登录成功与否
*/
public class StatementUserLoginPart {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.输入账号和密码
Scanner scanner = new Scanner(System.in);
System.out.println("请输入账号: ");
String account = scanner.nextLine();
System.out.println("请输入密码: ");
String password = scanner.nextLine();
scanner.close();
//2.jdbc的查询使用
/**
* 类加载: java文件 -> 编译 -> 【 class字节码文件 --> 类加载 --> jvm虚拟中 --> Class对象】
* 类加载具体步骤: 加载 【class文件转成对象加载到虚拟机中】->
* 连接 【验证(检查类文件) -> 准备 (静态变量赋默认值) -> 解析 (调用静态代码块) 】 ->
* 初始化 -> (赋真实值)
* 以下7种方式会触发类加载:
* 1. new关键字
* 2. 调用静态属性
* 3. 调用静态方法
* 4. 接口 包含1.8 新特性 default关键字
* 5. 反射 【Class.forName() 类名.class】
* 6. 子类调用会触发父类的静态代码块
* 7. 触发类的入口方法main
*/
//注册一次驱动
// 字符串 -> 提取到外部配置文件 -> xx.properties -> oracle -> 配置文件的修改
Class.forName("com.mysql.cj.jdbc.Driver");
/**
* 重写: 为了子类扩展父类的方法!父类也间接的规范了子类方法的参数和返回!
* 重载: 重载一般应用在第三方的工具类上,为了方便用户多种方式传递参数形式!简化形式!
*/
/**
* 三个参数:
* String URL: 连接数据库地址
* String user: 连接数据库用户名
* String password: 连接数据库用户对应的密码
* 数据库URL语法:
* JDBC:
* jdbc:[mysql / oracle]://ip:port/database
* 如:
* jdbc:mysql | jdbc:oracle :// 127.0.0.1 | localhost : 3306 / 数据库名
* jdbc:mysql://localhost:3306/day01
* 当前电脑的省略写法! 注意:本机和端口3306
* jdbc:mysql://localhost:3306/day01 = jdbc:mysql:///day01
*
* 两个参数:
* String URL : 写法还是jdbc的路径写法!
* Properties : 就是一个参数封装容器!至少要包含 user / password key!存储连接账号信息!
*
* 一个参数:
* String URL: URl可以携带目标地址,可以通过?分割,在后面key=value&key=value形式传递参数
* jdbc:mysql:///day01?user=root&password=123456
* 扩展路径参数(了解):
* 8.0.27版本驱动 下面是一些可选属性
* 8.0.25以后自动识别时区 serverTimezone=Asia/Shanghai 不用添加
* 8版本以后默认使用utf-8格式,剩下的都可以省略
* serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true
*
*/
//获取连接 方式1
Connection connection = DriverManager.getConnection("jdbc:mysql:///testdatabase", "root", "123456");
//获取连接 方式2
// Properties info = new Properties();
// info.put("user", "root");
// info.put("password", "3663573");
// DriverManager.getConnection("jdbc:mysql:///testdatabase", info);
//获取连接 方式3
// DriverManager.getConnection("jdbc:mysql:///testdatabase?user=root&password=3663573wxf");
//固定方法固定剂
//创建statement
//statement 可以承载sql语句运送到sql服务器
Statement statement = connection.createStatement();
//执行SQL语句 [动态SQL语句,需要字符串拼接]
String sql = "select * from t_user where account = '"+account+"' and password = '"+password+"' ;";
/**
* SQL分类 :
* DDL(容器创建,修改,删除) DML(数据插入,修改,删除) DQL(查询) DCL(劝降控制) TPL(事务控制语言)
* ResultSet 结果集对象 = executeQuery(DQL语句)
* int 响应行数 = executeUpdate(非DQL语句)
*/
ResultSet resultSet = statement.executeQuery(sql);
//ResultSet == 小海豚 你必须有面向对象的思维:Java是面向对象编程的语言 OOP!
/**
*
* TODO:1.需要理解ResultSet的数据结构和小海豚查询出来的是一样,需要在脑子里构建结果表!
* TODO:2.有一个光标指向的操作数据行,默认指向第一行的上边!我们需要移动光标,指向行,在获取列即可!
* boolean = next()
* false: 没有数据,也不移动了!
* true: 有更多行,并且移动到下一行!
* 推荐:推荐使用if 或者 while循环,嵌套next方法,循环和判断体内获取数据!
* if(next()){获取列的数据!} || while(next()){获取列的数据!}
*
*TODO:3.获取当前行列的数据!
* get类型(int columnIndex | String columnLabel)
* 列名获取 //label 如果没有别名,等于列名, 有别名label就是别名,他就是查询结果的标识!
* 列的角标 //从左到右 从1开始! 数据库全是从1开始!
*/
//进行结果集对象解析
if (resultSet.next()){
//只要向下移动,就是有数据 就是登录成功!
System.out.println("登录成功!");
}else{
System.out.println("登录失败!");
}
//关闭资源
resultSet.close();
statement.close();
connection.close();
}
}
存在问题
密码写为 ' or '1' = '1 在这种情况下 无论是否存在用户 都可以登录
2.5 基于preparedstatement方式的优化(重点)
基于preparedstatement解决上述案例的注入攻击和SQL拼接问题(需要重点掌握)
/**
* ClassName: PSUserLoginPart
* Description:
* 使用预编译的statement完成用户登录
* TODO:
* 防止注入攻击 | 演示PS的使用流程
*/
public class PSUserLoginPart {
public static void main(String[] args) throws Exception {
//1.输入账号和密码
Scanner scanner = new Scanner(System.in);
System.out.println("请输入账号: ");
String account = scanner.nextLine();
System.out.println("请输入密码: ");
String password = scanner.nextLine();
scanner.close();
//2.ps的数据库流程
//2.1注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.2获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql:///testdatabase?user=root&password=123456");
//2.3创建preparedStatement
//TODO 需要传入SQL语句结构
//TODO 要的是SQL语句结构,动态值的部分使用 ? , 占位符!
//TODO ? 不能加 '?' ? 只能替代值,不能替代关键字和容器名
//编写SQL语句结果
String sql = "select * from t_user where account = ? and password = ? ;";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//占位符赋值
//给占位符赋值! 从左到右,从1开始!
/**
* int 占位符的下角标
* object 占位符的值
*/
preparedStatement.setObject(1, account);
preparedStatement.setObject(2, password);
//这哥们内部完成SQL语句拼接!
//执行SQL语句即可
//ResultSet 结果集对象 = executeQuery(DQL语句)
//int 响应行数 = executeUpdate(非DQL语句)
ResultSet resultSet = preparedStatement.executeQuery();
// preparedStatement.executeUpdate();
//进行结果集对象解析
if (resultSet.next()){
//只要向下移动,就是有数据 就是登录成功!
System.out.println("登录成功!");
}else{
System.out.println("登录失败!");
}
//关闭资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
2.6 基于preparedstatement方式的CRUB(重点)
2.6.1 数据库数据插入
@Test
public void PSInsert() throws Exception {
/**
* t_user插入一条数据
* account test
* password test
* nickname 二狗子
*/
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql:///testdatabase?user=root&password=123456");
//3.编写SQL语句结果,动态值部分用?代替
String sql = "insert into t_user (account, password, nickname) values(?,?,?)";
//4.创建prepareStatement 并且传入SQL语句结果
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//5.占位符赋值
preparedStatement.setObject(1, "test");
preparedStatement.setObject(2, "test");
preparedStatement.setObject(3, "二狗子");
//6.发送SQL语句
int rows = preparedStatement.executeUpdate();
//7.输出结果
if (rows > 0) {
System.out.println("数据插入成功!");
} else {
System.out.println("数据插入失败!");
}
//8.释放资源
preparedStatement.close();
connection.close();
}
2.6.2 数据库数据修改
@Test
public void PSUpdate() throws Exception {
/**
* t_user更新指定id的数据为
* account test
* password test
* nickname 三狗子
*/
//1.注册驱动
Class.forName("com.mysql.cj.Driver");
//2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql:///testdatabase", "root", "123456");
//3.编写SQL语句结果,动态值部分用?代替
String sql = "update t_user set nickname = ? where id = ?";
//4.创建prepareStatement 并且传入SQL语句结果
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//5.占位符赋值
preparedStatement.setObject(1, "三狗子");
preparedStatement.setObject(2, 3);
//6.发送SQL语句
int rows = preparedStatement.executeUpdate();
//7.输出结果
if (rows > 0) {
System.out.println("数据更新成功!");
} else {
System.out.println("数据更新失败!");
}
//8.释放资源
preparedStatement.close();
connection.close();
}
2.6.3 数据库数据删除
@Test
public void PSDelete() throws SQLException, ClassNotFoundException {
/**
* t_user删除一条指定id的数据
* account test
* password test
* nickname 段佳妮
*/
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql:///testdatabase", "root", "xxxxxxxx");
//3.编写SQL语句结果,动态值部分用?代
String sql = "Delete from t_user where id = ?";
//4.创建prepareStatement 并且传入SQL语句结果
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//5.占位符赋值
preparedStatement.setObject(1, 3);
//6.发送SQL语句
int rows = preparedStatement.executeUpdate();
//7.输出结果
if (rows > 0) {
System.out.println("数据删除成功!");
} else {
System.out.println("数据删除失败!");
}
//8.释放资源
preparedStatement.close();
connection.close();
}
2.6.4 数据库数据查询(高级版 用List<Map> 存储)
@Test
public void PSSelect() throws ClassNotFoundException, SQLException {
/**
* t_user查询所有数据,并封装到一个List<Map> list集合当中
* map -> 对应一行数据
* map key -> 数据库列名或者别名
* map value -> 数据库列的值
* TODO: 思路分析
* 1.先创建一个List<Map>集合
* 2.遍历resultSet对象的行数据
* 3.将每一行数据存储到一个map对象中!
* 4.将对象存到List<Map>中
* 5.最终返回
*
* TODO:
* 初体验,结果存储!
* 学习获取结果表头信息(列名和数量等信息)
*/
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql:///testdatabase", "root", "xxxxxxx");
//3.编写SQL语句结果,动态值部分用?代替
String sql = "select id, account, password, nickname from t_user";
//4.创建prepareStatement 并且传入SQL语句结果
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//5.占位符赋值
//省略占位符赋值
//6.遍历resultSet对象的行数据
ResultSet resultSet = preparedStatement.executeQuery();
//7.输出结果
List<Map> list = new ArrayList<>();
//利用ResultSet内置的函数获取它列名称和列数据角标
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
while(resultSet.next()){
Map map = new HashMap<>();
//纯手动取值 不智能
// map.put("id", resultSet.getInt("id"));
// map.put("account", resultSet.getString("account"));
// map.put("password", resultSet.getString("password"));
// map.put("nickname", resultSet.getString("nickname"));
//自动取值 即便是换一个数据表,也是相同的操作
for (int i = 1; i <= columnCount; i++) {
//获取对应列下角标的值
Object value = resultSet.getObject(i);
//获取指定下角标列的名称
//getColumnLabel :会获取别名,无别名获取列名 getColumName 只会获取列的名称
String columnName = metaData.getColumnLabel(i);
map.put(columnName, value);
}
list.add(map);
}
System.out.println(list.toString());
//8.释放资源
resultSet.close();
preparedStatement.close();
connection.close();
}
2.7 preparedstatement使用方式的总结(重点)
1. 步骤总结
1.注册驱动 Class.forName("com.mysql.cj.jdbc.Driver");
2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql:///testdatabase?user=x&password=x");
3.编写SQL语句
4.创建preparedstatement并且传入SQL语句结构
PreparedStatement preparedStatement = connection.prepareStatement(sql);
5.占位符赋值
preparedStatement.setObject(<?num>, <?String or Int Object>);
6.发送SQL语句,并且获取结果
ResultSet resultSet = preparedStatement.executeQuery();
7.结果集解析
int rows = preparedStatement.executeUpdate(); // 非查询型语句返回int
ResultSet resultSet = preparedStatement.executeQuery(); // 查询型语句 next() 函数返回boolean变量
8.关闭资源
resultSet.close();
preparedStatement.close();
connection.close();
2. 使用API总结
//1.注册驱动
方案1: 调用静态方法,但是会注册两次
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
方案2: 反射触发
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
Connection connection = DriverManager.getConnection();
3 (String url,String user,String password)
2 (String url,Properties info(user password))
1 (String url?user=账号&password=密码 )
//3.创建statement
//静态
Statement statement = connection.createStatement();
//预编译
PreparedStatement preparedstatement = connection.preparedStatement(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对象 包含的就是列的信息
getColumnCount(); | getCloumnLebal(index)
//7.关闭资源
close();