概述
是一种用于执行SQL语句的Java 的API.可以为多种关系型数据库提
供统一的访问.它是由一组使用Java语言编写的类或接口组成.
1.什么是JDBC以及为什么要使用JDBC
2.JDBC核心API的讲解
3.使用JDBC核心API进行CRUD操作
4.JDBC的工具类的抽取与改进
5.JDBC的SQL注入漏洞分析与解决方案
6.使用JDBC的PreparedStatement预编译对象进行CRUD操作(重点)
7.JDBC的批处理操作
简说:用java语言编写的一组用于访问关系型数据库的接口和类.
作用:使用Java语言连接到数据库
驱动:两个设备之间的通信桥梁.
Java语言要连接数据库必须使用数据库的驱动
- 本质:
SUN公司提供了一组接口,各个数据库生产商提供了这套接口的实现.(这组规范就是JDBC规范.)
1、Driver接口
Java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现
在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver实现。
-
加载和注册JDBC驱动
加载 JDBC 驱动需调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动的类名
DriverManager 类是驱动程序管理器类,负责管理驱动程序
通常不用显式调用 DriverManager 类的 registerDriver() 方法来注册驱动程序类的实例,因为 Driver 接口的驱动程序类都包含了静态代码块,在这个静态代码块中,会调用 DriverManager.registerDriver() 方法来注册自身的一个实例。
-
建立连接
可以调用 DriverManager 类的 getConnection() 方法建立到数据库的连接
JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。-
JDBC URL的标准由三部分组成,各部分间用冒号分隔。
- jdbc:<子协议>:<子名称>
- 协议:JDBC URL中的协议总是jdbc
- 子协议:子协议用于标识一个数据库驱动程序
- 子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息
几种常用的JDBC URL
对于 Oracle 数据库连接,采用如下形式:
jdbc:oracle:thin:@localhost:1521:sid
对于 SQLServer 数据库连接,采用如下形式:
jdbc:microsoft:sqlserver//localhost:1433; DatabaseName=sid
对于 MYSQL 数据库连接,采用如下形式:
jdbc:mysql://localhost:3306/sid
2、Statement
通过调用 Connection 对象 的 createStatement 方法创建该对象
该对象用于执行静态的 SQL 语句,并且返回执行结果
Statement 接口中定义了下列方法用于执行 SQL 语句:
- Statement有两个子类,分别是
ResultSet excuteQuery(String sql)
int excuteUpdate(String sql)
- 访问数据库
数据库连接被用于向数据库服务器发送命令和 SQL 语句,在连接建立后,需要对数据库进行访问,执行 sql 语句
在 java.sql 包中有 3 个接口分别定义了对数据库的调用的不同方式:
-Statement
- PrepatedStatement(预编译SQL语句的方式,防止注入攻击,执行SQL语句)
- CallableStatement(调用数据库过程)
- Praparedstatement和CallableStatement,
3、ResultSet
通过调用 Statement 对象的 excuteQuery() 方法创建该对象
ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商实现
ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行
ResultSet 接口的常用方法:
boolean next()
getString()
4、数据类型转换
小结
java.sql.DriverManager用来装载驱动程序,获取数据库连接。
java.sql.Connection完成对某一指定数据库的联接
java.sql.Statement在一个给定的连接中作为SQL执行声明的容器,他包含了两个重要的子类型。
Java.sql.PreparedSatement 用于执行预编译的sql声明
Java.sql.CallableStatement用于执行数据库中存储过程的调用
java.sql.ResultSet对于给定声明取得结果的途径
使用
- 设置工作空间的编码为utf-8
- SQL脚本:
create database web_test3;
use web_test3;
create table user(
id int primary key auto_increment,
username varchar(20),
password varchar(20),
nickname varchar(20),
age int
);
insert into user values (null,'aaa','123','小丽',34);
insert into user values (null,'bbb','123','大王',32);
insert into user values (null,'ccc','123','小明',28);
insert into user values (null,'ddd','123','大黄',21);
步骤:
1.引入数据库的驱动((注意:复制驱动包过来后,一定要选中, add to Build Path)
2.加载驱动.
3.获得连接.
4.获得语句(Statement )对象
5.执行SQL
6.释放资源.
public class JDBCDemo1 {
@Test
/**
* JDBC的入门
*/
**public** **void** demo1() **throws** Exception{
// 1.加载驱动
Class.*forName*("com.mysql.jdbc.Driver");
// 2.获得连接
Connection conn = DriverManager.*getConnection*("jdbc:mysql://localhost:3306/web_test3", "root", "abc"); //注意:密码改成你的密码
// 3.基本操作:执行SQL
// 3.1获得执行SQL语句的对象
Statement statement = conn.createStatement();
// 3.2编写SQL语句:
String sql = "select * from user";
// 3.3执行SQL:
ResultSet rs = statement.executeQuery(sql);
// 3.4遍历结果集:
**while**(rs.next()){
System.***out***.print(rs.getInt("id")+" ");
System.***out***.print(rs.getString("username")+" ");
System.***out***.print(rs.getString("password")+" ");
System.***out***.print(rs.getString("nickname")+" ");
System.***out***.print(rs.getInt("age"));
System.***out***.println();
}
// 4.释放资源
rs.close();
statement.close();
conn.close();
}
}
DriverManager
- 获取连接的url路径的含义?
指定数据库的网络位置以及帐号密码
- DriverManager对象有什么作用?
DriverManager对象两个作用:1、注册驱动管理驱动2、获取连接;
- 为什么没有使用registerDriver()注册驱动?
原来这样写:
DriverManager.*registerDriver*(**new** com.mysql.jdbc.Driver());
现在这样写:
Class.forName(“com.mysql.jdbc.Driver”);//强制加载class到内存
直接调用registerDriver方法会导致驱动加载2次,因为com.mysql.jdbc.Driver是Java类,其内部有一个静态代码块,在类加载
的时候就会执行,而静态代码块中的内容就是注册驱动,所以只需要Class.forName(“com.mysql.jdbc.Driver”);就可以完成
驱动的注册
Connection对象
作用一、创建执行sql语句的对象Statement或其子类
作用二:开启事务和提交事务
connection.setAutoCommit(false);//手动开启事务
connection.commit()
connection.rollback();
Statement
- 作用一:执行sql语句
- 作用二:进行批处理(就是把几条sql语句添加到Statement对象命令列表中,一次性执行多条sql语句。)
ResultSet的介绍
- ResultSet代表select查询后的结果集
遍历结果集:
Result rs = statement.executeQuery(“select * from user”);
while(rs.next()){
int id = rs.getInt("id");
String username = rs.getString("username");
String password = rs.getString("password");
System.out.println(id+" "+username+" "+password);
}
获得结果集中的数据:
int id = rs.getInt("id");
String username = rs.getString("username");
String password = rs.getString("password");
**如果结果集中只有一条记录:**
if(rs.next()){
...
}
JDBC的资源的释放
- Connection的使用原则:尽量要晚创建,尽量早释放!!!
把释放资源的代码放到finally里面去执行,因为finally无论如何都是会执行的s
try{
...
}catch(){...}
finally {....}
注意关闭顺序是遵从栈顺序的
finally {
if (resultSet!=null) {
try {
resultSet.close();
} catch(SQLException e) {
e.printStackTrace();
}
resultSet=null;
}
if(statement!=null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
statement=null;
}
if(conn!=null) {
try {
conn.close();
} catch(SQLException e) {
e.printStackTrace();
}
conn=null;
}
}
JDBC的CRUD操作之插入
1.加载驱动
2.获得连接
3.获得Statement对象,执行SQL
4.释放资源
/**
*保存操作的代码实现
*/
public void demo1(){
Connection conn = null;
Statement stmt = null;
try{
//注册驱动:
Class.forName("com.mysql.jdbc.Driver");
//获得连接:
conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc");
//执行操作:
//创建执行SQL语句对象:
stmt = conn.createStatement();
//编写SQL语句:
String sql = "insert into user values (null,'eee','123','阿黄',21)";
//执行SQL语句:
int num = stmt.executeUpdate(sql);
if(num > 0){
System.out.println("保存用户成功!!!");
}
}catch(Exception e){
e.printStackTrace();
}finally{
//资源释放:
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
JDBC的CRUD操作之修改
/**
*修改操作的代码实现
*/
public void demo2(){
Connection conn = null;
Statement stmt = null;
try{
//注册驱动:
Class.forName("com.mysql.jdbc.Driver");
//获得连接
conn = DriverManager.*getConnection*("jdbc:mysql:///web_test3", "root", "abc");
//执行操作:
//创建执行SQL语句的对象:
stmt = conn.createStatement();
//编写SQL语句:
String sql = "update user set password='abc',nickname='旺财' where id = 5";
//执行SQL语句:
int num = stmt.executeUpdate(sql);
if(num > 0){
System.out.println("修改用户成功!!!");
}
}catch(Exception e){
e.printStackTrace();
}finally{
//资源释放:
if(stmt != null){
try{
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
JDBC的CRUD操作之删除
/**
*删除操作的代码实现
*/
public void demo3(){
Connection conn = null;
Statement stmt = null;
try{
//注册驱动:
Class.forName("com.mysql.jdbc.Driver");
//获得连接:
conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc");
//创建执行SQL语句对象:
stmt = conn.createStatement();
//编写SQL:
String sql = "delete from user where id = 5";
//执行SQL:
int num = stmt.executeUpdate(sql);
if(num > 0){
System.out.println("删除用户成功!!!");
}
}catch(Exception e){
e.printStackTrace();
}finally{
//资源释放:
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try{
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
JDBC的CRUD操作之查询
/**
*查询多条记录
*/
public void demo4(){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获得连接
conn = DriverManager.*getConnection*("jdbc:mysql:///web_test3", "root", "abc");
//执行操作
//创建执行SQL语句的对象:
stmt = conn.createStatement();
//编写SQL:
String sql = "select * from user";
//执行SQL:
rs = stmt.executeQuery(sql);
//遍历结果集:
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
//资源释放:
if(rs != **null**){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(stmt != null){
try{
stmt.close();
} catch(SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch(SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
JDBC的工具类的抽取
- 关于JDBC可以抽取:加载驱动、获取连接、释放资源这三部分的代码。
/**
* JDBC的工具类
* **@author** itheima
*/
**public** **class** JDBCUtils {
**private** **static** **final** String ***driverClassName***;
**private** **static** **final** String ***url***;
**private** **static** **final** String ***username***;
**private** **static** **final** String ***password***;
**static**{
***driverClassName***="com.mysql.jdbc.Driver";
***url***="jdbc:mysql:///web_test3";
***username***="root";
***password***="abc";
}
/**
*注册驱动的方法
*/
**public** **static** **void** loadDriver(){
**try** {
Class.*forName*(***driverClassName***);
} **catch** (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
*获得连接的方法
*/
**public** **static** Connection getConnection(){
Connection conn = **null**;
**try**{
//将驱动一并注册:
*loadDriver*();
//获得连接
conn = DriverManager.*getConnection*(***url***,***username***, ***password***);
}**catch**(Exception e){
e.printStackTrace();
}
**return** conn;
}
/**
*释放资源的方法
*/
**public** **static** **void** release(Statement stmt,Connection conn){
**if**(stmt != **null**){
**try** {
stmt.close();
} **catch** (SQLException e) {
e.printStackTrace();
}
stmt = **null**;
}
**if**(conn != **null**){
**try** {
conn.close();
} **catch** (SQLException e) {
e.printStackTrace();
}
conn = **null**;
}
}
**public** **static** **void** release(ResultSet rs,Statement stmt,Connection conn){
//资源释放:
**if**(rs != **null**){
**try** {
rs.close();
} **catch** (SQLException e) {
e.printStackTrace();
}
rs = **null**;
}
**if**(stmt != **null**){
**try** {
stmt.close();
} **catch** (SQLException e) {
e.printStackTrace();
}
stmt = **null**;
}
**if**(conn != **null**){
**try** {
conn.close();
} **catch** (SQLException e) {
e.printStackTrace();
}
conn = **null**;
}
}
}
测试工具类
@Test
/**
*查询操作:使用工具类
*/
**public** **void** demo1(){
Connection conn = **null**;
Statement stmt = **null**;
ResultSet rs = **null**;
**try**{
//获得连接:
conn = JDBCUtils.getConnection();
// 创建执行SQL语句的对象:
stmt = conn.createStatement();
//编写SQL:
String sql = "select * from user";
//执行查询:
rs = stmt.executeQuery(sql);
//遍历结果集:
**while**(rs.next()){
System.***out***.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
}
}**catch**(Exception e){
e.printStackTrace();
}**finally**{
//释放资源:
JDBCUtils.release(rs, stmt, conn);
}
}
JDBC的配置信息提取到配置文件
1.定义配置文件
2.获取配置文件中的信息
使用PreparedStatement防止JDBC的SQL注入攻击(篡改SQL语句)
PreparedStatement有一个预编译的过程,这个过程会固定sql语句的格式,对于变量要求是用?来占位,那么传递过来
的数据即使包含了sql关键字也不会当做关键字来识别。
**public** **class** UserDao {
**public** **boolean** login(String username,String password){
Connection conn = **null**;
**PreparedStatement** pstmt = **null**;
ResultSet rs = **null**;
//定义一个变量:
**boolean** flag = **false**;
**try**{
//获得连接:
conn = JDBCUtils.*getConnection*();
//编写SQL语句:
String **sql** = "select * from user where username = ? and password = ?";
//预编译SQL
pstmt = conn.**prepareStatement**(sql);
//设置参数:
pstmt.**setString**(1, username);
pstmt.**setString**(2, password);
//执行SQL语句:
rs = pstmt.executeQuery();**//无需传入SQL**
**if**(rs.next()){
//说明根据用户名和密码可以查询到这条记录
flag = **true**;
}
}**catch**(Exception e){
e.printStackTrace();
}**finally**{
JDBCUtils.*release*(rs, pstmt, conn);
}
**return** flag;
}
/**
*保存操作
*/
**public** **void** demo1(){
Connection conn = **null**;
PreparedStatement pstmt = **null**;
**try**{
//获得连接:
conn = JDBCUtils.*getConnection*();
//编写SQL语句:
String **sql** = "insert into user values (null,?,?,?,?)";
//预编译SQL:
pstmt = conn.**prepareStatement**(**sql**);
//设置参数:
pstmt.**setString**(1, "eee");
pstmt.**setString**(2, "abc");
pstmt.**setString**(3, "旺财");
pstmt.**setInt**(4, 32);
//执行SQL
**int** num = pstmt.**executeUpdate**();//**不传SQL**
**if**(num > 0){
System.***out***.println("保存成功!");
}
}**catch**(Exception e){
e.printStackTrace();
}**finally**{
JDBCUtils.*release*(pstmt, conn);
}
}
/**
*修改操作
*/
**public** **void** demo2(){
Connection conn = **null**;
**PreparedStatement pstmt = null;**
**try**{
//获得连接:
conn = JDBCUtils.*getConnection*();
//编写SQL语句:
String **sql** = "update user set username = ?,password =?,nickname=?,age = ? where id = ?";
//预编译SQL:
pstmt = conn.**prepareStatement(sql);**
//设置参数:
pstmt.setString(1, "abc");//设置第一个?的值
pstmt.setString(2, "1234");
pstmt.setString(3, "旺旺");
pstmt.setInt(4, 23);
pstmt.setInt(5, 4);
//执行SQL:
**int** num = pstmt.executeUpdate();
**if**(num > 0){
System.***out***.println("修改成功!");
}
}**catch**(Exception e){
e.printStackTrace();
}**finally**{
JDBCUtils.*release*(pstmt, conn);
}
}
/**
*删除操作
*/
**public** **void** demo3(){
Connection conn = **null**;
PreparedStatement pstmt = **null**;
**try**{
//获得连接:
conn = JDBCUtils.*getConnection*();
//编写SQL语句:
String **sql** = "delete from user where id = ?";
//预编译SQL
pstmt = conn.**prepareStatement**(**sql**);
//设置参数:
pstmt.**setInt**(1, 4);
//执行SQL:
**int** num = pstmt.**executeUpdate**();
**if**(num > 0){
System.***out***.println("删除成功!");
}
}**catch**(Exception e){
e.printStackTrace();
}**finally**{
JDBCUtils.*release*(pstmt, conn);
}
}
/**
*查询操作
*/
**public** **void** demo4(){
Connection conn = **null**;
**PreparedStatement** pstmt = **null**;
ResultSet rs = **null**;
**try**{
//获得连接:
conn = JDBCUtils.*getConnection*();
//编写SQL:
String **sql** = "select * from user";
//预编译SQL:
pstmt = conn.**prepareStatement**(sql);
//设置参数:
//执行SQL:
rs = pstmt.**executeQuery**();
//遍历结果集:
**while**(rs.next()){
System.***out***.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password")+" "+rs.getString("nickname"));
}
}**catch**(Exception e){
e.printStackTrace();
}**finally**{
JDBCUtils.*release*(rs, pstmt, conn);
}
}
JDBC的批处理操作
批处理:一批SQL一起执行
@Test
/**
*批处理基本操作
*/
**public** **void** demo1(){
Connection conn = **null**;
Statement stmt = **null**;
**try**{
//获得连接:
conn = JDBCUtils.*getConnection*();
//创建执行批处理对象:
stmt = conn.createStatement();
//编写一批SQL语句:
String sql1 = "create database test1";
String sql2 = "use test1";
String sql3 = "create table user(id int primary key auto_increment,name varchar(20))";
String sql4 = "insert into user values (null,'aaa')";
String sql5 = "insert into user values (null,'bbb')";
String sql6 = "insert into user values (null,'ccc')";
String sql7 = "update user set name = 'mmm' where id = 2";
String sql8 = "delete from user where id = 1";
//添加到批处理
stmt.addBatch(sql1);
stmt.addBatch(sql2);
stmt.addBatch(sql3);
stmt.addBatch(sql4);
stmt.addBatch(sql5);
stmt.addBatch(sql6);
stmt.addBatch(sql7);
stmt.addBatch(sql8);
//执行批处理:
stmt.executeBatch();
}**catch**(Exception e){
e.printStackTrace();
}**finally**{
JDBCUtils.*release*(stmt, conn);
}
}
批量插入(使用PreparedStatement)
@Test
/**
*批量插入记录:
*需要在url后面拼接一个参数即可,效率高
?rewriteBatchedStatements=true
*/
**public** **void** demo2(){
//记录开始时间:
**long** begin = System.*currentTimeMillis*();
Connection conn = **null**;
PreparedStatement pstmt = **null**;
**try**{
//获得连接:
conn = JDBCUtils.*getConnection*();
//编写SQL语句:
String sql = "insert into user values (null,?)";
//预编译SQL:
pstmt = conn.**prepareStatement**(sql);
**for**(**int** i=1;i<=10000;i++){
pstmt.**setString**(1, "name"+i);
//添加到批处理
pstmt.**addBatch**();
//注意问题:
//执行批处理
**if**(i % 1000 == 0){
//执行批处理:
pstmt.**executeBatch**();
//清空批处理:
pstmt.**clearBatch**();
}
}
}**catch**(Exception e){
e.printStackTrace();
}**finally**{
JDBCUtils.*release*(pstmt, conn);
}
**long** end = System.*currentTimeMillis*();
System.***out***.println((end-begin));
}