JDBC
java17+MySQL8
jdbc是数据库框架的底层原理:MyBatis(半自动),Hibernate(全自动),Spring Data(注解方式进行操作)
java database connectivity:
是java连接数据库技术的统称。
1.jdbc概念理解
1.使用jdbc提供的方法,可以发送字符串类型的sql语句到数据库管理软件(MySQL,Oracle等),并且获得语句的执行结果,进而实现数据库数据的crud。
2.java只提供jdbc的规范(接口)在java.aql和javax.sql包下。具体实现由第三方数据库厂商完成具体的实现驱动代码(jar),实现代码可能不同,但是方法都相同。
jdbc本身就是多态的体现,是面向接口编程的。
3.我们要干的事情是用java的接口去接jdbc的具体实现。
(jar包:java程序达成的一种压缩包,你可以将这些jar包导入你的项目中,然后你可以使用这个java程序中的类和方法以及属性)
2.jdbc核心类和接口
- DriverManager:(作用)
- 将第三方jar包注册到程序中
- 可以根据数据库连接信息获取connection
- Connection:(作用)
- 和数据库建立的连接,在连接对象上,可以多次执行数据库的crud动作
- 可以获取statement和preparedStatement以及callableStatement对象
- Statement|PreparedStatement|CallableStatement:(作用)
- 具体发送SQL语句到数据库管理软件的对象
- 不同发送方式稍有不同!preparedStatement是重点
- Result:
- OOP的产物(抽象成数据库的查询结果表)
- 存储DQL查询结果的对象(查询语句返回结果,其他语句返回int类型)
- 需要我们进行解析,获取具体的数据库数据
不同Statement的不同:
- Statement:执行静态SQL语句(没有动态值语句),没有条件值(或条件只涉及常数)->整个语句可以写成固定的字符串;
- PreparedStatement:有动态值(预编译SQL路线);
- 执行标准存储过程
(驱动版本选用8±>8.0.25+可以省时区设置;对应java jdbc规范4.2+)
3.jdbc核心API
3.1 导入jar包方式
- 项目先创建lib文件夹
- 导入驱动依赖jar包
- jar包右键,添加为项目依赖Add as Library…
3.2 jdbc基本使用步骤分析(6步)
- 注册驱动:把依赖的jar包进行安装
- 建立连接connection;
- 创建发送sql语句的对象statement;
- statement对象发送SQL语句到数据库,并且获取返回结果resultSet;
- 解析结果集;
- 释放资源:砸桥connection,砸车statement,砸箱子resultSet。
准备工作:
CREATE DATABASE jdbcTestDb;
USE jdbcTestDB;
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 '昵称');
DESC t_user;
INSERT INTO t_user(account,`password`,nickname)
VALUES('root','123456','经理'),('admin','66666','管理员');
SELECT * FROM t_user;
java实现基于statement演示查询:
package com.jdbcLearning.statement;
import com.mysql.cj.jdbc.Driver;
import java.sql.*;
/**
* @author 马聪
* @version 1.0
* introduction:使用statement查询t_user表下的数据
*/
public class StatementQuery {
/**
* TODO:
* DriverManager
* Connection
* Statement
* ResultSet
* @param args
*/
public static void main(String[] args) throws SQLException {
// 1. 注册驱动:把依赖的jar包进行安装
/**
* TODO:
* 注册驱动
* 依赖:驱动版本 8+ com.mysql.cj.jdbc.Driver
* 驱动版本 5+ com.mysql.jdbc.Driver
*
*/
DriverManager.registerDriver(new Driver());
// 2. 建立连接connection;
/**
* TODO:
* 连接要素:
* 数据库ip 127.0.0.1
* db端口号 默认3306
* 账号 root
* 密码
* 连接数据库名称: jdbcTestDb
*/
/**
* para1:url
* jdbc:数据库厂商名://ip:port/数据库名
* jdbc:mysql://127.0.0.1:3306/jdbcTestDb
*
*/
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jdbcTestDb", "root", "gaoceng3905");
// 3. 创建发送sql语句的对象statement;
Statement statement = connection.createStatement();
// 4. statement对象发送SQL语句到数据库,并且获取返回结果resultSet;
String sql = "select * from t_user";
ResultSet resultSet = statement.executeQuery(sql);
// 5. 解析结果集;
//先看看有没有
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. 释放资源:砸桥connection,砸车statement,砸箱子resultSet。
resultSet.close();
statement.close();
connection.close();
}
}
3.3jdbc使用步骤详解
需求,在3.1的数据库基础上模拟登陆
目的:明确jdbc使用流程和详细讲解内部设计;发现问题,引出PreparedStatement
1.键盘输入时间,收集账号和密码信息
2.注册驱动
3.获取连接
4.创建Statement
5.发送SQL语句,获得查询结果
6.解析查询结果
7.关闭资源
step1:Driver本身静态代码块有调用DriverManager的registerDriver方法所以避免注册两次(所以不采用3.1中的DriverManager.registerDriver(new Driver());
),只用Driver加载一下类即可(加载类就会调用)->回顾触发类加载的多种情况:
- 调用静态属性、方法;
- 子类实例化
- 本类实例化
- 反射
- 接口1.8的default默认实现
- 程序入口main
如果直接new Driver有些不优雅,硬编码,改其他dbms的jdbc会不适用
不如反射Class.forName("com.mysql.cj.jdbc.Driver");
后续还可以:字符串->提取到外部配置文件->可以不改动代码的情况下,完成数据库驱动的切换!
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
package com.jdbcLearning.statement;
import com.mysql.cj.jdbc.Driver;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;
/**
* @author 马聪
* @version 1.0
* introduction:
* jdbcLearning3.2
* TODO:
* 输入账号以及密码
* 进行数据库查询t_user
* 反馈登录成功还是失败
* (引出PreparedStatement)
* TODO:
* 1.键盘输入时间,收集账号和密码信息
* 2.注册驱动
* 3.获取连接
* 4.创建Statement
* 5.发送SQL语句,获得查询结果
* 6.解析查询结果
* 7.关闭资源
*/
public class StatementUserLoginPart {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
//1.键盘输入时间,收集账号和密码信息
Scanner scanner = new Scanner(System.in);
System.out.println("请输入账号:");
String acct = scanner.nextLine();
System.out.println("请输入密码:");
String pswd = scanner.nextLine();
//2.注册驱动。会注册两次,方法本身会注册一次,Driver静态代码块本身也会注册一次
//DriverManager.registerDriver(new Driver());
//只触发static
new Driver();//硬编码,不优雅
//字符串->提取到外部配置文件->可以不改动代码的情况下,完成数据库驱动的切换!
Class.forName("com.mysql.cj.jdbc.Driver");
//3.获取连接
/*
* getConnection(1,2,3)方法是一个重载方法
* 允许开发者,用不同形式传入数据库的核心参数
* 核心属性:
* 1.数据库所在主机的ip或主机名:localhost|127.0.0.1
* 2.数据库软件所在主机的端口号:3306
* 3.连接的具体库:jdbcTestDb
* 4.连接的账号:root
* 5.连接的密码:
* 6.可选的信息 没有
* 三个参数:
* String url 其他 jdbc:mysql://127.0.0.1:3306/jdbcTestDb?key=value&key=value
* 如果dbms安装到本机,可以省略为jdbc:mysql:///jdbcTestDb,省略了本机地址和默认3306端口号
* String user账号
* String pswd密码
* 注意:此处key=value&key=value并不需要写
*
* 两个参数:
* String url 同三个参数
* Properties info账号密码 key=value成对字符串 user:xxx;password:xxx
*
* 一个参数
* String url 将账号密码放在可选信息?key=value&key=value处
*
* url=账号&password=密码
* 8.0.25+版本驱动可以不填时区作为可选信息;
* 8+版本可以不用写useUnicode=true&characterEncoding=utf8&useSSL=true
* serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true
*/
Properties info = new Properties();
info.put("user","root");
info.put("password","gaoceng3905");
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jdbcTestDb", info);
//Connection connection1 = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jdbcTestDb?user=root&password=gaoceng3905");
//4.创建Statement
Statement statement = connection.createStatement();//小汽车
//5.发送SQL语句,获得查询结果
String sql = "SELECT * FROM t_user WHERE account = '"+acct+"' AND password = '"+pswd+"';";
/*
* sql分类:DDL,DML(增删改),DQL,DCL,TPL
* 参数sql:非DQL
* 如果为DML->返回修改行数
* 如果非DML->返回0
* int row = statement.executeUpdate(sql);
*
*/
ResultSet resultSet = statement.executeQuery(sql);
//6.解析查询结果
/*
*取数据:逐行cursor开始指向第一行之前
* next方法向后移动cursor,有数据,返回true,反之false
*
* 获取列数据
* resultSet.get类型(String columnLabel | int columnIndex)
* columnLabel:列名或者列的别名
* int columnIndex:从左到右从1开始
*/
// while(resultSet.next()){
// int id = resultSet.getInt(1);
// String account = resultSet.getString("account");
// String password = resultSet.getString(3);
// String nickname = resultSet.getString(4);
// System.out.println(id+"--"+account+"--"+password+"--"+nickname);
// }
if(resultSet.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
//7.关闭资源
resultSet.close();
statement.close();
connection.close();
}
}
3.4statement的问题以及preparedStatement基本使用
statement的问题:
会在字符串拼接的时候有SQL注入攻击的危险,使得结果出现问题。比如输入' or '1'='1
;字符串拼接比较麻烦;只能拼接字符串类型,其他类型无法处理(不懂为啥sql语句会有其他类型)
statement只适合静态查询语句,不适合动态查询
原本:1.创建Statement;2.拼接sql;3.发送sql
preparedStatement
1.先编写sql语句结果,不包含动态值部分的语句,动态值部分使用占位符?代替 注意?只能代替动态值
2.创建PreparedStatement,并传入动态值
3.动态值 占位符 赋值?单独赋值即可
4.发送sql语句即可,并返回结果
获取结果集时,不需要传sql语句,因为已经知道语句以及动态值,只需要点击发送即可。
package com.jdbcLearning.preparedStatement.api;
import com.mysql.cj.jdbc.Driver;
import java.sql.*;
import java.util.Scanner;
/**
* @author 马聪
* @version 1.0
* introduction:
* 使用预编译的Statement完成用户登录
*
* TODO:
* 防止注入攻击
* 演示ps的使用流程
*/
public class PSUserLogin {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.键盘输入时间,收集账号和密码信息
Scanner scanner = new Scanner(System.in);
System.out.println("请输入账号:");
String acct = scanner.nextLine();
System.out.println("请输入密码:");
String pswd = scanner.nextLine();
//2.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//3.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql:///jdbcTestDb?user=root&password=gaoceng3905");
//4.创建Statement:
//原本:1.创建Statement;2.拼接sql;3.发送sql
//preparedStatement
//(1).先编写sql语句结果,不包含动态值部分的语句,动态值部分使用占位符?代替 注意?只能代替动态值
//(2).创建PreparedStatement
//(3).动态值 占位符 赋值?单独赋值即可
//(4).发送sql语句即可,并返回结果
String sql = "select * from t_user where account = ? and password = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//参数1:index占位符的位置,从左向右数从1开始
//参数2:占位符的值,Object类型可以设置任何类型的值
preparedStatement.setObject(1,acct);
preparedStatement.setObject(2,pswd);
//5.获取结果集
//不需要传sql语句,因为已经知道语句以及动态值,只需要点击发送即可
ResultSet resultSet = preparedStatement.executeQuery();
//6.结果集解析
if(resultSet.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
//关闭资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
3.5查询进阶(转List<Map>
)
查询自动化进阶->getMetaData(获取列相关信息的方法得到metaData对象)
List<Map> list = new ArrayList<>();
while(resultSet.next()){
Map<String,Object> map = new HashMap<>();
//纯手动取值
// map.put("id",resultSet.getObject("id"));
// map.put("account",resultSet.getObject("account"));
// map.put("password",resultSet.getObject("password"));
// map.put("nickname",resultSet.getObject("nickname"));
//自动遍历列
//TODO:metadata装的是当前的列信息
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
for (int i = 1; i < columnCount; i++) {
//name是实际名,label是别名,美别名才是实际名
map.put(metaData.getColumnLabel(i),resultSet.getObject(i));
}
list.add(map);
}
insert示例
@Test
public void testInsert() throws ClassNotFoundException, SQLException {
/*
*t_user插入一条数据
* account test
* password test
* nickname 二狗子
*/
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.connection
Connection connection = DriverManager.getConnection("jdbc:mysql:///jdbcTestDb","root","gaoceng3905");
//3.ps(编写sql,动态值赋值,发送)
String sql = "insert into t_user(account,password,nickname) values(?,?,?);";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setObject(1,"test");
preparedStatement.setObject(2,"test");
preparedStatement.setObject(3,"二狗子");
//4.resultset
int rows = preparedStatement.executeUpdate();
//5.insert不用解析,输出结果
if(rows >0){
System.out.println("插入成功");
}else{
System.out.println("插入失败");
}
//6.关闭资源
preparedStatement.close();
connection.close();
}
insert示例(delete和update实现同理)
@Test
public void testInsert() throws ClassNotFoundException, SQLException {
/*
*t_user插入一条数据
* account test
* password test
* nickname 二狗子
*/
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.connection
Connection connection = DriverManager.getConnection("jdbc:mysql:///jdbcTestDb","root","gaoceng3905");
//3.ps(编写sql,动态值赋值,发送)
String sql = "insert into t_user(account,password,nickname) values(?,?,?);";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setObject(1,"test");
preparedStatement.setObject(2,"test");
preparedStatement.setObject(3,"二狗子");
//4.resultset
int rows = preparedStatement.executeUpdate();
//5.insert不用解析,输出结果
if(rows >0){
System.out.println("插入成功");
}else{
System.out.println("插入失败");
}
//6.关闭资源
preparedStatement.close();
connection.close();
}