Lob 实际上有 2 种类型,分别是用于存储大文本的字段 Clob 和存储二进制大量数据的字段 Blob 。不同的数
据库处理方式略有区别,大字段一般都是采用流的方式进行处理
由于大字段类型存储的数据较多,一般针对 lob 字段会采用分表存储的方式实现
需求:存储学生信息,要求存储学生的照片
将学生照片存储在文件系统中,实际数据中只存储照片的路径
如果图片要求加密,存储在文件系统中则不合理
可以利用 DBMS 提供的压缩加密功能将数据存储在数据表
如果具体存储 ?
一个表中存储相关的基本信息,非大字段数据
一个表中存储大字段数据
Clob
Clob 在 MySQL 种的数据类型
tinytext 最大存放 255 的字符
text 最大存放 65535 个字符
mediumtext 最大存放 16M 的字符
longtext 最大存放 4G 的字符
表的创建
-- 学生的基本信息create table tb_student(id bigint primary key auto_increment,name varchar ( 20 ));-- 学生表对应的大字段数据create table tb_memo(id bigint -- 具体实现中将其定义为外键,并且支持级联删除memo longtext);-- 为了测试方便,所以将两个表合在一起create table tb_student(id bigint primary key auto_increment,name varchar ( 20 ),memo longtext);
使用JDBC向表中插入数据
//JDK1.8 支持 JDBC4.2 ,所以只要驱动支持,加载驱动的操作可以省略/*目前可以使用的驱动有 2 种,一个 com.mysql.jdbc.Driver 是针对 MySQL8 以前的老版本的驱动,官方 已经不再建议使用;如果使用MySQL5.7+ 的数据库则建议使用新版本的驱动com.mysql.cj.jdbc.Driver*/try (Connection conn = DriverManager . getConnection ( "jdbc:mysql:///test?serverTimezone=UTC" , "root" , "123456" );Reader r = new BufferedReader ( new FileReader ( "memo.txt" ));) {PreparedStatement ps = conn . prepareStatement ( "insert into tb_studentvalues(null,?,?)" );ps . setString ( 1 , " 张毅 " );ps . setClob ( 2 , r );int len = ps . executeUpdate ();if ( len > 0 )System . out . println ( " 插入成功 !" );elseSystem . out . println ( " 插入失败 !" );}
如何判断是否支持不用加载驱动

