JDBC概述
JDBC,全称Java Database Connectivity
提供一种与平台无关的,用于执行sql语句的标准API,可以方便的实现多种关系型数据库的统一操作,由一组用Java语言编写的类和接口组成。
厂商只要实现JDBC接口就能使用JDBC执行Java操作,可移植性强
JDBC的分类
JDBC分为以下四种
1.sun:JDBC-ODBC桥驱动
- 转换过程中有性能损伤
2.JDBC本地驱动
- 缺点 : 只能应用在特定的数据库上,会丧失程序的可移植性
- 优点: 操作的性能非常高
- 开发的时候使用最为频繁(基本在本地进行开发)
3.JDBC网络驱动:最为灵活的驱动
- JDBC网络驱动先转为DBMS无关的网络协议,通过某个服务器再次转化为DBMS协议
- 要进行两次转换
4.本地协议纯JDBC驱动
- 将JDBC直接转化为DBMS,与网络驱动相反
- Internet访问的一种非常实用的方式
JDBC主要接口和类
主要接口
1.DriverManager 用于管理JDBC驱动程序
2.Connection 用于建立特定数据库的连接,一个连接就是一个会话,建立连接之后,可以执行sql语句,获取检索结果
3.Statement 一个statement对象用于执行静态sql,并且获得语句执行后的结果
4.Preparedstatement 创建一个可以编译的sql语句对象,该对象可以被多次运行,用来提高执行的效率,Statement的子接口
5.Resultset 用于创建表示sql语句结果的结果集,用户可以通过结果集完成对数据库的访问
6.Callablestatement 用于执行sql的存储过程
7.Driver 定义一个书库驱动程序的接口
Preparedstatement是Statement的子接口
主要类
8.Date 该类是标准的Date的一个子集,用于表示sqlDate类型,日期类型 不包含时间
9.Timestamp 标准的Date的一个扩展,用于表示sg1中时间戳,增加了一个关于纳秒时间域
10.Time 该类是标准的Date的一个子集,用于表示sqlTime类型,时间类型 的时分秒
11.DatabaseMetaData再出现数据库异常的时候,报告异常或者警告
12.DataTruncation 跟ReusltSetMetaData一起访问数据库的元信息
异常类
13.SOLException 数据库异常
14.SOLWarming 数据库警告
其余
15.DriverPropertyInfo 驱动属性的所有信息
16.Types 常量
JDBC的操作步骤
1.安装数据库
- 电脑上需要有数据库,下面以mysql为例
2.加载数据库的驱动程序 每个数据库的厂商都有不同的数据库驱动程序 mysql
- 在加载驱动前可以看看自己的数据库匹配的驱动版本,下面以加载8.0.33版本为例
- 打开浏览器 搜索maven repository,点击网站进入
- 点击进入后会先让验证是否是真人,验证通过即可进入,进入后在搜索框内写mysql java进行搜索,以下两个都可进入下载
- 都可选择,这里我选择最新版本
- 进入后点击jar包进行下载,下载成功即可在后续用,无需解压
- 这时驱动就下载完成,如果用的不是mysql也可以照猫画虎,步骤都是大差不差的
3.让Java连接数据库 连接地址:jdbc:mysgl://localhost:3306/数据库名
- 连接时可以是localhost,也可以是默认127.0.0.1
4.通过java进行数据库的操作
Statement
PreparedStatement
Callablestatement
- 将会在API详解中进行进一步详细操作
5.数据库属于资源类,打开之后,一定需要关闭
JDBC的快速入门
写程序的步骤:
0.创建工程
新建一个lib包,复制上面在maven下载的驱动jar包,粘贴到lib包内,右击jar包,选择Add as Library
- Level要选择Module Library
此时创建工程就好了
1.注册驱动
- mysql5.8版本以前:
Class.forName("com.mysql.jdbc.Driver");
- mysql5.8版本以后:
Class.forName("com.mysql.cj.jdbc.Driver");
因为mysql5.8版本以后jdbc就在cj包下了,要明确注册驱动的路径,否则找不到就注册失败了
2.获取连接
Connection conn = DriverManager.getConnection(url, username, password);
括号内三个参数不能写死,要为后续的更改提供便利,在获取连接代码上面写
String url="jdbc:mysql://127.0.0.1:3306/my_db";//自己要连接进行下一步操作的数据库
String username="root";//自己电脑mysql的用户名
String password="1234";//用户名对应的密码
Connection conn = DriverManager.getConnection(url, username, password);
3.定义SQL语句
String sql="UPDATE…………";//自行定义操作
4.获取执行sql的对象
Statement stmt=conn.createStatement();
5.执行sql
int count = stmt.executeUpdate(sql);//受影响的行数
6.处理结果
- 不同的操作要进行不同的处理
7.释放资源
- 关闭执行sql的对象以及连接对象
简易案例
package com.svt.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
/**
* JDBC的快速入门
*/
public class JDBCDemo {
//public static final String DBDRIVER = "com.mysql.cj.jdbc.Driver";
public static void main(String[] args) throws Exception {
//1.注册驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//5.16之后省略注册
//2.获取连接
String url="jdbc:mysql://127.0.0.1:3306/my_db";
String username="root";//自己电脑mysql的用户名
String password="1234";//用户名对应的密码
Connection conn = DriverManager.getConnection(url, username, password);
//3.定义SQL语句
String sql="UPDATE account SET money = 2000 WHERE id=1";
//4.获取执行sql的对象 Statement
Statement stmt=conn.createStatement();
//5.执行sql
int count = stmt.executeUpdate(sql);//受影响的行数
//6.处理结果
System.out.println(count);
//7.释放资源
stmt.close();
conn.close();
}
}
JDBC API 详解
DriverManager
-
作用
1、注册驱动
2、获取数据库连接 -
举例
package com.svt.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class JDBCDemo2_DriverManager {
//public static final String DBDRIVER = "com.mysql.cj.jdbc.Driver";
public static void main(String[] args) throws Exception {
//1.注册驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接 如果连接的是本机mysql并且端口是默认的3306 可以简化书写
//String url="jdbc:mysql://127.0.0.1:3306/my_db";
String url="jdbc:mysql:///my_db";//有安全隐患的可以在数据库名称后直接加上?useSSL=false 不能有空格
String username="root";//自己电脑mysql的用户名
String password="1234";//用户名对应的密码
Connection conn = DriverManager.getConnection(url, username, password);
//3.定义SQL语句
String sql="UPDATE account SET money = 2000 WHERE id=1";
//4.获取执行sql的对象 Statement
Statement stmt=conn.createStatement();
//5.执行sql
int count = stmt.executeUpdate(sql);//受影响的行数
//6.处理结果
System.out.println(count);
//7.释放资源
stmt.close();
conn.close();
}
}
Connection
- 作用
- 1、获取执行sql的对象
- 普通执行SQL对象
StatementcreateStatement() - 预编译SQL的执行SQL对象:防止SQL注入
PreparedStatementprepareStatement (sl) - 执行存储过程的对象
CallableStatement prepareCall (sql)
- 普通执行SQL对象
- 2、管理事务
- MySQL 事务管理
开启事务: BEGIN;/START TRANSACTION;
提交事务: COMMIT;
回滚事务: ROLLBACK;
MySQL默认自动提交事务 - JDBC事务管理:Connection接口中定义了3个对应的方法
开启事务: setAutoCommit(boolean autoCommit): true为自动提交事务; false为手动提交事务,即为开启事务
提交事务: commit()
回滚事务: rollback()
- MySQL 事务管理
- 1、获取执行sql的对象
- 举例
package com.svt.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class JDBCDemo3_Connection {
//public static final String DBDRIVER = "com.mysql.cj.jdbc.Driver";
public static void main(String[] args) throws Exception {
//1.注册驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接 如果连接的是本机mysql并且端口是默认的3306 可以简化书写
//String url="jdbc:mysql://127.0.0.1:3306/my_db";
String url="jdbc:mysql:///my_db";//有安全隐患的可以在数据库名称后直接加上?useSSL=false 不能有空格
String username="root";
String password="1234";
Connection conn = DriverManager.getConnection(url, username, password);
//3.定义SQL语句
String sql1="UPDATE account SET money = 3000 WHERE id=1";
String sql2="UPDATE account SET money = 3000 WHERE id=2";
//4.获取执行sql的对象 Statement
Statement stmt=conn.createStatement();
try {
//开启事务
conn.setAutoCommit(false);
//5.执行sql
int count1 = stmt.executeUpdate(sql1);//受影响的行数
//6.处理结果
System.out.println(count1);
//int i = 3/0;
//5.执行sql
int count2 = stmt.executeUpdate(sql2);//受影响的行数
//6.处理结果
System.out.println(count2);
//提交事务
conn.commit();
} catch (Exception e) {
//回滚事务
conn.rollback();
e.printStackTrace();
}
//7.释放资源
stmt.close();
conn.close();
}
}
Statement
-
作用
执行SQL语句
int executeUpdate(sql): 执行DML、DDL语句
返回值: (1)DML语句影响的行数(2)DDL语句执行后,执行成功也可能返回 0
ResultSet executeQuery(sql): 执行DQL语句
返回值: ResultSet 结果集对象 -
举例-使用Test单元测试来分块测试,需要导包,可以写@Test,然后alt+回车自动导入
package com.svt.jdbc;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCDemo4_Statement {
/**
* 执行DML语句
* @throws Exception
*/
@Test//要导包
public void testDML() throws Exception {
//1.注册驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接 如果连接的是本机mysql并且端口是默认的3306 可以简化书写
//String url="jdbc:mysql://127.0.0.1:3306/my_db";
String url="jdbc:mysql:///my_db";//有安全隐患的可以在数据库名称后直接加上?useSSL=false 不能有空格
String username="root";
String password="1234";
Connection conn = DriverManager.getConnection(url, username, password);
//3.定义SQL语句
String sql="UPDATE account SET money = 4000 WHERE id=2";
//4.获取执行sql的对象 Statement
Statement stmt=conn.createStatement();
//5.执行sql
int count = stmt.executeUpdate(sql);//执行完DML语句后 受影响的行数
//6.处理结果
//System.out.println(count);
if (count>0){
System.out.println("修改成功!");
}else {
System.out.println("修改失败!");
}
//7.释放资源
stmt.close();
conn.close();
}
/**
* 执行DDL语句
* @throws Exception
*/
@Test//要导包
public void testDDL() throws Exception {
//1.注册驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接 如果连接的是本机mysql并且端口是默认的3306 可以简化书写
//String url="jdbc:mysql://127.0.0.1:3306/my_db";
String url="jdbc:mysql:///my_db";//有安全隐患的可以在数据库名称后直接加上?useSSL=false 不能有空格
String username="root";
String password="1234";
Connection conn = DriverManager.getConnection(url, username, password);
//3.定义SQL语句
String sql="drop database db1";
//4.获取执行sql的对象 Statement
Statement stmt=conn.createStatement();
//5.执行sql
int count = stmt.executeUpdate(sql);//执行完DDL语句后 可能返回0
//6.处理结果
//System.out.println(count);
/*if (count>0){
System.out.println("修改成功!");
}else {
System.out.println("修改失败!");
}*/
System.out.println(count);
//7.释放资源
stmt.close();
conn.close();
}
}
ResultSet
- ResultSet(结果集对象)作用:
- 封装了DQL查询语句的结果
ResultSet stmt.executeQuery(sql): 执行DQL 语句,返回 ResultSet 对象 - 获取查询结果
boolean next(): (1)将光标从当前位置向前移动一行 (2)判断当前行是否为有效行
返回值:
true: 有效行,当前行有数据
false: 无效行,当前行没有数据 - getXxx(参数): 获取数据XXX
xxx: 数据类型;如: int getlnt(参数); String getString(参数)- 参数:
int: 列的编号,从1开始
String:列的名称
- 参数:
- 封装了DQL查询语句的结果
- 举例
package com.svt.jdbc;
import com.svt.pojo.Account;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class JDBCDemo5_ResultSet {
@Test//要导包
public void testResultSet() throws Exception {
//1.注册驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接 如果连接的是本机mysql并且端口是默认的3306 可以简化书写
//String url="jdbc:mysql://127.0.0.1:3306/my_db";
String url="jdbc:mysql:///my_db";//有安全隐患的可以在数据库名称后直接加上?useSSL=false 不能有空格
String username="root";
String password="1234";
Connection conn = DriverManager.getConnection(url, username, password);
//3.定义SQL语句
String sql="select * from account";
//4.获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
//5.执行sql
ResultSet rs = stmt.executeQuery(sql);
//6.处理结果,遍历rs中的所有数据
//6.1光标向下移动一行,并且判断当前行是否有数据
/*while (rs.next()){
//6.2获取数据 getXxx()
int id=rs.getInt(1);
String name = rs.getString(2);
double money = rs.getDouble(3);
System.out.println(id);
System.out.println(name);
System.out.println(money);
System.out.println("--------------------");
}*/
while (rs.next()){
//6.2获取数据 getXxx()
int id=rs.getInt("id");
String name = rs.getString("name");
double money = rs.getDouble("money");
System.out.println(id);
System.out.println(name);
System.out.println(money);
System.out.println("--------------------");
}
//7.释放资源
rs.close();
stmt.close();
conn.close();
}
}
案例
- 需求:查询account账户表数据,封装为Account对象中,并且存储到ArrayList集合中
- 步骤
- 1.定义实体类Account
- 2.查询数据,封装到Account对象中
- 3.将Account对象存储到ArrayList集合中
@Test//要导包
public void testResultSet2() throws Exception {
//1.注册驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接 如果连接的是本机mysql并且端口是默认的3306 可以简化书写
//String url="jdbc:mysql://127.0.0.1:3306/my_db";
String url="jdbc:mysql:///my_db";//有安全隐患的可以在数据库名称后直接加上?useSSL=false 不能有空格
String username="root";
String password="1234";
Connection conn = DriverManager.getConnection(url, username, password);
//3.定义SQL语句
String sql="select * from account";
//4.获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
//5.执行sql
ResultSet rs = stmt.executeQuery(sql);
//创建集合
List<Account> list = new ArrayList<>();
//6.处理结果,遍历rs中的所有数据
//6.1光标向下移动一行,并且判断当前行是否有数据
while (rs.next()){
Account account = new Account();
//6.2获取数据 getXxx()
int id=rs.getInt("id");
String name = rs.getString("name");
double money = rs.getDouble("money");
//赋值
account.setId(id);
account.setName(name);
account.setMoney(money);
//存入集合
list.add(account);
}
//查看结果 打印数据
System.out.println(list);
//7.释放资源
rs.close();
stmt.close();
conn.close();
}
应用-用户登录(SQL注入)
- SQL注入:通过操作输入来修改事先好的SQL语句,用以达到执行代码对服务器进行攻击的方法
- 操作输入:写好的特定的破解码
String pwd=“’ or’1’='1”;
String sql=“SELECT * FROM tb_user where username='”+name+“‘and password=’”+pwd+“'”;
这样写会导致输出结果为 select * from tb_user where username = '...' and password = '' or '1'='1';
username = '...'
返回true,password = ''
->空字符串,返回false,而'1'='1'
永远返回true,此时where就面临选择false or true,条件就为true,所以无论你输入什么密码都能进入数据库进行修改,这就是SQL注入
package com.svt.jdbc;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* 用户登录
*/
public class JDBCDemo6_UserLogin {
@Test//要导包
public void testLogin() throws Exception {
//1.注册驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接 如果连接的是本机mysql并且端口是默认的3306 可以简化书写
//String url="jdbc:mysql://127.0.0.1:3306/my_db";
String url="jdbc:mysql:///my_db";//有安全隐患的可以在数据库名称后直接加上?useSSL=false 不能有空格
String username="root";
String password="1234";
Connection conn = DriverManager.getConnection(url, username, password);
//接受用户输入的用户名和密码
String name="zhangsan";
String pwd="123";
//3.定义SQL语句
String sql="SELECT * FROM tb_user where username='"+name+"'and password='"+pwd+"'";
//4.获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
//5.执行sql
ResultSet rs = stmt.executeQuery(sql);
//判断登录是否成功
if (rs.next()){
System.out.println("登陆成功");
}else {
System.out.println("登陆失败");
}
//7.释放资源
rs.close();
stmt.close();
conn.close();
}
/**
* 演示SQL注入
* @throws Exception
*/
@Test//要导包
public void testLogin_Inject() throws Exception {//Inject-注入
//1.注册驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接 如果连接的是本机mysql并且端口是默认的3306 可以简化书写
//String url="jdbc:mysql://127.0.0.1:3306/my_db";
String url="jdbc:mysql:///my_db";//有安全隐患的可以在数据库名称后直接加上?useSSL=false 不能有空格
String username="root";
String password="1234";
Connection conn = DriverManager.getConnection(url, username, password);
//接受用户输入的用户名和密码
String name="sdgfghukgm";
String pwd="' or'1'='1";
//3.定义SQL语句
String sql="SELECT * FROM tb_user where username='"+name+"'and password='"+pwd+"'";
System.out.println(sql);
//4.获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
//5.执行sql
ResultSet rs = stmt.executeQuery(sql);
//判断登录是否成功
if (rs.next()){
System.out.println("登陆成功");
}else {
System.out.println("登陆失败");
}
//7.释放资源
rs.close();
stmt.close();
conn.close();
}
}
如何不让SQL注入呢?下面来看PreparedStatement
PreparedStatement
PreparedStatement作用
- 预编译SQL并执行SQL语句
获取 PreparedStatement 对象
//SQL语句中的参数值,使用? 占位符替代
String sql = "select * from user where username = ? and password = ?"
//通过Connection对象获取,并传入对应的sgl语句
PreparedStatement pstmt = conn.prepareStatement(sal);
- 设置参数值
PreparedStatement对象: setXxx(参数1,参数2): 给?赋值
Xxx: 数据类型;如 setInt(参数1,参数2)
参数:
参数1: ?的位置编号,从1 开始
参数2: ?的值 - 执行SQL
executeUpdate();/executeQuery();: 不需要再传递sql - 这样就能防注入,再用上面特定的破解码时,敏感字符’会被转义为’,与之前的拼接就不一样了
PreparedStatement 好处
1. 预编译SQL,性能更高
2. 防止SQL注入: 将敏感字符进行转义
PreparedStatement 预编译功能开启: useServerPrepStmts=true
配置MySQL执行日志(重启mysql服务后生效)
log-output=FILE
general-log=1
general_log_file="D: mysql.log"
slow-query-log=1
slow_query_log_file="D:mysql_slow.log"
long_query_time=2
PreparedStatement 原理
1.在获取PreparedStatement对象时,将sql语何发送给mysql服务器进行检查,编译(这些步骤很耗时)
2.执行时就不用再进行这些步骤了,速度更快
3.如果sql模板一样,则只需要进行一次检查、编译
举例
package com.svt.jdbc;
import org.junit.Test;
import java.sql.*;
public class JDBCDemo7_PreparedStatement {
@Test//要导包
public void testPreparedStatement() throws Exception {
//1.注册驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接 如果连接的是本机mysql并且端口是默认的3306 可以简化书写
//String url="jdbc:mysql://127.0.0.1:3306/my_db";
String url="jdbc:mysql:///my_db";//有安全隐患的可以在数据库名称后直接加上?useSSL=false 不能有空格
String username="root";
String password="1234";
Connection conn = DriverManager.getConnection(url, username, password);
//接受用户输入的用户名和密码
String name="zhangsan";
String pwd="' or'1'='1";
//3.定义SQL语句
String sql="SELECT * FROM tb_user WHERE username= ? AND password= ? ";
//获取pstmt对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//设置?的值
pstmt.setString(1,name);
pstmt.setString(2,pwd);
//执行sql
ResultSet rs = pstmt.executeQuery();
//判断登录是否成功
if (rs.next()){
System.out.println("登陆成功");
}else {
System.out.println("登陆失败");
}
//7.释放资源
rs.close();
pstmt.close();
conn.close();
}
@Test//要导包
public void testPreparedStatement2() throws Exception {
//1.注册驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接 如果连接的是本机mysql并且端口是默认的3306 可以简化书写
//String url="jdbc:mysql://127.0.0.1:3306/my_db";
//useServerPrepStmts=true 参数开启预编译功能
String url="jdbc:mysql:///my_db?useServerPrepStmts=true";//有安全隐患的可以在数据库名称后直接加上?useSSL=false 不能有空格
String username="root";
String password="1234";
Connection conn = DriverManager.getConnection(url, username, password);
//接受用户输入的用户名和密码
String name="zhangsan";
String pwd=" ";
//3.定义SQL语句
String sql="SELECT * FROM tb_user WHERE username= ? AND password= ? ";
//获取pstmt对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//设置?的值
pstmt.setString(1,name);
pstmt.setString(2,pwd);
//执行sql
ResultSet rs = pstmt.executeQuery();
//判断登录是否成功
if (rs.next()){
System.out.println("登陆成功");
}else {
System.out.println("登陆失败");
}
//7.释放资源
rs.close();
pstmt.close();
conn.close();
}
/**
* 演示SQL注入
* @throws Exception
*/
@Test//要导包
public void testLogin_Inject() throws Exception {//Inject-注入
//1.注册驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接 如果连接的是本机mysql并且端口是默认的3306 可以简化书写
//String url="jdbc:mysql://127.0.0.1:3306/my_db";
String url="jdbc:mysql:///my_db";//有安全隐患的可以在数据库名称后直接加上?useSSL=false 不能有空格
String username="root";
String password="1234";
Connection conn = DriverManager.getConnection(url, username, password);
//接受用户输入的用户名和密码
String name="sdgfghukgm";
String pwd="' or'1'='1";
//3.定义SQL语句
String sql="SELECT * FROM tb_user where username='"+name+"'and password='"+pwd+"'";
System.out.println(sql);
//4.获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
//5.执行sql
ResultSet rs = stmt.executeQuery(sql);
//判断登录是否成功
if (rs.next()){
System.out.println("登陆成功");
}else {
System.out.println("登陆失败");
}
//7.释放资源
rs.close();
stmt.close();
conn.close();
}
}