一、PreparedStatement的使用
PreparedStatement是Statement的子接口,属于预处理操作。
获取PreparedStatement的方法:
使用Connection对象的 PreparedStatement prepareStatement(String sql)
package preparedstatment;
import java.sql.*;
import util.DBUtil;
public class InsertDemo {
public static void main(String[] args) {
Connection conn=DBUtil.getDBConnection();
String sql="insert into person(perid,name,sex)values(?,?,?)";
PreparedStatement ps=null;
try {
ps=conn.prepareStatement(sql);
ps.setInt(1, 99);
ps.setString(2, "东方不败");
ps.setString(3, "女");
ps.executeUpdate();
System.out.println("执行成功!");
} catch (SQLException e) {
e.printStackTrace();
}finally{
try {
ps.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
1、使用PreparedStatement的优点(1) 对于结构相同的SQL,可以提高执行效率
在创建PreparedStatement对象时就指定了sql语句,该语句将被立即发送给DBMS进行编译。
预编译的语句被存储在DBMS的缓存中,下次执行相同的SQL语句时,则可以直接从缓存中取出来。
(2)可以有效预防SQL注入
在使用参数化查询的情况下,数据库系统(DBMS)不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,即使参数中含有恶意的指令,也不会被数据库所运行。
package util;
import java.io.*;
import java.sql.*;
import java.util.Properties;
public class DBUtil {
static{
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getDBConnection(){
Properties pro=new Properties();
InputStream input=null;
Connection conn=null;
try {
input =new FileInputStream("c:"+File.separator+"mydb.properties");
pro.load(input);
conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", pro);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
}
package inject;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import util.DBUtil;
public class StmtInject {
public static void main(String[] args) {
Connection conn=DBUtil.getDBConnection();
Statement stmt=null;
ResultSet rs=null;
try {
stmt=conn.createStatement();
String username="lily";
String password="abcxyz' or '2=2";
String sql="select * from user where username='"+username+"' and '"+password+"'";
System.out.println(sql);
rs=stmt.executeQuery(sql);
if(rs.next()){
System.out.println("查询成功!");
}else{
System.out.println("查询失败!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
try {
rs.close();
conn.close();
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
package inject;
import java.sql.*;
import util.DBUtil;
public class PreventInject {
public static void main(String[] args) {
Connection conn=DBUtil.getDBConnection();
PreparedStatement ps=null;
ResultSet rs=null;
String sql="select * from user where username=? and password=?";
try {
ps=conn.prepareStatement(sql);
String username="lily";
String password="123456";
ps.setString(1, username);
ps.setString(2, password);
rs=ps.executeQuery();
if(rs.next()){
System.out.println("查询成功!");
}else{
System.out.println("查询失败!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
try {
rs.close();
conn.close();
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
二、事务
概念: 数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么全部成功,要么全部失败。
1、事务的四个特性(ACID)
(1)原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
(2) 一致性(Consistency)事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
(3) 隔离性(Isolation)事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
(4) 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响
2、事务的隔离级别
前提概念:
(1)脏读(dirty read):一个事务读取到了另一个事务未提交的数据(脏数据)的现象。
(2)不可重复读:一个事务两次读取到的数据不一致的现象。
(3)幻读:一个事务在读取数据时,另一个事务添加了数据记录,则第一个事务读取到了新添加的数据(与第一次读取数据比较)。
事务的隔离级别:
1.读未提交级别(read uncommitted)
读取到了另一个事务还未提交的数据。可能会发生脏读、不可重复读、幻读。
2.读提交级别(read committed)
只能读取其他事务已提交的数据。可能会发生不可重复读、幻读。
3.可重复读级别(repeatable read,MySQL默认隔离级别)
在一个事务中可以重复读取相同的数据。在InnoDB数据库存储引擎(此存储引擎支持
事务)中,已经解决了幻读问题。
4.串行化级别(serializable)
最安全,但并发效率低。
3、java对事务的支持
为了让多个 SQL 语句作为一个事务执行:
(1)调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务
(2)在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
(3)在出现异常时,调用 rollback(); 方法回滚事务
(4)若此时 Connection 没有被关闭, 则需要恢复其自动提交状态
默认情况下,一个sql是一个事务。
1.查看当前会话(session)的隔离级别:
select @@tx_isolation;
2.设置当前会话的隔离级别:
set session transaction isolation level read uncommitted | read committed
| repeatable read |
3.开启事务
start transaction;
4.提交事务
commit;
5.回滚整个事务
rollback
6.设置保存点
savepoint 保存点名称
7.回滚到保存点
rollback to 保存点名称
package transaction;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import util.DBUtil;
public class AccoutDemo {
public static void main(String[] args) {
boolean isSucceed=operator(2,1,500);
if(isSucceed){
System.out.println("交易成功!");
}else{
System.out.println("交易失败!");
}
}
public static boolean operator(int formId,int toId,double money){
boolean flag=false;
Connection conn=DBUtil.getDBConnection();
PreparedStatement ps=null;
String sql1="update account set balance=balance-? where id=?";
String sql2="update account set balance=balance+? where id=?";
try {
conn.setAutoCommit(false);
ps=conn.prepareStatement(sql1);
ps.setDouble(1, money);
ps.setInt(2, formId);
ps.executeUpdate();
ps.close();
ps=conn.prepareStatement(sql2);
ps.setDouble(1, money);
ps.setInt(2, toId);
ps.executeUpdate();
conn.commit();
flag=true;
} catch (SQLException e) {
try {
conn.rollback();
flag=false;
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally{
try {
ps.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return flag;
}
}