import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class TestJDBC4 {
private static String driver ="com.mysql.cj.jdbc.Driver";
private static String url="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
private static String user="root";
private static String password="root";
public static void main(String[] args) {
//testDelete();
testUpdate();
}
public static void testUpdate(){
Connection connection=null;
Statement statement=null;
try{
Class.forName(driver);
connection =DriverManager.getConnection(url, user,password);
statement = connection.createStatement();
String sql="update dept set dname='总部',loc='北京' where deptno= 30 ";
int rows = statement.executeUpdate(sql);
System.out.println("影响数据行数为:"+rows);
}catch (Exception e){
e.printStackTrace();
}finally {
if(null != statement){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null != connection){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static void testDelete(){
Connection connection=null;
Statement statement=null;
try{
Class.forName(driver);
connection =DriverManager.getConnection(url, user,password);
statement = connection.createStatement();
String sql="delete from dept where deptno =40";
int rows = statement.executeUpdate(sql);
System.out.println("影响数据行数为:"+rows);
}catch (Exception e){
e.printStackTrace();
}finally {
if(null != statement){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null != connection){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
import java.sql.*;
public class TestJDBC5 {
private static String driver ="com.mysql.cj.jdbc.Driver";
private static String url="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
private static String user="root";
private static String password="root";
public static void main(String[] args) {
testQuery();
}
public static void testQuery(){
Connection connection = null;
Statement statement=null;
ResultSet resultSet=null;
try{
Class.forName(driver);
connection = DriverManager.getConnection(url, user,password);
statement = connection.createStatement();
String sql="select * from emp";
resultSet = statement.executeQuery(sql);
while(resultSet.next()){
int empno = resultSet.getInt("empno");
String ename = resultSet.getString("ename");
String job = resultSet.getString("job");
int mgr = resultSet.getInt("mgr");
Date hiredate = resultSet.getDate("hiredate");
double sal= resultSet.getDouble("sal");
double comm= resultSet.getDouble("comm");
int deptno= resultSet.getInt("deptno");
System.out.println(""+empno+" "+ename+" "+job+" "+mgr+" "+hiredate+" "+sal+" "+comm+" "+deptno);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(null != resultSet){
try {
resultSet.close();
} catch (SQLException e) {e.printStackTrace();
}
}
if(null != statement){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null != connection){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
•ResultSet里的数据一行一行排列,每行有多个字段,且有一个记录指针,指针所指的数据行叫做当前数据行,我们只能来操作当前的数据行。我们如果想要取得某一条记录,就要使用ResultSet的next()方法 ,如果我们想要得到ResultSet里的所有记录,就应该使用while循环。
•ResultSet对象自动维护指向当前数据行的游标。每调用一次next()方法,游标向下移动一行。
•初始状态下记录指针指向第一条记录的前面,通过next()方法指向第一条记录。循环完毕后指向最后一条记录的后面。
方法名
|
说 明
|
|
boolean next()
|
将光标从当前位置向下移动一行
|
|
boolean previous()
|
游标从当前位置向上移动一行
|
|
void close()
|
关闭ResultSet 对象
|
|
int getInt(int colIndex)
|
以int形式获取结果集当前行指定列号值
|
|
int getInt(String colLabel)
|
以int形式获取结果集当前行指定列名值
|
|
float getFloat(int colIndex)
|
以float形式获取结果集当前行指定列号值
|
|
Float getFloat(String colLabel)
|
以float形式获取结果集当前行指定列名值
|
|
String getString(int colIndex)
|
以String 形式获取结果集当前行指定列号值
|
|
StringgetString(String colLabel)
|
以String形式获取结果集当前行指定列名值
|
|
作为一种好的编程风格,应在不需要Statement对象和Connection对象时显式地关闭它们。关闭Statement对象和Connection对象的语法形式为:用户不必关闭ResultSet。当它的 Statement 关闭、重新执行或用于从多结果序列中获取下一个结果时,该ResultSet将被自动关闭。
为什么将结果封装成对象或者对象集合?
1、java是面向对象的编程语言,java中所有的数据处理都是基于面向对象的编码风格实现的,让数据以符合java风格的形式存在,便于对数据的后续处理
2、ResultSet 集合虽然可以存放数据,但是它是JDBC中查询数据的一种手段,是一种数据的临时存储方案,使用完毕是要进行释放和关闭
封装后台查询数据并在前台显示
如何将结果集中的数据在java中进行存储和传递?
准备和数据库表格相对应的一个实体类,用于封装结果集中的每一条数据,数据库表格中的每一个字段就是实体类的一个属性,实体类的一个对象就可以用于存储数据库表中的一条记录.
准备实体类
import java.io.Serializable;
import java.util.Date;
/*
* 实体类:
* 和数据库表格名称和字段是一一对应的类
* 该类的对象主要用处是存储从数据库中查询出来的数据
* 除此之外,该类没有任何的其他功能
* 要求
* 1类名和表名保持一致 (见名知意)
* 2属性个数和数据库的表的列数保持一致
* 3属性的数据类型和列的数据类型保持一致
* 4属性名和数据库表格的列名要保持一致
* 5所有的属性必须都是私有的 (出于安全考虑)
* 6实体类的属性推荐写成包装类
* 7日期类型推荐写成java.util.Date
* 8所有的属性都要有get和set方法
* 9必须具备空参构造方法
* 10实体类应当实现序列化接口 (mybatis缓存 分布式需要 )
* 11实体类中其他构造方法可选
* */
public class Emp implements Serializable {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
@Override
public String toString() {
return "Emp{" +
"empno=" + empno +
", ename='" + ename + '\'' +
", job='" + job + '\'' +
", mgr=" + mgr +
", hiredate=" + hiredate +
", sal=" + sal +
", comm=" + comm +
", deptno=" + deptno +
'}';
}
public Emp(Integer empno, String ename, String job, Integer mgr, Date hiredate, Double sal, Double comm, Integer deptno) {
this.empno = empno;
this.ename = ename;
this.job = job;
this.mgr = mgr;
this.hiredate = hiredate;
this.sal = sal;
this.comm = comm;
this.deptno = deptno;
}
public Emp(){
}
public Integer getEmpno() {
return empno;
}
public void setEmpno(Integer empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Integer getMgr() {
return mgr;
}
public void setMgr(Integer mgr) {
this.mgr = mgr;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
public Double getComm() {
return comm;
}
public void setComm(Double comm) {
this.comm = comm;
}
public Integer getDeptno() {
return deptno;
}
public void setDeptno(Integer deptno) {
this.deptno = deptno;
}
}
使用实体类封装结果集
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class TestJDBC5 {
private static String driver ="com.mysql.cj.jdbc.Driver";
private static String url="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
private static String user="root";
private static String password="root";
public static void main(String[] args) {
List emps = testQuery();
// 遍历集合
for (Emp emp : emps) {
System.out.println(emp);
}
}
public static List testQuery(){
Connection connection = null;
Statement statement=null;
ResultSet resultSet=null;
List list =null;
try{
Class.forName(driver);
connection = DriverManager.getConnection(url, user,password);
statement = connection.createStatement();
String sql="select * from emp";
resultSet = statement.executeQuery(sql);
list=new ArrayList<>();
while(resultSet.next()){
int empno = resultSet.getInt("empno");
String ename = resultSet.getString("ename");
String job = resultSet.getString("job");
int mgr = resultSet.getInt("mgr");
Date hiredate = resultSet.getDate("hiredate");
double sal= resultSet.getDouble("sal");
double comm= resultSet.getDouble("comm");
int deptno= resultSet.getInt("deptno");
Emp emp =new Emp(empno, ename, job, mgr, hiredate, sal, comm, deptno);
list.add(emp);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(null != resultSet){
try {
resultSet.close();
} catch (SQLException e) {e.printStackTrace();
}
}
if(null != statement){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null != connection){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return list;
}
}
Sql注入
SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据,致使非法数据侵入系统。
以模拟登录为例:在前台输入用户名和密码,后台判断信息是否正确,并给出前台反馈信息,前台输出反馈信息。
具体实现步骤为
创建数据库表
public class Account implements Serializable {
private int aid;
private String username;
private String password;
private int money;
测试代码
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class TestInjection {
private static String driver ="com.mysql.cj.jdbc.Driver";
private static String url="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
private static String user="root";
private static String password="root";
public static void main(String[] args) {
Scanner sc =new Scanner(System.in);
System.out.println("请输入用户名");
String username=sc.next();
System.out.println("请输入密码");
String pwd =sc.next();
Account account = getAccount(username, pwd);
System.out.println(null!= account?"登录成功":"登录失败");
sc.close();
}
public static Account getAccount(String username,String pwd){
Connection connection = null;
Statement statement=null;
ResultSet resultSet=null;
Account account =null;
try{
Class.forName(driver);
connection = DriverManager.getConnection(url, user,password);
statement = connection.createStatement();
String sql="select * from account where username ='"+username+"' and password ='"+pwd+"'";
System.out.println(sql);
resultSet = statement.executeQuery(sql);
while(resultSet.next()){
int aid = resultSet.getInt("aid");
String usernamea = resultSet.getString("username");
String pwda = resultSet.getString("password");
double money = resultSet.getDouble("money");
account=new Account(aid,usernamea,pwda,money);
System.out.println(account);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(null != resultSet){
try {
resultSet.close();
} catch (SQLException e) {e.printStackTrace();
}
}
if(null != statement){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null != connection){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return account;
}
}
当输入了精心设计的用户名密码后,即使是错误的,也能登录成功。让登录功能形同虚设。这是为什么呢,这就是SQL注入风险,原因在于SQL语句是字符串拼接的。SQL语句中拼接的内容破坏了SQL语句原有的判断逻辑
如何解决呢?使用PreparedStatement预编译语句对象就可以解决掉。
使用预编译语句对象防止注入攻击
import java.sql.*;
import java.util.Scanner;
public class TestInjection2 {
private static String driver ="com.mysql.cj.jdbc.Driver";
private static String url="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
private static String user="root";
private static String password="root";
public static void main(String[] args) {
Scanner sc =new Scanner(System.in);
System.out.println("请输入用户名");
String username=sc.next();
System.out.println("请输入密码");
String pwd =sc.next();
Account account = getAccount(username, pwd);
System.out.println(null!= account?"登录成功":"登录失败");
sc.close();
}
public static Account getAccount(String username,String pwd){
Connection connection = null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
Account account =null;
try{
Class.forName(driver);
connection = DriverManager.getConnection(url, user,password);
/*
* 1使用PreparedStatement语句对象防止注入攻击
* 2PreparedStatement 可以使用 ? 作为参数的占位符
* 3使用?作为占位符,即使是字符串和日期类型,也不使用单独再添加 ''
* 4connection.createStatement();获得的是普通语句对象 Statement
* 5connection.prepareStatement(sql);可以获得一个预编译语句对象PreparedStatement
* 6如果SQL语句中有?作为参数占位符号,那么要在执行CURD之前先设置参数
* 7通过set***(问号的编号,数据) 方法设置参数
* */
String sql="select * from account where username = ? and password = ?";
preparedStatement = connection.prepareStatement(sql);//这里已经传入SQL语句
//设置参数
preparedStatement.setString(1,username );
preparedStatement.setString(2,pwd );
//执行CURD
resultSet = preparedStatement.executeQuery();// 这里不需要再传入SQL语句
while(resultSet.next()){
int aid = resultSet.getInt("aid");
String usernamea = resultSet.getString("username");
String pwda = resultSet.getString("password");
double money = resultSet.getDouble("money");
account=new Account(aid,usernamea,pwda,money);
System.out.println(account);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(null != resultSet){
try {
resultSet.close();
} catch (SQLException e) {e.printStackTrace();
}
}
if(null != preparedStatement){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null != connection){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return account;
}
}
prepareStatment对象在set***方法上,会对单引号进行转译处理,也就是说,?中的数据的单引号 ‘ 会被转义成 \’,这样就单引号就不会破坏sql语句的结构,
SELECT * FROM users WHERE userName = ? AND password = ?
preparedStatement.setString(1,"xiaoming");
preparedStatement.setString(2,'anything' OR 'x'='x');
会被转义为
SELECT * FROM users WHERE userName = 'xiaoming' AND password = 'anything\' OR\'x\'=\'x\''
而不是
SELECT * FROM users WHERE userName = 'xiaoming' AND password = 'anything' OR 'x'='x'
|
说白了就是把值当中的所有单引号给转义了!这就达到了防止sql注入的目的,说白了mysql驱动的PreparedStatement实现类的setString();方法内部做了单引号的转义,而Statement不能防止sql注入,就是因为它没有把单引号做转义,而是简单粗暴的直接拼接字符串,所以达不到防止sql注入的目的。
预编译
当客户端发送一条sql语句给DBMS时,MySQL的执行流程如下图
sql命令的执行流程如下
1. 客户端向服务器端发送SQL命令
2. 服务器端连接模块连接并验证
3. 缓存模块解析SQL为Hash并与缓存中Hash表对应。如果有结果直接返回结果,如果没有对应继续向下执行
4. 解析器解析SQL为解析树,如果出现错误,报SQL解析错误。如果正确,向下传递
5. 预处理器对解析树继续处理,处理成新的解析树。
6. 优化器根据开销自动选择最优执行计划,生成执行计划
7. 执行器执行执行计划,访问存储引擎接口
8. 存储引擎访问物理文件并返回结果
9. 如果开启缓存,缓存管理器把结果放入到查询缓存中。
10. 返回结果给客户端
当客户发送一条SQL语句给DBMS后,DBMS总是需要校验SQL语句的语法格式是否正确,然后把SQL语句编译成可执行的函数,最后才是执行SQL语句。其中校验语法,和编译所花的时间可能比执行SQL语句花的时间还要多。
预编译语句PreparedStatement 是java.sql中的一个接口,它是Statement的子接口。通过Statement对象执行SQL语句时,需要将SQL语句发送给DBMS,由DBMS首先进行编译后再执行。预编译语句和Statement不同,在创建PreparedStatement 对象时就指定了SQL语句,该语句立即发送给DBMS进行编译。当该编译语句被执行时,DBMS直接运行编译后的SQL语句,而不需要像其他SQL语句那样首先将其编译。预编译的SQL语句处理性能稍微高于普通的传递变量的办法。
例如:我们需要执行多次insert语句,但只是每次插入的值不同,MySQL服务器也是需要每次都去校验SQL语句的语法格式,以及编译,这就浪费了太多的时间。如果使用预编译功能,那么只对SQL语句进行一次语法校验和编译,所以效率要高。
预编译如何开启?
我们可以通过设置URL中的参数来控制预编译是否开启
useServerPrepStmts是否开启预编译
cachePrepStmts 是否启用预编译缓存
"jdbc:mysql://localhost:3306/mydb?*****&useServerPrepStmts=true&cachePrepStmts=true";
|
值得注意的是,我们的Connector/J 5.0.5及之后useServerPrepStmts默认false,就是默认没有开启预编译,之前默认为true, cachePrepStmts 一直默认为false,需要我们手动设置才可以启用预编译,在开启预编译的同时要同时开启预编译缓存才能带来些许的性能提升
Statement和PreparedStatment的关系和区别
关系:public interface PreparedStatement extends Statement
区别
PreparedStatment安全性高,可以避免SQL注入
PreparedStatment简单不繁琐,不用进行字符串拼接
PreparedStatment性能高,用在执行多个相同数据库DML操作时,可以减少sql语句的编译次数
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class TestPreparedSstatement {
private static String driver ="com.mysql.cj.jdbc.Driver";
private static String url="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
private static String user="root";
private static String password="root";
public static void main(String[] args) {
//testAdd();
//testUpdate();
//testDelete();
testQuery();
}
public static void testAdd(){
// 向 Emp表中增加一条数据
Connection connection = null;
PreparedStatement preparedStatement=null;
try{
Class.forName(driver);
connection = DriverManager.getConnection(url, user,password);
String sql="insert into emp values(DEFAULT ,?,?,?,?,?,?,?)";
preparedStatement = connection.prepareStatement(sql);//这里已经传入SQL语句
//设置参数
preparedStatement.setString(1,"Mark");
preparedStatement.setString(2,"MANAGER" );
preparedStatement.setInt(3,7839);
preparedStatement.setDate(4,new Date(System.currentTimeMillis()));
preparedStatement.setDouble(5,3000.12);
preparedStatement.setDouble(6,0.0);
preparedStatement.setDouble(7,30);
//执行CURD
int rows =preparedStatement.executeUpdate();// 这里不需要再传入SQL语句
System.out.println(rows);
}catch (Exception e){
e.printStackTrace();
}finally {
if(null != preparedStatement){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null != connection){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static void testUpdate(){
// 根据工号修改员工表中的数据
Connection connection = null;
PreparedStatement preparedStatement=null;
try{
Class.forName(driver);
connection = DriverManager.getConnection(url, user,password);
String sql="update emp set ename =? ,job=? where empno =?";
preparedStatement = connection.prepareStatement(sql);//这里已经传入SQL语句
//设置参数
preparedStatement.setString(1,"Jhon");
preparedStatement.setString(2,"ANALYST" );
preparedStatement.setInt(3,7935);
//执行CURD
int rows =preparedStatement.executeUpdate();// 这里不需要再传入SQL语句
System.out.println(rows);
}catch (Exception e){
e.printStackTrace();
}finally {
if(null != preparedStatement){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null != connection){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static void testDelete(){
// 根据工号删除员工表中的数据
Connection connection = null;
PreparedStatement preparedStatement=null;
try{
Class.forName(driver);
connection = DriverManager.getConnection(url, user,password);
String sql="delete from emp where empno =?";
preparedStatement = connection.prepareStatement(sql);//这里已经传入SQL语句
//设置参数
preparedStatement.setInt(1,7935);
//执行CURD
int rows =preparedStatement.executeUpdate();// 这里不需要再传入SQL语句
System.out.println(rows);
}catch (Exception e){
e.printStackTrace();
}finally {
if(null != preparedStatement){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null != connection){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static void testQuery(){
// 查询名字中包含字母A的员工信息
Connection connection = null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
List list =null;
try{
Class.forName(driver);
connection = DriverManager.getConnection(url, user,password);
/*
* 1使用PreparedStatement语句对象防止注入攻击
* 2PreparedStatement 可以使用 ? 作为参数的占位符
* 3使用?作为占位符,即使是字符串和日期类型,也不使用单独再添加 ''
* 4connection.createStatement();获得的是普通语句对象 Statement
* 5connection.prepareStatement(sql);可以获得一个预编译语句对象PreparedStatement
* 6如果SQL语句中有?作为参数占位符号,那么要在执行CURD之前先设置参数
* 7通过set***(问号的编号,数据) 方法设置参数
* */
String sql="select * from emp where ename like ? ";
preparedStatement = connection.prepareStatement(sql);//这里已经传入SQL语句
//设置参数
preparedStatement.setString(1,"%A%");
//执行CURD
resultSet = preparedStatement.executeQuery();// 这里不需要再传入SQL语句
list=new ArrayList() ;
while(resultSet.next()){
int empno = resultSet.getInt("empno");
String ename = resultSet.getString("ename");
String job = resultSet.getString("job");
int mgr = resultSet.getInt("mgr");
Date hiredate = resultSet.getDate("hiredate");
double sal= resultSet.getDouble("sal");
double comm= resultSet.getDouble("comm");
int deptno= resultSet.getInt("deptno");
Emp emp =new Emp(empno, ename, job, mgr, hiredate, sal, comm, deptno);
list.add(emp);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(null != resultSet){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null != preparedStatement){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null != connection){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 遍历集合
for (Emp emp : list) {
System.out.println(emp);
}
}
}
什么是批处理?
当我们有多条sql语句需要发送到数据库执行的时候,有两种发送方式,一种是执行一条发送一条sql语句给数据库,另一个种是发送一个sql集合给数据库,也就是发送一个批sql到数据库。普通的执行过程是:每处理一条数据,就访问一次数据库;而批处理是:累积到一定数量,再一次性提交到数据库,减少了与数据库的交互次数,所以效率会大大提高,很显然两者的数据库执行效率是不同的,我们发送批处理sql的时候数据库执行效率要高
statement语句对象实现批处理有如下问题
缺点:采用硬编码效率低,安全性较差。
原理:硬编码,每次执行时相似SQL都会进行编译
PreparedStatement+批处理
优点:语句只编译一次,减少编译次数。提高了安全性(阻止了SQL注入)
原理:相似SQL只编译一次,减少编译次数
注意: 需要设置批处理开启&rewriteBatchedStatements=true
import java.sql.*;
public class TestBatch {
private static String driver ="com.mysql.cj.jdbc.Driver";
private static String url="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useServerPrepStmts=true&cachePrepStmts=true&&rewriteBatchedStatements=true";
private static String user="root";
private static String password="root";
public static void main(String[] args) {
testAddBatch();
}
// 定义一个方法,向部门表增加1000条数据
public static void testAddBatch(){
Connection connection = null;
PreparedStatement preparedStatement=null;
try{
Class.forName(driver);
connection = DriverManager.getConnection(url, user,password);
String sql="insert into dept values (DEFAULT ,?,?)";
preparedStatement = connection.prepareStatement(sql);//这里已经传入SQL语句
//设置参数
for (int i = 1; i <= 10663; i++) {
preparedStatement.setString(1, "name");
preparedStatement.setString(2, "loc");
preparedStatement.addBatch();// 将修改放入一个批次中
if(i%1000==0){
preparedStatement.executeBatch();
preparedStatement.clearBatch();// 清除批处理中的数据
}
}
/*
* 整数数组中的元素代表执行的结果代号
* SUCCESS_NO_INFO -2
* EXECUTE_FAILED -3
* */
/*int[] ints = */
preparedStatement.executeBatch();
preparedStatement.clearBatch();
}catch (Exception e){
e.printStackTrace();
}finally {
if(null != preparedStatement){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null != connection){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}