PreparedStatement除了解决Statement的拼串、sql注入问题之外,还有哪些好处呢?
① PreparedStatement操作Blob的数据,而Statement做不到。
② Preparedstatement可以实现更高效的批量操作。
1.对于常见的数据我们可以存储在数据库中,那么对于图片、视频等文件是否也可以存储在数据库中呢?
答案是肯定的,先来认识一下BLOB类型的数据:
MySQL中,BLOB是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据。
插入BLOB类型的数据必须使用PreparedStatement,因为BLOB类型的数据无法使用字符串拼接写的(因此对于BLOB类型的文件,也不能使用Statement)。
2.MySQL的四种BLOB类型(除了在存储的最大信息量上不同外,他们是等同的)
MySQL数据库中customers表的结构:
1.向数据库表中插入BLOB类型的数据:
//向数据库表中插入图片文件
@Test
public void insertTest() {
Connection connect = null;
PreparedStatement ps = null;
try {
//1.与数据库建立连接
connect = JdbcUtils.getConnection();
//2.预编译sql
String sql = "insert into customers(name,email,birth,photo) values(?,?,?,?)";
ps = connect.prepareStatement(sql);
ps.setObject(1,"交大");
ps.setObject(2,"[email protected]");
ps.setObject(3,"2000-2-26");
FileInputStream fis = new FileInputStream(new File("E:\\java\\JDBC\\day01_jdbc\\src\\和谐园.png"));
ps.setBlob(4,fis);
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
JdbcUtils.closeConnection(ps,connect);
}
}
插入结果:
注意:此处在插入时,可能会因为插入图片太大而导致插入不成功,此时需要在my.ini文件中对最大允许的上传参数进行设置:max_allowed_packet=16M。设置结束后,先关闭mysql服务,再对mysql服务进行重启,再启动程序进行上传即可成功。
2.从数据库中读取BLOB类型的数据,并下载到本地
//从数据库表中读取BLOB数据,下载到本地
@Test
public void queryTest(){
Connection connect = null;
PreparedStatement ps = null;
InputStream is = null;
FileOutputStream fos = null;
try {
connect = JdbcUtils.getConnection();
String sql = "select id,name,email,birth,photo from customers where id = ?";
ps = connect.prepareStatement(sql);
ps.setInt(1,28);
ResultSet resultSet = ps.executeQuery();
if (resultSet.next()){
// //方式一:使用索引
// int id = resultSet.getInt(1);
// String name = resultSet.getString(2);
// String email = resultSet.getString(3);
// Date date = resultSet.getDate(4);
//方式二:使用别名(若没有起别名,这就是列名)
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String email = resultSet.getString("email");
Date birth = resultSet.getDate("birth");
Customer customer = new Customer(id, name, email, birth);
System.out.println(customer);
Blob photo = resultSet.getBlob("photo");
is = photo.getBinaryStream();
fos = new FileOutputStream(new File("E:\\java\\JDBC\\day01_jdbc\\src\\交大.jpg"));
byte[] bytes = new byte[1024];
int len ;
while ((len= is.read(bytes))!=-1){
fos.write(bytes,0,len);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(is!=null){
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fos!=null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//关闭资源
JdbcUtils.closeConnection(ps,connect);
}
}
/**
* CREATE TABLE goods(id INT PRIMARY KEY AUTO_INCREMENT,NAME VARCHAR(20));
* 数据库中的数据的批量插入,从上到下,依次效率提高
* @author wds
* @date 2021-12-13 20:07
*/
public class InsertTest {
//方式1:批量插入数据的操作,使用Statement
@Test
public void insertTest01(){
Connection connect = null;
Statement statement = null;
try {
connect = JdbcUtils.getConnection();
statement = connect.createStatement();
for(int i = 1;i<=20000;i++){
String sql = "insert into goods(name) values('name_'"+i+"')'";
boolean execute = statement.execute(sql);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.closeConnection(statement,connect);
}
}
//方式2:批量插入数据的操作,使用PreparedStatement
@Test
public void insertTest02() {
Connection connect = null;
PreparedStatement ps = null;
try {
long start = System.currentTimeMillis();
connect = JdbcUtils.getConnection();
String sql = "insert into goods(name) values (?)";
ps = connect.prepareStatement(sql);
for (int i = 1;i<=20000;i++){
ps.setObject(1,"name_"+i);
ps.execute();
}
long end = System.currentTimeMillis();
System.out.println("花费的时间是:"+(end-start)+"毫秒");
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.closeConnection(ps,connect);
}
}
//方式3:批量插入数据的操作,使用PreparedStatement和Batch
/*
1.addBatch()、executeBatch()、clearBatch()
2.mysql服务器默认是关闭批处理的,我们需要通过一个参数,让mysql开启批处理的支持。
?rewriteBatchedStatements=true写在配置文件的url后面
3.使用更新的mysql 驱动:mysql-connector-java-5.1.37-bin.jar
*/
@Test
public void insertTest03(){
Connection connect = null;
PreparedStatement ps = null;
try {
long start = System.currentTimeMillis();
connect = JdbcUtils.getConnection();
String sql = "insert into goods(name) values (?)";
ps = connect.prepareStatement(sql);
for (int i = 1;i<=20000;i++){
ps.setObject(1,"name_"+i);
//1.“攒”sql
if(i%500==0){
//2.执行batch
ps.executeBatch();
//3.清空batch
ps.clearBatch();
}
}
long end = System.currentTimeMillis();
System.out.println("花费的时间是:"+(end-start)+"毫秒");
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.closeConnection(ps,connect);
}
}
//方式4:批量插入数据的操作,使用PreparedStatement和Batch,以及数据库不自动提交
@Test
public void insertTest04(){
Connection connect = null;
PreparedStatement ps = null;
try {
long start = System.currentTimeMillis();
connect = JdbcUtils.getConnection();
//设置数据库不自动提交
connect.setAutoCommit(false);
String sql = "insert into goods(name) values (?)";
ps = connect.prepareStatement(sql);
for (int i = 1;i<=20000;i++){
ps.setObject(1,"name_"+i);
//1.“攒”sql
if(i%500==0){
//2.执行batch
ps.executeBatch();
//3.清空batch
ps.clearBatch();
}
}
connect.commit();
long end = System.currentTimeMillis();
System.out.println("花费的时间是:"+(end-start)+"毫秒");
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.closeConnection(ps,connect);
}
}
}
jdbc.properties
user=root
password=12345678
driverClass=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
PreparedStatement 和 Statement在执行数据库操作时有什么异同?
① PreparedStatement接口是Statement的子接口,它表示一条预编译过的SQL语句
② PreparedStatement通过预编译能最大可能提高操作性能
③ PreparedStatement可以防止SQL注入