学习目标:解决MySQL存储大文本数据和二进制数据的问题,解决数据库批量执行SQL语句的问题。
处理大文本
当文本数据内容超过64K字节时,可以称为大文本数据,大文本数据也称为CLOB数据。在MySQL中,提供了LONGTEXT类型的字段用来存储大文本数据。
课程案例shop数据库的goods(产品表)的brief字段就是大文本数据字段,可以存储4G以内的文本数据。写入大文本数据时,需要通过I/O流的方式从文本文件中读取文本内容。
针对大文本数据,PreparedStatement接口提供了如下几个方法用于写入大文本数据:
● void setAsciiStream(int parameterIndex, InputStream x, int length)
该方法将指定的输入流写入数据库文本字段中。参数parameterIndex表示字段所在记录的列号。参数InputStream为输入流。参数length表示输入流的长度。
● void setBinaryStream(int parameterIndex, InputStream x, int length)
该方法将指定的输入流写入数据库的二进制字段中。参数parameterIndex表示字段所在记录的列号。参数InputStream为输入流。参数length表示输入流的长度。
大文本数据写入数据库后,在查询时需要使用ResultSet接口将数据读出来。ResultSet接口提供了如下几个方法用于读取大文本数据:
● InputStream getAsciiStream(int columnIndex)
该方法根据记录的列编号返回文本输入流。参数columnIndex表示结果集当前光标所在行的列号。
● InputStream getAsciiStream(String columnLabel)
该方法根据记录的列名称返回文本输入流。参数columnLabel表示结果集当前光标所在行的列名称。
● Clob getClob(int columnIndex)
该方法根据记录的列编号返回Clob对象。参数columnIndex表示结果集当前光标所在行的列号。
● Clob getClob(String columnLabel)
该方法根据记录的列名称返回Clob对象。参数columnIndex表示结果集当前光标所在行的列名称。
● InputStream getBinaryStream (int columnIndex)
该方法根据记录的列编号返回二进制数据输入流。参数columnIndex表示结果集当前光标所在行的列号。
● InputStream getBinaryStream(String columnLabel)
该方法根据记录的列名称返回二进制数据输入流。参数columnLabel表示结果集当前光标所在行的列名称。
● Blob getBlob(int columnIndex)
该方法根据记录的列编号返回Blob对象(二进制大数据对象)。参数columnIndex表示结果集当前光标所在行的列号。
● Blob getBlob(String columnLabel)
该方法根据记录的列名称返回Blob对象(二进制大数据对象)。参数columnIndex表示结果集当前光标所在行的列名称。
案例8:在goods表中插入一条记录,goods表的brief是LONGTEXT类型字段,该字段的值需要从文本文件读入。
在demo包下新建SqlDemoTest7类。代码如下:
package demo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class SqlDemoTest7 {
// 定义JDBC加载路径
static String jdbc = "com.mysql.cj.jdbc.Driver";
// 定义MySQL数据库的连接地址
static String mysqlurl = "jdbc:mysql://localhost:3306/shop?serverTimezone=GMT%2B8";
// 定义MySQL数据库的用户名
static String username = "root";
// 定义MySQL数据库的用户名登录密码
static String password = "~123456q";
public static void main(String[] args) throws FileNotFoundException {
Connection conn = null;
PreparedStatement prepartStatement = null;
try {
// 加载JDBC驱动
Class.forName(jdbc);
// 连接数据库
conn = DriverManager.getConnection(mysqlurl, username, password);
if (null != conn) {
System.out.println(conn);
// 向shoper表中插入一条记录
String sql = "insert into goods(id,name,categoryId,photo,shoperId,price,status,createDate,brief)"
+ "values(?,?,?,?,?,?,?,?,?)";
// 实例化PreparedStatement对象
prepartStatement = conn.prepareStatement(sql);
// 设置id字段的值
prepartStatement.setString(1,"0001");
// 设置name字段的值
prepartStatement.setString(2,"电脑");
// 设置categoryId字段的值
prepartStatement.setString(3,"010");
// 设置photo字段的值
prepartStatement.setString(4,"");
// 设置photo字段的值
prepartStatement.setString(5,"01");
// 设置price字段的值
prepartStatement.setDouble(6, 0.0);
// 设置status字段的值
prepartStatement.setInt(7, 0);
// 设置createDate字段的值
java.util.Date date = new java.util.Date();
java.sql.Date sqldate = new java.sql.Date(date.getTime());
prepartStatement.setDate(8,sqldate);
// 定义一个File对象,准备从文件中读取数据
File file = new File("d:" + File.separator + "temp.txt");
InputStream input = new FileInputStream(file);
prepartStatement.setAsciiStream(9, input, file.length());
// 执行SQL语句
int row = prepartStatement.executeUpdate();
if( row == 1 )
{
System.out.println("goods表添加记录成功");
}
else
{
System.out.println("goods表添加记录失败");
}
prepartStatement.close();
conn.close();
} else {
System.out.println("数据库连接失败");
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
执行大文本数据的写入必须使用PreparedStatement接口,PreparedStatement接口的setDate()方法和Statement接口的setDate()方法有所不同,它需要传入java.sql.Date类型的对象。
程序使用FileInputStream将文本文件读取进来,然后通过PreparedStatement接口的setAsciiStream()方法将文件内容写入到对应的大文本字段中。
大文本文件写入完成后,在读取数据库记录时,可以使用ResultSet接口的读取大文本数据的方法将文本内容读取出来。
处理二进制数据
二进制数据不能使用文本编辑器查看内容,需要专门读取二进制数据的应用程序查看。例如读取图片文件时,读取图片的程序需要了解该文件的结构,并解释读取的数据,如果不了解该图片文件的结构,读取图片文件就会失败,也就无法把图片显示出来了。
在MySQL中,提供了LONGBLOB类型的字段用来存储二进制数据,可以存储4G以内的二进制数据。除此之外,还提供了BLOB、MediumBlob、TinyBlob字段来存储二进制数据,BLOB用于存储65K以内的二进制数据,MediumBlob用于存储16M以内的二进制数据,TinyBlob用来存储255字节以内的二进制数据。
JDBC提供了Blob接口来封装对MySQL二进制字段类型的存取封装操作,Blob接口包含了getBinaryStream()方法,用于获取InputStream输入流。
课程案例shop数据库的user(用户表)的photo字段就是LONGBLOB字段,可以存储4G以内的图文文件。写入二进制数据时,需要通过I/O流的方式从图片文件中读取数据。
案例8:在user表中插入一条记录,user表的photo是LONGBLOB类型字段,该字段的值需要从图片文件读入。
在demo包下新建SqlDemoTest8类。代码如下:
package demo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class SqlDemoTest8 {
// 定义JDBC加载路径
static String jdbc = "com.mysql.cj.jdbc.Driver";
// 定义MySQL数据库的连接地址
static String mysqlurl = "jdbc:mysql://localhost:3306/shop?serverTimezone=GMT%2B8";
// 定义MySQL数据库的用户名
static String username = "root";
// 定义MySQL数据库的用户名登录密码
static String password = "~123456q";
public static void main(String[] args) throws FileNotFoundException {
Connection conn = null;
PreparedStatement prepartStatement = null;
try {
// 加载JDBC驱动
Class.forName(jdbc);
// 连接数据库
conn = DriverManager.getConnection(mysqlurl, username, password);
if (null != conn) {
System.out.println(conn);
// 向user表中插入一条记录
String sql = "insert into user(id,name,loginName,psw,email,createDate,userType,photo)"
+ "values(?,?,?,?,?,?,?,?)";
// 实例化PreparedStatement对象
prepartStatement = conn.prepareStatement(sql);
// 设置id字段的值
prepartStatement.setString(1,"0002");
// 设置name字段的值
prepartStatement.setString(2,"张三");
// 设置loginName字段的值
prepartStatement.setString(3,"zhansan");
// 设置psw字段的值
prepartStatement.setString(4,"123456");
// 设置email字段的值
prepartStatement.setString(5,"zhangsan@163.com");
// 设置createDate字段的值
java.util.Date date = new java.util.Date();
java.sql.Date sqldate = new java.sql.Date(date.getTime());
prepartStatement.setDate(6,sqldate);
// 设置userType字段的值
prepartStatement.setInt(7,0);
// 定义一个File对象,准备从图片文件中读取数据
File file = new File("d:" + File.separator + "head.png");
InputStream input = new FileInputStream(file);
prepartStatement.setBinaryStream(8, input, file.length());
// 执行SQL语句
int row = prepartStatement.executeUpdate();
if( row == 1 )
{
System.out.println("user表添加记录成功");
}
else
{
System.out.println("user表添加记录失败");
}
prepartStatement.close();
conn.close();
} else {
System.out.println("数据库连接失败");
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
SqlDemoTest9程序代码和SqlDemoTest7程序
代码基本相同,SqlDemoTest9使用FileInputStream将图片文件读取进来,然后通过PreparedStatement接口的setBinaryStream ()方法将图片文件内容写入到对应的二进制字段中。
批处理操作
PreparedStatement接口的addBatch()方法,可以将SQL更新语句的一组参数添加到SQL命令中,对SQL更新语句涉及的表中记录进行批量更新。数据库记录的批处理更新需要addBatch()方法和executeBatch()方法共同完成。
案例9:使用SQL语句批处理操作在shoper表中插入多个店铺。
在demo包下新建SqlDemoTest9类。代码如下:
package demo;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class SqlDemoTest9 {
// 定义JDBC加载路径
static String jdbc = "com.mysql.cj.jdbc.Driver";
// 定义MySQL数据库的连接地址
static String mysqlurl = "jdbc:mysql://localhost:3306/shop?serverTimezone=GMT%2B8";
// 定义MySQL数据库的用户名
static String username = "root";
// 定义MySQL数据库的用户名登录密码
static String password = "~123456q";
public static void main(String[] args) {
Connection conn = null;
PreparedStatement prepareStatement = null;
try {
// 加载JDBC驱动
Class.forName(jdbc);
// 连接数据库
conn = DriverManager.getConnection(mysqlurl, username, password);
if (null != conn) {
System.out.println(conn);
String name = "打印机专卖店";
String brief = "打印机专业销售";
// 向shoper表中插入条记录
String sql = "insert into shoper(id,userId,name,brief,log,shopMoney,status,createDate)"
+ "values(?,?,?,?,?,?,?,?)";
// 实例化Statement对象
prepareStatement = conn.prepareStatement(sql);
// 批处理设置SQL语句更新参数
for( int i = 0; i < 10; i++ )
{
// 设置id字段的值
prepareStatement.setString(1,"0000" + i);
// 设置userId字段的值
prepareStatement.setString(2,"uoo" + i);
// 设置name字段的值
prepareStatement.setString(3,"店铺-" + i);
// 设置brief字段的值
prepareStatement.setString(4,"店铺结束-" + i);
// 设置log字段的值
prepareStatement.setString(5,"log" + i);
// 设置shopMoney字段的值
prepareStatement.setDouble(6,0.0);
// 设置status字段的值
prepareStatement.setInt(7, 0);
// 设置日期字段
java.util.Date date = new java.util.Date();
java.sql.Date sqldate = new java.sql.Date(date.getTime());
prepareStatement.setDate(8,sqldate);
prepareStatement.addBatch();
}
// 执行批处理
int row[] = prepareStatement.executeBatch();
System.out.println("更新了" + row.length + "条记录");
prepareStatement.close();
conn.close();
} else {
System.out.println("数据库连接失败");
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
SqlDemoTest9程序在shoper表中插入10条记录。程序使用for循环,将10条记录的SQL插入命令,通过addBatch()方法添加到PreparedStatement对象的批处理命令中。最后通过executeBatch()方法执行批处理命令。
批处理执行完成后,数据库shoper表被插入了10条记录。