使用JDBC从表中读取大对象类型字段
PreparedStatement ps = conn . prepareStatement ( "select * from tb_student whereid=?" );ps . setLong ( 1 , 1 );ResultSet rs = ps . executeQuery ();if ( rs . next ()) {Long id = rs . getLong ( "id" );String name = rs . getString ( "name" );Clob clob = rs . getClob ( "memo" );Reader r = clob . getCharacterStream ();System . out . println ( id + "\t" + name );char [] buffer = new char [ 8192 ];int len = 0 ;while (( len = r . read ( buffer )) > 0 ){System . out . print ( new String ( buffer , 0 , len ));}r . close ();}
Blob
Blob 是二进制大字段类型,一般用于存储图形、声音、视频等二进制序列数据。 MySQL 针对二进制大对象提供了4 种数据类型
tinyblob 上限 255
blob 上限 65535
mediumblob 上限 16M
longblob 上限 4G
表定义: 针对大对象类型的字段必须分表存储
alter table tb_student add image longblob ;
使用JDBC插入二进制大对象数据
InputStream is = new BufferedInputStream ( newFileInputStream ( "pic/xiaopang.jpg" ));PreparedStatement ps = conn . prepareStatement ( "update tb_student set image=?where id=?" );ps . setBlob ( 1 , is );ps . setLong ( 2 , 1 );int len = ps . executeUpdate ();
使用JDBC获取二进制大对象数据
PreparedStatement ps = conn . prepareStatement ( "select * from tb_student whereid=?" );ps . setLong ( 1 , 1 );ResultSet rs = ps . executeQuery ();if ( rs . next ()) {Long id = rs . getLong ( "id" );String name = rs . getString ( "name" );String memo = rs . getString ( "memo" );System . out . println ( id + "\t" + name + "\t" + memo );Blob image = rs . getBlob ( "image" );InputStream is = image . getBinaryStream ();OutputStream os = new FileOutputStream ( "c:\\" + name + ".jpg" );byte [] buffer = new byte [ 8192 ];int len = 0 ;while (( len = is . read ( buffer )) > 0 )os . write ( buffer , 0 , len );os . close ();is . close ();}
操作方法补充
JDBC 指 Java 数据库连接,是一种标准 Java 应用编程接口 JAVA API ,用来连接 Java 编程语言和广泛的数据库
通过 JDBC 往数据库的表格中添加一条记录,其中有一个字段是自增的 id bigint primary key
auto_increment ,那么在 JDBC 这边怎么在添加之后直接获取到这个自增的值。
PreparedStatement是Statement的子接口。Statement接口中有一些常量值,如
Statement.RETURN_GENERATED_KEYS
要先添加后获取到自增的 key 值:
(1) PreparedStatement pst =
conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
(2)添加 sql 执行完成后 , 通过 PreparedStatement 的对象调用 getGeneratedKeys() 方法来获取自增长键值,遍历结果集 ResultSet rs = pst.getGeneratedKeys();
try ( Connection conn = DriverManager . getConnection ( "jdbc:mysql:///test?serverTimezone=UTC" , "root" ,"123456" );) {String sql = "insert into tb_student(name) values(?)" ;// 默认不返回生成的 key 值,如果需要返回自增长的主键值,则需要添加一个常量值作为参数。默认值NO_GENERATED_KEYSPreparedStatement ps = conn . prepareStatement ( sql ,Statement . RETURN_GENERATED_KEYS );ps . setString ( 1 , " 赵小胖 2" );int pos = ps . executeUpdate ();if ( pos > 0 ) {System . out . println ( " 插入成功 !" );// 如果插入成功则会返回插入数据对应的主键值// 从 ps 中获取到服务器端返回的键值ResultSet rs = ps . getGeneratedKeys (); // 因为这里的 key 值可能多个,因为 insert 语句可以同时添加多行,所以用 ResultSet 封装// 这里因为只添加一条,所以用 if 判断if ( rs . next ()){Object key = rs . getObject ( 1 );System . out . println ( " 自增的 key 值 id =" + key );}rs . close ();} elseSystem . out . println ( " 插入失败 !" );ps . close ();}
批处理
批处理:批量处理 sql 。例如 1 )订单明细表的多条记录的添加。 2 )批量添加模拟数据 等不用批处理,和用批处理有什么不同?批处理的效率会好很多,如何进行批处理操作
ps . addBatch ();int [] all = ps . executeBatch ();
try(Connection conn=DriverManager.getConnection("jdbc:mysql:///test?
serverTimezone=UTC" , "root" , "123456" );) {String sql = "insert into tb_student(name) values(?)" ;PreparedStatement ps = conn . prepareStatement ( sql );// 一次性插入 100 条数据for ( int i = 0 ; i < 100 ; i ++ ) {ps . setString ( 1 , " 张 _" + i );ps . addBatch (); // 添加到批处理一组操作中,攒一块处理// 如果一次性提交的数据量过大,允许部分提交if ( i % 10 == 0 ){int [] res = ps . executeBatch ();System . out . println ( i + ":" + Arrays . toString ( res ));ps . clearBatch ();}}int [] pos = ps . executeBatch (); // 执行批处理,返回各个对应处理的结果System . out . println ( Arrays . toString ( pos ));ps . close ();}
如果批处理优势不明显,则没有必要使用批处理了,因为限制太多了
可滚动结果集
默认情况下 ResultSet 结果集是单向的,也就是只能通过 rs.next 方法单向的读取数据;如果在读取数据库中的记录时不用逐个逐个的读取,而可以根据具体需要跳过几条记录再进行读取,或者从后向前读取。
PreparedStatement prepareStatement ( String sql , int resultSetType , intresultSetConcurrency ) throws SQLException ;
参数 1 为要指定的 sql 语句
参数 2 为结果集的类型,可以使用的 ResultSet 接口中的 3 个常量值
TYPE_SCROLL_INSENSITIVE 可滚动不敏感
TYPE_SCROLL_SENSITIVE 可滚动敏感的
TYPE_FORWARD_ONLY 单向滚动,只能调用 next
参数 3 用于设置结果集的并发性支持
CONCUR_READ_ONLY 并发访问时只读。其中 concur 译作同时发生,即并发,即当并发访问
结果集的时候只能够只读访问,也就是只能读取里面的内容,不能做更改
CONCUR_UPDATABLE 访问的同时允许修改
要做到这点就是在创建 Statement 对象时使用两个属性做参数 Statement stmt =
conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)
try ( Connection conn = DriverManager . getConnection ( "jdbc:mysql:///testserverTimezone=UTC" , "root" , "123456" )) {PreparedStatement ps = conn . prepareStatement ( "select * from tb_student" ,ResultSet . TYPE_SCROLL_INSENSITIVE ,ResultSet . CONCUR_READ_ONLY );ResultSet rs = ps . executeQuery ();rs . last (); // 将行指针指向到最后一行do {long id = rs . getLong ( "id" );String name = rs . getString ( "name" );System . out . println ( id + "\t" + name );} while ( rs . previous ());rs . close ();ps . close ();}
获取一个可滚动结果集 ResultSet 引用 rs 之后,就可以使用结果集的方法使行指针任意滚动的方法
rs.last() // 使游标指向最后一行记录
rs.isLast() // 判断是否指向最后一行记录
rs.isAfterLast() // 判断是否指向最行一行记录的上面一行,即倒数第二行
rs.getRow() // 返回当前游标所指向的是第几行
rs.previous() // 使游标指向当前所指向的行的上一行
rs.absolute(int x) // 使游标直接指向第 x 行
版权声明:本文为m0_56627229原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。