使用達(dá)夢(mèng)數(shù)據(jù)庫(kù)的大字段前不得不說(shuō)一下數(shù)據(jù)庫(kù)大字段的性能問(wèn)題:在數(shù)據(jù)庫(kù)中,經(jīng)常需要用到大字段類型,如Oracle中l(wèi)ong、blob、clob,SQLServer中的text、image,MySql中的text、longtext、clob、blob以及達(dá)夢(mèng)數(shù)據(jù)庫(kù)中的clob、blob類型。存儲(chǔ)的信息大概主要是兩類,一類是長(zhǎng)文本,如大段的文字,普通的varchar最長(zhǎng)只能存儲(chǔ)4K的數(shù)據(jù),已經(jīng)不能滿足要求;另一類是存儲(chǔ)二進(jìn)制信息,如上傳的文件等。不過(guò)通常情況下,大字段不意味著保存很大的文件,例如很長(zhǎng)的文章,圖標(biāo),小圖片等等。數(shù)據(jù)過(guò)大保存在數(shù)據(jù)庫(kù)有諸多的問(wèn)題:
1.速度慢:影響一張表的查詢速度的,除了行數(shù),還包括表所占的物理空間的大小。此表在數(shù)據(jù)量較小時(shí),在查詢方面感覺不到明顯的差異。但是如果某個(gè)字段所存儲(chǔ)的數(shù)據(jù)都是大段文本或較大的文件時(shí),會(huì)導(dǎo)致表的物理空間迅速變大,該字段所占用的空間有可能達(dá)到整表所占空間的90%以上。在此基礎(chǔ)上,如果行數(shù)再增加到數(shù)十萬(wàn)、上百萬(wàn)級(jí)時(shí),整個(gè)表所占的空間將達(dá)到一個(gè)驚人的數(shù)字,查詢的速度亦會(huì)受到非常大的影響。
2.操作不方便:必須把數(shù)據(jù)庫(kù)打開一個(gè)流,構(gòu)造一個(gè)Buffer,然后再輸出一個(gè)ServletOutputStream。占用數(shù)據(jù)庫(kù)連接,加重?cái)?shù)據(jù)庫(kù)訪問(wèn)負(fù)載不說(shuō),如果用戶突然中斷下載,還需要處理數(shù)據(jù)庫(kù)關(guān)閉動(dòng)作,容易造成性能問(wèn)題。如果把整個(gè)數(shù)據(jù)讀入內(nèi)存再輸出,則內(nèi)存占用非常可觀。如果是硬盤文件,只要返回一個(gè)URL 就可以了。即使你不希望用戶直接訪問(wèn)到文件,你也可以構(gòu)造一個(gè)IOStream來(lái)輸出文件,既不會(huì)占用數(shù)據(jù)庫(kù)資源,傳輸速度也快。
3.性能有問(wèn)題:特別的情況是,如果并發(fā)很多用戶來(lái)下載大文件的時(shí)候,應(yīng)用服務(wù)器要占用非常多的內(nèi)存來(lái)緩存文件內(nèi)容,假設(shè)并發(fā)10個(gè)用戶,下載10MB的文件,JVM的峰值就至少需要100MB內(nèi)存來(lái)支撐,很容易造成JVM崩潰。
所以說(shuō)數(shù)據(jù)庫(kù)大字段并不適合存儲(chǔ)過(guò)大的數(shù)據(jù),數(shù)據(jù)過(guò)大可能會(huì)影響到數(shù)據(jù)庫(kù)存儲(chǔ)的性能。
下面言歸正傳,使用Hibernate操作達(dá)夢(mèng)數(shù)據(jù)庫(kù)中的大字段應(yīng)該有如下幾步:
1 首先需要一張表存儲(chǔ)大字段數(shù)據(jù):包括內(nèi)容,類型;
2 必須得到一個(gè)代表上傳文件的數(shù)據(jù)流;
3 進(jìn)行保存操作
好了我們先建一張表,里面包括達(dá)夢(mèng)數(shù)據(jù)庫(kù)的2個(gè)大字段類型(CLOB、BLOB)字段:
--創(chuàng)建表
create table TESTLOB(
ID int primary key,
TITLE varchar(50),
CLOBNAME varchar(50),
CLOBCONTENT clob,
BLOBNAME varchar(50),
BLOBCONTENT blob
);
在建立一個(gè)序列用于處理表的流水ID:
--創(chuàng)建序列
create SEQUENCE "TESTLOB"."SEQ_TESTLOB_ID"
INCREMENT BY 1 START WITH 1 MAXVALUE 100000 MINVALUE 1
NOCYCLE
然后編寫關(guān)于這2個(gè)大字段的Java對(duì)象文件TestLob.java,分別定義類型為CLOB和BLOB屬性字段為String和byte[]類型,其中由于CLOB是處理大文本類型所以它對(duì)應(yīng)了Java中的String類型,BLOB是處理一些以二進(jìn)制流形勢(shì)存儲(chǔ)的沒有嚴(yán)ge定義的大文件所以讓它使用byte[]類型,然后分別定義這2個(gè)屬性的Getter和Setter方法,相關(guān)代碼如下:
package com.dm.lobtest.vo;
public class TestLob {
private int id;
private String title; //記錄標(biāo)題
private String clobName; //clob文件名稱
private String clobContent; //clob文件
private byte[] blobContent; //blob文件
private String blobName; //blob文件名稱
public byte[] getBlobContent() {
return blobContent;
}
public void setBlobContent(byte[] blobContent) {
this.blobContent = blobContent;
}
public String getClobContent() {
return clobContent;
}
public void setClobContent(String clobContent) {
this.clobContent = clobContent;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBlobName() {
return blobName;
}
public void setBlobName(String blobName) {
this.blobName = blobName;
}
public String getClobName() {
return clobName;
}
public void setClobName(String clobName) {
this.clobName = clobName;
}
}
將對(duì)象的映射文件中主健字段元素的內(nèi)置Generator指定為sequence,并與達(dá)夢(mèng)數(shù)據(jù)庫(kù)重的內(nèi)置序列(sequence)關(guān)聯(lián),最后指定CLOB類型的屬性為"string"類型,BLOB類型的屬性為"binary"類型:
<?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.dm.lobtest.vo.TestLob" table="TESTLOB">
<id name="id" column="ID" type="int">
<generator class="sequence">
<param name="sequence">SEQ_TESTLOB_ID</param>
</generator>
</id>
<property name="title" column="TITLE" type="string" />
<property name="clobName" column="CLOBNAME" type="string" />
<property name="blobName" column="BLOBNAME" type="string" />
<property name="clobContent" column="CLOBCONTENT" type="string" />
<property name="blobContent" column="BLOBCONTENT" type="binary" />
</class>
</hibernate-mapping>
完成以上這些操作后我們可以開始編寫上傳文件頁(yè)面和處理上傳文件的業(yè)務(wù)邏輯了,
定義上傳文件頁(yè)面:
<form name="myTestLobFrm" method="post" enctype="multipart/form-data" action="/LobTest/testlob.do?method=save">
<table align="center">
<tr>
<td colspan="2">
<b>達(dá)夢(mèng)數(shù)據(jù)庫(kù)CLOB和BLOB數(shù)據(jù)類型在Hibernate下使用的示例</b>
</td>
</tr>
<tr>
<td>標(biāo)題:</td>
<td>
<input type="text" name="title" size="81">
</td>
</tr>
<tr>
<td>文件內(nèi)容:</td>
<td>
<textarea rows="20" cols="80" name="clobTest" ></textarea>
</td>
</tr>
<tr>
<td>附件:</td>
<td>
<input type="file" name="blobTest" size="71">
</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value=" 確 認(rèn) ">
</td>
</tr>
<tr>
<td colspan="2" align="center"> <font id="info" color="red"><%=message %></font></td>
</tr>
</table>
</form>
處理業(yè)務(wù)邏輯類:
public class TestLobAction extends DispatchAction{
public ActionForward save(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception {
try{
//設(shè)置文件標(biāo)題
String title = request.getParameter("title");
TestLob testLob = new TestLob();
testLob.setTitle(title);
//處理blob字段
Hashtable files = form.getMultipartRequestHandler().getFileElements();
FormFile blobTestFile = (FormFile) files.get("blobTest");//得到文件
String blobName = blobTestFile.getFileName(); //得到文件名
testLob.setBlobName(blobName); //設(shè)置文件
byte[] blobContent=blobTestFile.getFileData(); //得到文件內(nèi)容
testLob.setBlobContent(blobContent); //設(shè)置文件內(nèi)容
//處理clob字段
String clobName = title; //文件名就是標(biāo)題名
String clobContent = request.getParameter("clobTest"); //得到文件
testLob.setClobName(clobName); //設(shè)置文件名
testLob.setClobContent(clobContent); //設(shè)置文件
TestLobService testLobService = TestLobService.getInstance();
if(testLobService.createTestLob(testLob)==0){
request.setAttribute("message", "上傳失敗");
return list(mapping,form,request,response);
}
}catch(Exception e){
throw new BaseException();
}
request.setAttribute("message", "上傳成功");
return list(mapping,form,request,response);
}
}
因?yàn)閏lob字段對(duì)應(yīng)的是String類型,所以可以直接將表單中得到的數(shù)據(jù)設(shè)置到對(duì)象的屬性中去,而blob類型處理的是二進(jìn)制類型的文件,我們需要將得到的文件以流的形式存入到對(duì)象的屬性中,這里我使用了struts的FormFile組件來(lái)進(jìn)行文件的上傳,將得到的數(shù)據(jù)流設(shè)置到對(duì)象的屬性中。
完成以上操作后我們就可以將設(shè)置好的對(duì)象用以下面的方法保存數(shù)據(jù)到數(shù)據(jù)庫(kù)中:
首先定義一個(gè)Hibernate的Session管理類:HibernateUtil.java,它是使用ThreadLocal類建立的一個(gè)Session管理的輔助類。
package com.dm.lobtest.util;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
public class HibernateUtil {
private static SessionFactory sessionFactory = null;
public static final ThreadLocal session = new ThreadLocal();
public static Session currentSession() throws HibernateException {
if (sessionFactory == null) {
if (getSystemSessionFactory() == false) {
throw new HibernateException(
"Exception geting SessionFactory ! ");
}
}
Session s = (Session) session.get();
// 如果該線程還不存在,打開一個(gè)新的Session
if (s == null) {
s = sessionFactory.openSession();
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
session.set(null);
if (s != null)
s.close();
}
}
最后利用Hibernate的運(yùn)作中心--Session接口的save()方法保存對(duì)象并提交相關(guān)的Session實(shí)例:
public int createTestLob(TestLob testLob){
Session session = null;
try{
session = HibernateUtil.currentSession();
Transaction tr = session.beginTransaction();
session.save(testLob);
tr.commit();
return 1;
}catch(Exception e){
e.printStackTrace();
return 0;
}finally {
try {
if (session != null) {
HibernateUtil.closeSession();
}
}catch (Exception ex) {}
}
}
現(xiàn)在我們已經(jīng)利用Hibernate完成了多個(gè)不同的大數(shù)據(jù)類型存儲(chǔ)到達(dá)夢(mèng)數(shù)據(jù)庫(kù)中的全過(guò)程。
新聞熱點(diǎn)
疑難解答