转载请注明出处:
http://blog.csdn.net/lishihong108/article/details/52232775
#前言
前几天临时支援,帮pc端写几个接口,主要是对一个表的CRUD操作,虽然快两年多没写后台相关的东西了,但还不至于连一个表的基本的CRUD操作都忘了。但悲剧往往就来的这么及时,表中有一个字段是Clob类型的,以前没怎么处理过这种大字段类型,网上查了下资料,感觉写的五花八门,没有一个完整的、适用的方法。通过参照别人的出来,加上自己的实践,终于搞定。在此分享出来,希望给以后遇到同样问题的人提供一些帮助。处理Clob类型的像hibernate、mybatis框架本身也提供了一些简单的工具帮我们处理,先介绍使用最原始的的jdbc来处理。对于Blob类型的处理就不再介绍,同样处理即可。
Clob类型的字段最多可存储2G的数据,不过超过5M的数据,我建议还是放到文件里面,数据库放对应文件的地址。至于两种方式的利弊,不是本文重点,大家自行查之。
#实例
环境
数据库表结构
##jdbc方式
首先一个基本的jdbc连接类,还是贴下吧:
public class ConnectionUntils {
private static Connection conn;
private ConnectionUntils(){
try {
throw new Exception("can't be instance");
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getInstance(){
if(conn == null){
initConnection();
}
return conn;
}
private static void initConnection() {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");// 加载Oracle驱动程序
String url = "jdbc:oracle:" + "thin:@127.0.0.1:1521:orcl";
String user = "lsh";
String password = "lsh";
conn = DriverManager.getConnection(url, user, password);// 获取连接
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
/*
* 测试连接成功
*/
/*public static void main(String[] args) {
PreparedStatement pst = null;
ResultSet rs = null;
String sql = "select count(a_id) from article";
Connection con = getInstance();
try {
pst = con.prepareStatement(sql);
rs = pst.executeQuery();
if(rs.next()){
System.out.println(rs.getInt(1));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
close(rs,pst,con);
}
}*/
public static void close(ResultSet rs, PreparedStatement pst, Connection connection) {
try {
if(rs!=null){
rs.close();
rs=null;
}
if(pst!=null){
pst.close();
pst=null;
}
if(connection!=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
接下来是具体的增删改查方法,先说增加,一般会插入一个空的clob到数据库对应的字段,然后锁定该列,用Write将待插入字符串写入进去。重点:这两步操作要放在同一个事务里面。具体增加的方法如下:
public boolean save(Article article){
boolean result = true;
Connection conn = ConnectionUntils.getInstance();
String sql = "insert into article values(?,?,empty_clob())";
//锁住该列,防止并发写入时候该字段同时被多次写入造成错误
String sqlClob = "select a_content from article where a_id=? for update";
PreparedStatement pst =null;
ResultSet rs = null;
Writer writer = null;
try {
conn.setAutoCommit(false);//设置不自动提交,开启事务
pst = conn.prepareStatement(sql);
pst.setInt(1,article.getId());
pst.setString(2,article.getName());
pst.executeUpdate();
pst= conn.prepareStatement(sqlClob);
pst.setInt(1, article.getId());
rs = pst.executeQuery();
CLOB clob = null;
if(rs.next()){
try {
clob = (CLOB) rs.getClob(1);
writer = clob.getCharacterOutputStream(); //拿到clob的字符输入流
writer.write(article.getContent());
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
conn.commit();
} catch (SQLException e) {
result = false;
try {
conn.rollback();//当commit或者rollback后会自动释放该列的锁定
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
conn.setAutoCommit(true);//还原
ConnectionUntils.close(rs, pst, conn);
}
return result;
}
用Junit 测试:
@Test
public void testSave(){
dao = new CrudClob();
Article article = new Article();
article.setId(1);
article.setName("水浒传");
StringBuilder str = new StringBuilder();
for(int i=0;i<6000000;i++){
str.append("我是正文,我是正文,我是正文,我是正文,我是正文");
}
article.setContent(str.toString());
boolean result = dao.save(article);
System.out.print(result);
}
article.sql里面包括表结构和刚插入的一条数据,大小411MB,在我电脑上耗时36秒左右:
接下来看update操作,update时候主要利用PreparedStatement的setClob方法:
public boolean update(int id,String content){
int result = 0;
Connection conn = ConnectionUntils.getInstance();
String sql = "update article set a_content=? where a_id=?";
PreparedStatement pst =null;
try {
CLOB clob = oracle.sql.CLOB.createTemporary(conn, false,oracle.sql.CLOB.DURATION_SESSION);
clob.setString(1L, content);
pst = conn.prepareStatement(sql);
pst.setClob(1, clob);
pst.setInt(2,id);
result = pst.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally{
ConnectionUntils.close(null, pst, conn);
}
if(result==0)
return false;
return true;
}
查询就主要是从结果集ResultSet中定位到对应的字段后,往外读:
public Article select(int id){
Article article = new Article();
Connection conn = ConnectionUntils.getInstance();
String sql = "select a_id,a_name,a_content from article where a_id = ?";
PreparedStatement pst =null;
ResultSet rs = null;
try {
pst = conn.prepareStatement(sql);
pst.setInt(1,id);
rs = pst.executeQuery();
StringBuilder builder = new StringBuilder();
if(rs.next()){
Clob clob = rs.getClob("a_content");
Reader rd = clob.getCharacterStream();
char [] str = new char[12];
while(rd.read(str) != -1) {
builder.append(new String(str));
}
article.setContent(builder.toString());
article.setId(id);
article.setName(rs.getString("a_name"));
}
} catch (SQLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
ConnectionUntils.close(rs, pst, conn);
}
return article;
}
删除的方法和普通表一样,就不在贴出。
##hibernate方式
hibernate处理挺简单的,hibernate4处理,clob类型在实体类里面对应定一个一个Clob类型的字段,映射:
在插入对应字段的地方创建一个clob即可。
Hibernate.getLobCreator(session).createBlob(str);
hibernate5就更简单了,hibernate内部做了对应的处理,我们直接当string类型一样处理即可。
@Entity
public class Article {
@Id
@Column(name="a_id")
private int id;
@Column(name="a_name")
private String name;
@Column(name="a_content")
private String content;
public int getId() {
return id;
}
}
存的时候直接设置大数据到content,直接调用session.save()即可。
##mybatis方式
mybatis处理也非常简单,没必要贴代码,只需要配置mapper映射的时候配置一个处理器就会自动帮我们转类型处理。和String类型一样处理即可。
下载地址