java对Clob大数据字段类型的增删改查

转载请注明出处:
http://blog.csdn.net/lishihong108/article/details/52232775

#前言
  前几天临时支援,帮pc端写几个接口,主要是对一个表的CRUD操作,虽然快两年多没写后台相关的东西了,但还不至于连一个表的基本的CRUD操作都忘了。但悲剧往往就来的这么及时,表中有一个字段是Clob类型的,以前没怎么处理过这种大字段类型,网上查了下资料,感觉写的五花八门,没有一个完整的、适用的方法。通过参照别人的出来,加上自己的实践,终于搞定。在此分享出来,希望给以后遇到同样问题的人提供一些帮助。处理Clob类型的像hibernate、mybatis框架本身也提供了一些简单的工具帮我们处理,先介绍使用最原始的的jdbc来处理。对于Blob类型的处理就不再介绍,同样处理即可。
  Clob类型的字段最多可存储2G的数据,不过超过5M的数据,我建议还是放到文件里面,数据库放对应文件的地址。至于两种方式的利弊,不是本文重点,大家自行查之。  
#实例

环境

  • oracle11g
  • tomcat 7.0
  • jdk 1.8

数据库表结构


这里写图片描述


##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类型的字段,映射:

<property name="content" type="clob">
            <column name="a_content"/>
</property>

在插入对应字段的地方创建一个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类型一样处理即可。

<result column="a_content" property="content" jdbcType="Clob"  typeHandler="org.apache.ibatis.type.ClobTypeHandler"/> 

下载地址

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值