JDBC+BDUtil的扩展和SQL注入问题 +事务
一、JDBC
JDBC:java database connectivity SUN公司提供的一套操作数据库的标准规范
JDBC与数据库驱动的关系:接口与实现的关系。
1.JDBC的四大金刚
1.DriverManager:用于注册驱动
2.Connection: 表示与数据库创建的连接
3.Statement: 操作数据库sql语句的对象
4.ResultSet: sql语句执行后返回的结果集
2.JDBC操作步骤
1.加载驱动
2.创建连接对象
3.创建Statement对象
4.通过Statement对象执行sql语句
5.获取执行结果
3.JDBC实现增删改查
import org.junit.Test;
import java.sql.*;
public class Test01 {
//使用JDBC操作数据库
@Test
public void test01() throws ClassNotFoundException, SQLException {
//添加数据
//导入驱动包
Class.forName("com.mysql.jdbc.Driver");
//获取连接对象
String url = "jdbc:mysql://localhost:3306/2211javaee?useUnicode=true&characterEncoding=utf8";
String username = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, username, password);
//获取发送指令对象
Statement statement = connection.createStatement();
//发送SQL指令
String sql = "INSERT INTO student(name,sex,age,salary,course) VALUES('aaa','男',28,12000,'Java');";
int num = statement.executeUpdate(sql);
System.out.println("影响该表的行数:" + num);
//关闭资源
statement.close();
connection.close();
}
@Test
public void test02() throws ClassNotFoundException, SQLException {
//删除数据
//导入驱动包
Class.forName("com.mysql.jdbc.Driver");
//获取连接对象
String url = "jdbc:mysql://localhost:3306/2211javaee?useUnicode=true&characterEncoding=utf8";
String username = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, username, password);
//获取发送指令对象
Statement statement = connection.createStatement();
//发送SQL指令
String sql = "delete from student where id>7;";
int num = statement.executeUpdate(sql);
System.out.println("影响该表的行数:" + num);
//关闭资源
statement.close();
connection.close();
}
@Test
public void test03() throws ClassNotFoundException, SQLException {
//修改数据
//导入驱动包
Class.forName("com.mysql.jdbc.Driver");
//获取连接对象
String url = "jdbc:mysql://localhost:3306/2211javaee?useUnicode=true&characterEncoding=utf8";
String username = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, username, password);
//获取发送指令对象
Statement statement = connection.createStatement();
//发送SQL指令
String sql = "delete from student where id>7;";
int num = statement.executeUpdate(sql);
System.out.println("影响该表的行数:" + num);
//关闭资源
statement.close();
connection.close();
}
@Test
public void test04() throws ClassNotFoundException, SQLException {
//查询数据
//导入驱动包
Class.forName("com.mysql.jdbc.Driver");
//获取连接对象
String url = "jdbc:mysql://localhost:3306/2211javaee?useUnicode=true&characterEncoding=utf8";
String username = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, username, password);
//获取发送指令对象
Statement statement = connection.createStatement();
//发送SQL指令
String sql = "select * from student";
ResultSet resultSet = statement.executeQuery(sql);
//遍历结果集
while (resultSet.next()){//判断是否遍历的数据行
//获取指定字段上的数据
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String sex = resultSet.getString("sex");
int age = resultSet.getInt("age");
float salary = resultSet.getFloat("salary");
String course = resultSet.getString("course");
System.out.println(id + "\t" + name + "\t" + sex + "\t" + age + "\t" + salary + "\t" + course);
}
//关闭资源
resultSet.close();
statement.close();
connection.close();
}
}
二、创建工具类DBUtil
1.将变化的配置信息搬到配置文件中
2.工具类提供获取连接的方法
3.工具类提供关闭资源的方法
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
//数据库工具类
public class DBUtil {
private static String url;
private static String username;
private static String password;
static {
//获取配置文件对象
Properties properties = new Properties();
try {
//加载配置文件
properties.load(DBUtil.class.getClassLoader().getResourceAsStream("DBConfig.properties"));
} catch (IOException e) {
e.printStackTrace();
}
String driverName = properties.getProperty("driverName");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
try {
//导入驱动包
Class.forName(driverName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//获取连接对象
public static Connection getConnection() throws SQLException {
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
//关闭资源
public static void close(Connection connection, Statement statement, ResultSet resultSet){
if(resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
import com.qf.utils.DBUtil;
import org.junit.Test;
import java.sql.*;
//使用数据库工具类 操作数据库
public class Test02 {
@Test
public void test01(){
//添加数据
Connection connection = null;
Statement statement = null;
try {
//获取连接对象
connection = DBUtil.getConnection();
//获取发送指令对象
statement = connection.createStatement();
//发送SQL指令
String sql = "INSERT INTO student(name,sex,age,salary,course) VALUES('aaa','男',28,12000,'Java');";
int num = statement.executeUpdate(sql);
System.out.println("影响该表的行数:" + num);
} catch (SQLException e) {
e.printStackTrace();
}finally {
//关闭资源
DBUtil.close(connection,statement,null);
}
}
@Test
public void test02(){
//删除数据
Connection connection = null;
Statement statement = null;
try {
//获取连接对象
connection = DBUtil.getConnection();
//获取发送指令对象
statement = connection.createStatement();
//发送SQL指令
String sql ="delete from student where id>5";
int num = statement.executeUpdate(sql);
System.out.println("影响该表的行数:" + num);
} catch (SQLException e) {
e.printStackTrace();
}finally {
//关闭资源
DBUtil.close(connection,statement,null);
}
}
@Test
public void test03(){
//修改数据
Connection connection = null;
Statement statement = null;
try {
//获取连接对象
connection = DBUtil.getConnection();
//获取发送指令对象
statement = connection.createStatement();
//发送SQL指令
String sql = "update student set salary=30000 where id=3";
int num = statement.executeUpdate(sql);
System.out.println("影响该表的行数:" + num);
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
DBUtil.close(connection,statement,null);
}
}
@Test
public void test04(){
//查询数据
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
//获取连接对象
connection = DBUtil.getConnection();
//获取发送指令对象
statement = connection.createStatement();
//发送SQL指令,并返回结果集对象
String sql = "select * from student";
resultSet = statement.executeQuery(sql);
//遍历结果集
while(resultSet.next()){//判断是否遍历的数据行
//获取指定字段上的数据
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String sex = resultSet.getString("sex");
int age = resultSet.getInt("age");
float salary = resultSet.getFloat("salary");
String course = resultSet.getString("course");
System.out.println(id + "\t" + name + "\t" + sex + "\t" + age + "\t" + salary + "\t" + course);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
DBUtil.close(connection,statement,resultSet);
}
}
}
三、SQL注入问题
Statement
PreparedStatement
安全性,避免了SQL注入
性能,预编译,语句-编译-执行
出现原因:MySQL不能识别出哪些是数据哪些是SQL命令
解决思路:告诉MySQL哪些是数据哪些是SQL命令
import com.qf.utils.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Test02 {
public static void main(String[] args) throws SQLException {
String sql = "select * from user where username=? and password=?";
Connection connection = DBUtil.getConnection();
//获取PreparedStatement对象,并将sql命令放入该对象中
PreparedStatement statement = connection.prepareStatement(sql);
//设置参数
statement.setString(1,"aaa");
statement.setString(2,"123456");
//发送SQL命令(1.先把SQL命令发送到MySQL,2.再将数据包发送给MySQL)
ResultSet resultSet = statement.executeQuery();
if(resultSet.next()){
System.out.println("登录成功");
String username = resultSet.getString("username");
String name = resultSet.getString("name");
String nickname = resultSet.getString("nickname");
System.out.println(username + " -- " + name + " -- " + nickname);
}else{
System.out.println("登录失败");
}
DBUtil.close(connection,statement,resultSet);
}
}
四、事务
1.事务的概念
MySQL:每一条语句都属于独立事务,默认自动管理提交的。
如果需要把多条语句当成一个整体,那么就需要把多条语句放在一个事务里面
开启事务:start transaction
提交事务:commit;
回滚事务:rollback
2.JDBC控制事务语句
Connection.setAutoCommit(false); start transaction
Connection.rollback(); rollback
Connection.commit(); commit
1.MySQL
# 需求:模拟银行转账
START TRANSACTION;# 开启事务
UPDATE user set money=money-200 WHERE id=1;
UPDATE user set money=money+200 WHERE id=2;
# COMMIT;# 提交事务
ROLLBACK;# 回滚事务:将数据恢复到开启事务时
2.JDBC模拟银行转账
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
//数据库工具类
public class DBUtil {
private static String url;
private static String username;
private static String password;
static {
//获取配置文件对象
Properties properties = new Properties();
try {
//加载配置文件
properties.load(DBUtil.class.getClassLoader().getResourceAsStream("DBConfig.properties"));
} catch (IOException e) {
e.printStackTrace();
}
String driverName = properties.getProperty("driverName");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
try {
//导入驱动包
Class.forName(driverName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static ThreadLocal<Connection>local = new ThreadLocal<>();
//开启事务
public static void startTransaction() throws SQLException {
Connection connection = getConnection();
connection.setAutoCommit(false);
}
//提交事务
public static void commit() throws SQLException {
Connection connection = local.get();
if(connection != null){
connection.commit();
connection.close();
local.set(null);
}
}
//回滚事务
public static void rollback() throws SQLException {
Connection connection = local.get();
if (connection != null) {
connection.rollback();
connection.close();
local.set(null);
}
}
//获取连接对象
public static Connection getConnection() throws SQLException {
Connection connection = local.get();
if (connection == null) {
connection = DriverManager.getConnection(url, username, password);
local.set(connection);
}
return connection;
}
//关闭资源
public static void close(Connection connection, Statement statement, ResultSet resultSet){
if(resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection != null){
try {
if (connection.getAutoCommit()){//没有开启事务,MySQL自动管理提交
connection.close();
local.set(null);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
import com.qf.utils.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Test01 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement statement1 =null;
PreparedStatement statement2 =null;
try {
connection = DBUtil.getConnection();
//开启事务
connection.setAutoCommit(false);//设置不自动提交(其实就是开启事务)
String sql1= "UPDATE user set money = money-300 WHERE id = 1;";
statement1 = connection.prepareStatement(sql1);
statement1.executeUpdate();
String sql2= "UPDATE user set money = money+300 WHERE id = 2;";
statement2 = connection.prepareStatement(sql2);
statement2.executeUpdate();
//提交事务
connection.commit();
} catch (Exception e) {
System.out.println("出现异常了,回滚事务");
//回滚事务
if (connection != null){
try {
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}finally {
DBUtil.close(null,statement1,null);
DBUtil.close(connection,statement2,null);
}
}
}
3.优化代码
import com.qf.utils.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Test02 {
public static void main(String[] args) {
//转账
try {
DBUtil.startTransaction();
withdrawalMoney();
//System.out.println(10/0);
savaMoney();
DBUtil.commit();
} catch (Exception e) {
System.out.println("发生异常了,回滚事务");
try {
DBUtil.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
//单独调用存钱、取钱
// try {
// withdrawalMoney();
// } catch (SQLException e) {
// e.printStackTrace();
// }
// try {
// saveMoney();
// } catch (SQLException e) {
// e.printStackTrace();
// }
}
//取钱
public static void withdrawalMoney() throws SQLException {
Connection connection = null;
PreparedStatement statement = null;
try {
connection = DBUtil.getConnection();
System.out.println(connection);
String sql = "UPDATE user set money = money - 300 WHERE id =1";
statement = connection.prepareStatement(sql);
statement.executeUpdate();
} finally {
DBUtil.close(connection,statement,null);
}
}
//存钱
public static void savaMoney() throws SQLException {
Connection connection = null;
PreparedStatement statement = null;
try {
connection = DBUtil.getConnection();
System.out.println(connection);
String sql = "UPDATE user set money = money + 300 WHERE id =2";
statement = connection.prepareStatement(sql);
statement.executeUpdate();
}finally {
DBUtil.close(connection,statement,null);
}
}
}
3.事务的特点 ACID
1.原子性( Atomicity )
事务是数据库的逻辑工作单位,事务中包含的各操作要么都完成,要么都不完成
2.一致性( Consistency )
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。
3.隔离性( Isolation )
一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
4.持久性( Durability )
指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
4.事务的隔离级别
1.不考虑事务的隔离级别,会出现以下的情况
脏读:一个线程中的事务读到了另外一个线程中未提交的数据。
不可重复读:一个线程中的事务读到了另外一个线程中已经提交的update的数据。
虚读:一个线程中的事务读到了另外一个线程中已经提交的insert的数据。
2.避免以上现象,通过更改事务的隔离级别来避免
READ UNCOMMITTED 脏读、不可重复读、虚读有可能发生。
READ COMMITTED 避免脏读的发生,不可重复读、虚读有可能发生。
REPEATABLE READ 避免脏读、不可重复读的发生,虚读有可能发生。
SERIALIZABLE 避免脏读、不可重复读、虚读的发生。
级别依次升高,效率依次降低。
MySQL:默认REPEATABLE READ
ORACLE:默认READ COMMITTED
#查看当前的隔离级别
select @@tx_isolation;
#设置隔离级别
set transaction isolation level 隔离级别