目录
MySQL中,BLOB是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据。
插入BLOB类型的数据必须使用PreparedStatement,因为BLOB类型的数据无法使用字符串拼接写的。
MySQL的四种BLOB类型(除了在存储的最大信息量上不同外,他们是等同的)
类型 | 最大(单位:字节) |
TinyBlob | 255 |
Blob | 65k |
MediumBlob | 16M |
LongBlob | 4G |
一、如何使用preparedstatement流插入图片
对于图片之类的文件我们可以使用流插入的形式将我们的文件传入我们的MySQL数据库
注:下面的JDBCUtils.getConnection()是我们之前已经封装过的方法
closeResource 也是我们已经封装过的方法
JAVA【JDBC】【使用PreparedStatement操作数据库】_桜キャンドル淵的博客-CSDN博客
中的二中的封装连接和关闭数据库资源
//向数据表customers中插入Blob类型的字段
@Test
public void testInsert() throws Exception{
Connection conn = JDBCUtils.getConnection();
String sql = "insert into customers(name,email,birth,photo)values(?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setObject(1,"催逝员");
ps.setObject(2, "cuishiyuan@qq.com");
ps.setObject(3,"1992-09-08");
FileInputStream is = new FileInputStream(new File("test.jpg"));
ps.setBlob(4, is);
ps.execute();
JDBCUtils.closeResource(conn, ps);
}
如果是修改的话就把上面代码中的SQL语句的insert改成update再做一些修改即可
二、如何使用preparedstatement流查询图片
使用SQL查询并且将数据库中的照片保存下来,我们可以使用流的方式将我们查询到的图片文件保存到本地。
这是我们要查询的表的结构
//查询数据表customers中Blob类型的字段
@Test
public void testQuery(){
Connection conn = null;
PreparedStatement ps = null;
InputStream is = null;
FileOutputStream fos = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select id,name,email,birth,photo from customers where id = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, 24);
rs = ps.executeQuery();
if(rs.next()){
// 方式一:
// int id = rs.getInt(1);
// String name = rs.getString(2);
// String email = rs.getString(3);
// Date birth = rs.getDate(4);
//方式二:
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
Date birth = rs.getDate("birth");
Customer cust = new Customer(id, name, email, birth);
System.out.println(cust);
//将Blob类型的字段下载下来,以文件的方式保存在本地
Blob photo = rs.getBlob("photo");
is = photo.getBinaryStream();
fos = new FileOutputStream("龙叔说.jpg");
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1){
fos.write(buffer, 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.closeResource(conn, ps, rs);
}
}
我们的图片图片也能够正常地打开和查看。
如果我们上面插入的图片超出了我们之前表格的blob的最大限制,是会上传失败的。
实际使用中根据需要存入的数据大小定义不同的BLOB类型。
需要注意的是:如果存储的文件过大,数据库的性能会下降。
如果在指定了相关的Blob类型以后,还报错:xxx too large,那么在mysql的安装目录下,找my.ini文件加上如下的配置参数: max_allowed_packet=16M。同时注意:修改了my.ini文件之后,需要重新启动mysql服务。
三、使用preparestatement实现批量数据操作
update、delete本身就具有批量操作的效果
此时的批量操作主要指的是批量插入,使用preparedstatement如何实现更高效的批量插入?
题目:向goods表中插入20000条数据
CREATE TABLE goods(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(25)
);
1.使用statement
//方式一:使用Statement
Connection conn = JDBCUtils.getConnection();
Statement st = conn.createStatement();
for(int i = 1;i <= 20000;i++){
String sql = "insert into goods(name)values('name_" + i + "')";
st.execute(sql);
}
2.使用preparedstatement
//批量插入的方式二:使用PreparedStatement
@Test
public void testInsert1() {
Connection conn = null;
PreparedStatement ps = null;
try {
long start = System.currentTimeMillis();
conn = JDBCUtils.getConnection();
String sql = "insert into goods(name)values(?)";
ps = conn.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));//20000:3129
} catch (Exception e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource(conn, ps);
}
}
PreparedStatement 能最大可能提高性能:
DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。
在statement语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存。这样每执行一次都要对传入的语句编译一次。
3.使用addbanch
就如同我们之前在io流中的一样,我们可以读取一批数据然后统一写入磁盘一样,这种方式减少了对于磁盘的io读写,同样的,我们可以通过类似的方式来提高对于MySQL的批量插入效率。
由于我们的MySQL默认是不支持批处理的,所以我们需要在我们的配置文件中加入这个参数来开启批处理
![]()
?rewriteBatchedStatements=true
可以参考上面博客中的方法五来修改配置文件
在mysql.jar 5.1.7版本及之前的版本是不支持批量操作的,在5.1.37版本之后才会支持
@Test
public void testInsert2() {
Connection conn = null;
PreparedStatement ps = null;
try {
long start = System.currentTimeMillis();
conn = JDBCUtils.getConnection();
String sql = "insert into goods(name)values(?)";
ps = conn.prepareStatement(sql);
for(int i = 1;i <= 20000;i++){
ps.setObject(1, "name_" + i);
//1."攒"sql
ps.addBatch();
//每隔500次执行一次
if(i % 500 == 0){
//2.执行batch
ps.executeBatch();
//3.清空batch
ps.clearBatch();
}
}
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));//20000:3129 -- 446
} catch (Exception e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource(conn, ps);
}
}
4.设置不允许自动提交数据
像truncate table之类的DDL操作会自动地进行数据提交,而delete from之类的DML操作可以实现数据的回滚,前提是要设置过set autocommit=false,因为默认情况下DML的增删改也是会自动提交的。
MySQL【创建和管理表】_桜キャンドル淵的博客-CSDN博客
中的六、清空表
我们可以在获取连接数据库的时候不着急地去提交,而是把数据都传完之后再去提交。
//批量插入的方式四:设置连接不允许自动提交数据
@Test
public void testInsert3() {
Connection conn = null;
PreparedStatement ps = null;
try {
long start = System.currentTimeMillis();
conn = JDBCUtils.getConnection();
//设置不允许自动提交数据
conn.setAutoCommit(false);
String sql = "insert into goods(name)values(?)";
ps = conn.prepareStatement(sql);
for(int i = 1;i <= 20000;i++){
ps.setObject(1, "name_" + i);
//1."攒"sql
ps.addBatch();
if(i % 500 == 0){
//2.执行batch
ps.executeBatch();
//3.清空batch
ps.clearBatch();
}
}
//提交数据
conn.commit();
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));
} catch (Exception e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource(conn, ps);
}
}