本文内容
1.什么是JDBC以及为什么要使用JDBC
2.JDBC核心API的讲解
3.使用JDBC核心API进行CRUD操作
4.JDBC的工具类的抽取与改进
5.JDBC的SQL注入漏洞分析与解决方案
6.使用JDBC的PreparedStatement预编译对象进行CRUD操作(重点)
7.JDBC的批处理操作
使用JDBC的预编译对象进行CRUD操作(重点)
[if !supportLists]1.1 [endif]1.JDBC的概述
JDBC:(Java DataBase Connectivity Java数据库连接).
*是一种用于执行SQL语句的Java 的API.可以为多种关系型数据库提供统一的访问.它是由一组使用Java语言编写的类或接口组成.
简说:用java语言编写的一组用于访问关系型数据库的接口和类.
作用:使用Java语言连接到数据库
驱动:两个设备之间的通信桥梁.
Java语言要连接数据库必须使用数据库的驱动
本质:
SUN公司提供了一组接口,各个数据库生产商提供了这套接口的实现.(这组规范就是JDBC规范.)
[if !supportLists]1.2 [endif]02_JDBC的入门(写)
先解释下,再写
设置工作空间的编码为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);
引入数据库的驱动.
注意:复制驱动包过来后,一定要选中, add to Build Path
1.加载驱动.
2.获得连接.
获得语句(Statement )对象
3.执行SQL
4.释放资源.
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();
}
}
[if !supportLists]1.3 [endif]03_DriverManager的介绍
问题:
DriverManager对象有什么作用?
Ø为什么没有使用registerDriver()注册驱动?
Ø获取连接的url路径的含义?
1.DriverManager对象两个作用:①、注册驱动和②、获取连接;
2.为什么使用没有使用registerDriver注册驱动?
DriverManager类:
原来这样写:
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
com.mysql.jdbc.Driver源码:
现在这样写:
Class.forName(“com.mysql.jdbc.Driver”);//强制加载class到内存
结论:直接调用registerDriver方法会导致驱动加载2次.
[if !supportLists]3.[endif]获取连接的url路径的含义?
[if !supportLists]1.4 [endif]04_Connection的介绍
[if !supportLists]Ø [endif]Connection对象有哪些作用?
有两个作用:①、创建执行SQL的Statement(语句)对象;②、管理事务
作用一、创建执行sql语句的对象:
作用二:管理事务
总结:
[if !supportLists]1.[endif]Connection对象的作用?
获取语句对象
Statement
CallableStatement(后面Oracle说)
管理事务
setAutoCommit(false);//手动开启事务
...
...
commit()
rollback();
[if !supportLists]1.5 [endif]05_Statement的介绍
[if !supportLists]Ø [endif]Statement对象有哪些作用?
有两个作用:①、执行sql语句;②、进行批处理
作用一:执行sql语句
作用二:进行批处理:就是把几条sql语句添加到Statement对象命令列表中,一次性执行多条sql语句。
[if !supportLists]1.6 [endif]06_ResultSet的介绍
ResultSet代表select查询后的结果集
方法:
*向下移动光标.
*获得结果集中整形数据.
*获得结果集中字符串类型的数据:
...
遍历结果集:
* 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()){
...
}
原理:
总结:
1. Result rs = statement.executeQuery(“select * from user”);
2.while(rs.next()) {
rs.getInt(“id”);
rs.getString(“username”);
rs.getObject(“password”);
rs.getInt(4);
}
[if !supportLists]1.7 [endif]07_JDBC的资源的释放
Connection的使用原则:尽量要晚创建,尽量早释放!!!
try{
..
...
}catch(){...}
finally {....}
要掌握,看看代码,晚上写
[if !supportLists]Ø [endif]掌握释放资源的标准代码:一般把释放资源的代码放到finally里面去执行
finally {
//5、方法资源
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;
}
}
1、为什么对象调用了close()方法以后还要把对象手动赋值为null?
资源释放的一段标准代码:
// 4.释放资源.
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null; // 为了让JVM垃圾回收更早回收该对象.
}
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;
}
总结:
try {
//加载驱动
//获取连接
//创建statement
//执行SQL
//遍历结果集
}catch(Exception ex) {
}finally {
//释放资源
rs,stat,conn
if (rs!=null) {
try {
rs.close();
}catch() {}
rs = null;
}
}
[if !supportLists]1.8 [endif]08_JDBC的CRUD操作之保存(写)
思路:
加载驱动
获得连接
获得Statement对象,执行SQL
释放资源
@Test
/**
*保存操作的代码实现
*/
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;
}
}
}
[if !supportLists]1.9 [endif]09_JDBC的CRUD操作之修改操作(写)
@Test
/**
*修改操作的代码实现
*/
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;
}
}
}
[if !supportLists]1.10 [endif]10_JDBC的CRUD操作之删除操作
@Test
/**
*删除操作的代码实现
*/
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;
}
}
}
[if !supportLists]1.11 [endif]11_JDBC的CRUD操作之查询操作
@Test
/**
*查询多条记录
*/
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;
}
}
}
[if !supportLists]1.12 [endif]12_JDBC的工具类的抽取(写)
[if !supportLists]Ø [endif]总结一下抽取的原则:把相同的操作抽到一个方法里,可以重复利用。
关于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);
}
}
[if !supportLists]1.13 [endif]13_JDBC的配置信息提取到配置文件
[if !supportLists]Ø [endif]如何定义配置文件以及加载配置文件中的信息?
定义配置文件:
获取配置文件中的信息:
[if !supportLists]1.14 [endif]14_JDBC的SQL注入漏洞(SQL演示)
[if !supportLists]Ø [endif]什么是sql注入漏洞?
在我们的网站中肯定会有登录的操作,用户之所以能登录成功,肯定是通过用户名和对应的密码去数据库中能找到对应的一条数据,假设某个人不清楚密码,只通过输入用户名就能从数据库中找到一条对应的数据,那么这就称为sql注入漏洞。
简单说:
通过表单或界面输入一些SQL字符串,能到达服务器执行,达到非法操作的目的.
正常的基本登录流程代码:
sql注入代码演示:
输入用户名
[if !supportLists]n [endif]aaa’ or ‘1=1密码随意
[if !supportLists]n [endif]aaa’ -- 密码随意 注意:--后面有空格
SQL环境演示:
SELECT * FROM USER WHERE username='aaa' AND PASSWORD='123'
SELECT * FROM USER WHERE username='aaa' OR '1=1' AND PASSWORD='wrwrw'
SELECT * FROM USER WHERE username='aaa'-- ' AND PASSWORD='wrwrw'
代码参见:/jdbc/src/com/itheima/jdbc/demo4/UserDao.java
[if !supportLists]1.15 [endif]15_JDBC的SQL注入漏洞分析及解决
[if !supportLists]Ø [endif]sql注入漏洞的原因是什么?
[if !supportLists]Ø [endif]如何解决sql注入的漏洞?
[if !supportLists]1、 [endif]原因:主要是用户输入了sql中规定的关键字,导致我们的sql语句的判断条件发生了变化。
使用Statement对象来执行一个sql语句是这样的:
resultSet=statement.executeQuery("select * from user where username='"+username+"' and password='"+password+"';");
我们把用户输入的内容放到上面的sql语句对应的变量处:
or前面的一部分判断成立就能查到结果。
[if !supportLists]2、 [endif]解决sql注入的漏洞:使用PreparedStatement对象而不是Statement对象。
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;
}
代码参见:/jdbc/src/com/itheima/jdbc/demo4/UserDao.java
[if !supportLists]1.16 [endif]16_JDBC的CRUD操作之PreparedStatement保存操作(写)
@Test
/**
*保存操作
*/
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);
}
}
[if !supportLists]1.17 [endif]17_JDBC的CRUD操作之PreparedStatement修改操作
@Test
/**
*修改操作
*/
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);
}
}
[if !supportLists]1.18 [endif]18_JDBC的CRUD操作之PreparedStatement删除操作
@Test
/**
*删除操作
*/
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);
}
}
[if !supportLists]1.19 [endif]19_JDBC的CRUD操作之PreparedStatement查询操作
@Test
/**
*查询操作
*/
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);
}
}
[if !supportLists]1.20 [endif]20_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));
}
1.21总结:
阐述什么是JDBC以及为什么要使用JDBC
独立完成使用JDBC核心API对数据库表记录的CRUD操作(重点)
完成JDBC工具类的抽取与改进(重点)
阐述什么是SQL注入漏洞并给出解决方法
独立完成使用JDBC的预编译对象进行CRUD操作(重点)
JDBC的批处理操作