❶ 求struts和hibernate實現圖片上傳保存到資料庫中,並可以在頁面顯示出來,並顯示上傳人的名字的代碼啊
struts.xml中
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<constant name="sruts.il8n.encoding" value="UTF-8" />
<!--<constant name="struts.multipart.maxSize" value="102400" />
<constant name="struts.multipart.saveDir" value="d:/upload" /> -->
<constant name="struts.ui.theme" value="simple"></constant>
<package name="struts" extends="struts-default" namespace="/">
<action name="add2" class="DomeWeb2">
<!-- 限制圖片的格式和圖片的大小 -->
<interceptor-ref name="fileUpload">
<param name="allowedTypes">
image/bmp,image/png,image/gif,image/jpeg,image/pjpeg
</param>
</interceptor-ref>
<!-- 默認的攔截器,必須要寫 -->
<interceptor-ref name="defaultStack" />
<result name="input">add2.jsp</result>
<result name="success">showUpload1.jsp</result>
</action> </package>
</struts>
applicationContext.xml中
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="com.microsoft.sqlserver.jdbc.SQLServerDriver">
</property>
<property name="url" value="jdbc:sqlserver://localhost:1433;databaseName=bookshop">
</property>
<property name="username" value="sa"></property>
<property name="password" value="chen"></property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.SQLServerDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>entity/Dome.hbm.xml</value></list>
</property></bean>
<bean id="DomeDAO" class=".impl.DomeDaoImpl">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<bean id="DomeWeb2" class="web.UploadOneAction">
<property name="domeBiz" ref="DomeBiz"></property>
</bean>
<!-- 配置事務 -->
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" propagation="REQUIRED"
read-only="true" />
<tx:method name="find*" propagation="REQUIRED"
read-only="true" />
<tx:method name="search*" propagation="REQUIRED"
read-only="true" />
<tx:method name="query*" propagation="REQUIRED"
read-only="true" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="submit*" propagation="REQUIRED" />
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="modify*" propagation="REQUIRED" />
<tx:method name="check*" propagation="REQUIRED" />
<tx:method name="do*" propagation="REQUIRED" />
<tx:method name="*" propagation="REQUIRED" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceMethod"
expression="execution(* biz.impl.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
</aop:config>
</beans>
UploadOneAction.java類中
package web;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import org.apache.struts2.ServletActionContext;
import biz.DomeBiz;
import com.opensymphony.xwork2.ActionSupport;
import entity.Dome;
public class UploadOneAction extends ActionSupport {
private static final long serialVersionUID = 572146812454l;
private static final int BUFFER_SIZE = 16 * 1024;
private File myFile; //文件流
private String contentType; //內容類型
private String fileName; //文件名
private String imageFileName; //新文件名
private String caption; //標題
DomeBiz domeBiz ;
public void setDomeBiz(DomeBiz domeBiz) {
this.domeBiz = domeBiz;
}
private static void (File src, File dst) {
try {
InputStream in = null;
OutputStream out = null;
try {
in = new BufferedInputStream(new FileInputStream(src),
BUFFER_SIZE);
out = new BufferedOutputStream(new FileOutputStream(dst),
BUFFER_SIZE);
byte[] buffer = new byte[BUFFER_SIZE];
while (in.read(buffer) > 0) {
out.write(buffer);
}
} finally {
if (null != in) {
in.close();
}
if (null != out) {
out.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static String getExtention(String fileName) {
int pos = fileName.lastIndexOf(".");
return fileName.substring(pos);
}
public String execute() {
if (myFile == null)
return INPUT;
System.out.println(this.getMyFileFileName()+"123");
imageFileName=new Date().getTime()+ getExtention(this.getMyFileFileName());
//得到圖片保存的位置(根據root來得到圖片保存的路徑在tomcat下的該工程里)
File imageFile = new File(ServletActionContext.getServletContext().getRealPath("images")+ "/" + imageFileName);
(myFile, imageFile); //把圖片寫入到上面設置的路徑里
Dome dome = new Dome();
dome.setPicture(this.getImageFileName());
domeBiz.save(dome);
return SUCCESS;
}
//得到原文件名稱
public String getMyFileFileName() {
return fileName;
}
public void setMyFileFileName(String fileName) {
this.fileName = fileName;
}
public File getMyFile() {
return myFile;
}
public void setMyFile(File myFile) {
this.myFile = myFile;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getImageFileName() {
return imageFileName;
}
public void setImageFileName(String imageFileName) {
this.imageFileName = imageFileName;
}
public String getCaption() {
return caption;
}
public void setCaption(String caption) {
this.caption = caption;
}
}
add2.jsp頁面
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>上傳</title>
</head>
<body>
<s:form action="add2.action" method="POST"
enctype="multipart/form-data">
<s:fielderror />
<s:file name="myFile" label="Image File1" />
<s:textfield name="caption" label="Caption" />
<s:submit />
</s:form>
</body>
</html>
showUpload1.jsp頁面
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>顯示圖片</title>
</head>
<body>
<div
style="padding: 3px; border: solid 1px #cccccc; text-align: center">
<img src='images/<s:property value ="imageFileName" /> ' />
<br />
<s:property value="caption" />
</div>
<s:property value="caption" />
</body>
</html>
❷ hibernate如何保存blob數據
首先你得搞清楚一點BLOB是二進制大對象,是ORACLE的數據類型,它對應到java中有兩種方式:
byte[] 和java.sql.Blob(先搞清楚這重點哦)
我給你直接復制重點代碼,希望可以幫到你
1 資料庫中定義成BLOB類型,這個你自己定義吧(表名叫bigobject),~~~~~不過給你截個圖吧
2 綜上,把oracle資料庫中的BLOB映射到java中有兩種情況的,即java.sql.Blob和byte[],下面先說byte[]的映射
++++++++++++++++++++++++Bigobject.hbm.xml映射文件+++++++++++++++++++++++
<hibernate-mapping>
<class name="entity.Bigobject" table="BIGOBJECT" >
<id name="id" type="java.lang.Integer">
<column name="ID" precision="6" scale="0" />
<generator class="native" />
</id>
<property name="tclob" type="java.lang.String">
<column name="TCLOB" />
</property>
<property name="tblob" type="byte[]"> //!!!!!注意,這里是byte[]
<column name="TBLOB" />
</property>
</class>
</hibernate-mapping>
++++++++++++++++++++以下是bigobject實體類(用hibernate映射的)+++++++++++
public class Bigobject implements java.io.Serializable {
// Fields
private Integer id;
private String tclob;
private byte[] tblob; //!!!!!注意,這里是byte[]
// Constructors
/** default constructor */
public Bigobject() {
}
/** full constructor */
public Bigobject(String tclob, byte[] tblob) {
this.tclob = tclob;
this.tblob = tblob;
}
// Property accessors
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTclob() {
return this.tclob;
}
public void setTclob(String tclob) {
this.tclob = tclob;
}
public byte[] getTblob() {
return this.tblob;
}
public void setTblob(byte[] tblob) {
this.tblob = tblob;
}
========================控制台測試代碼(讀取圖片到資料庫,
再從資料庫讀取圖片到特定路徑下)=======================================
/**
* 按大對象數據類型BLOB的byte[]類型
* CLOB的java.lang.String
* 映射 並插入數據
* @author Administrator
*
*/
public class Test {
Session session=null;
Transaction tx=null;
/**
* 持久化數據,讀取本地圖片到資料庫
*/
public void get1(){
try {
session=HibernateSessionFactory.getSession();
//前提是文件必須放在src路徑下,讀取的是當前項目的根目錄
// InputStream input=this.getClass().getResourceAsStream("/file.txt");
//載入任意路徑下的圖片、大文件、視屏等(括弧里的參數圖片是絕對路徑)
InputStream input=new FileInputStream("G:/在線拍賣/page/images/gou1.jpg");
tx=session.beginTransaction();
byte[] byteArray=new byte[input.available()];
input.read(byteArray);
input.close();
Bigobject b=new Bigobject();
b.setId(1);
b.setTblob(byteArray);
b.setTclob("一條狗");
session.save(b);
tx.commit();
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 從資料庫bigObject表中按主鍵讀取一條數據
* @throws IOException
*/
public void get2() throws IOException{
session=HibernateSessionFactory.getSession();
Bigobject b=(Bigobject)session.get(Bigobject.class, 1);
System.out.println("文本內容是:"+b.getTclob());
//吧位元組數組數據通過位元組流,輸出到當前工程根目錄下2.jpg中
if(b.getTblob()!=null){
FileOutputStream out=new FileOutputStream("dog1.jpg");
out.write(b.getTblob());
out.close();
}
}
public static void main(String[] args) throws IOException {
test6 t=new test6();
t.get1();
t.get2();
}
===================hibernate.cfg.xml的代碼頁給你貼一下吧,不過這都是自動生成的,你自己動手生成吧,一下是我自己的,想用的話得改參數的=======================
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
<session-factory>
<property name="dialect">
org.hibernate.dialect.Oracle9Dialect
</property>
<property name="connection.url">
jdbc:oracle:thin:@localhost:1521:accp7 //!!!資料庫名稱
</property>
<property name="connection.username">scott</property>//!!!!!用戶名
<property name="connection.password">accp</property>//!!!!!!密碼
<property name="connection.driver_class">
oracle.jdbc.OracleDriver
</property>
<property name="myeclipse.connection.profile">scott</property>//!!!!!資料庫實例名
<mapping resource="entity/Bigobject.hbm.xml" />
</session-factory>
</hibernate-configuration>
3 上面介紹了BLOB的byte[]存儲,下面介紹另一種方式java.lang.Blob方式,還是直接粘貼代碼
++++++++++++++++++++資料庫還是上面圖片上的,保持不變++++++++++++++++++++++
====================Bigobject.hbm.xml映射文件======================
<hibernate-mapping>
<class name="bean.Bigobject" table="BIGOBJECT">
<id name="id" type="java.lang.Integer">
<column name="ID" precision="6" scale="0" />
<generator class="native" />
</id>
<property name="tclob" type="java.sql.Clob">//這里用的是Clob的另一種方式,有疑問再問
<column name="TCLOB" />
</property>
<property name="tblob" type="java.sql.Blob">//!!!!!注意,這里是java.sql.Blob
<column name="TBLOB" />
</property>
</class>
</hibernate-mapping>
========================以下是bigobject實體類=======================
import java.sql.Blob;
import java.sql.Clob;
/**
* Bigobject entity. @author MyEclipse Persistence Tools
*/
public class Bigobject implements java.io.Serializable {
// Fields
private Integer id;
private Clob tclob;
private Blob tblob;
// Constructors
/** default constructor */
public Bigobject() {
}
/** full constructor */
public Bigobject(Clob tclob, Blob tblob) {
this.tclob = tclob;
this.tblob = tblob;
}
// Property accessors
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public Clob getTclob() {
return this.tclob;
}
public void setTclob(Clob tclob) {
this.tclob = tclob;
}
public Blob getTblob() {
return this.tblob;
}
public void setTblob(Blob tblob) {
this.tblob = tblob;
}
}
==========================對應的控制台測試代碼===========================
/**
* 講字元串大對象聲明為java.sql.Clob類型,二進制大對象聲明為java.sql.Blob類型
* @author Administrator
*
*/
public class test7 {
Session session=null;
Transaction tx=null;
public void get1(){
try {
session=HibernateSessionFactory.getSession();
//前提是文件必須放在src路徑下
InputStream input=this.getClass().getResourceAsStream("/upload.txt");
//載入任意路徑下的圖片、大文件、視屏等
// InputStream input=new FileInputStream("F:/1.jpg");
tx=session.beginTransaction();
byte[] byteArray=new byte[input.available()];
input.read(byteArray);
input.close();
Bigobject b=new Bigobject();
//依據二進制數據創建一個Blob對象 !!!!!重點 務必看清楚
b.setTblob(Hibernate.createBlob(byteArray));
//依據字元串數據創建一個Clob對象 !!!!!重點務必看清楚
b.setTclob(Hibernate.createClob("上傳圖片"));
session.save(b);
tx.commit();
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void get2(){
session=HibernateSessionFactory.getSession();
Bigobject obj=(Bigobject)session.get(Bigobject.class, 131);
//把Clob對象通過字元流讀入到內存,並輸出
try {
if(obj.getTclob()!=null){
Reader read=obj.getTclob().getCharacterStream();
char[] chArray=new char[1];
StringBuilder sb=new StringBuilder();
while(read.read(chArray)!=-1){
sb.append(new String(chArray));
}
System.out.println(sb.toString().trim());
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//把Blob對象通過位元組流讀輸出,並保存到當前工程根目錄下,取名為upload.txt
try {
if(obj.getTblob()!=null){
InputStream in=obj.getTblob().getBinaryStream();
FileOutputStream fos=new FileOutputStream("upload.txt");
int b=-1;
while((b=in.read())!=-1){
fos.write(b);
}
fos.close();
in.close();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
test7 t=new test7();
t.get1();
t.get2();
}
}
++++++++++++++++++++hibernate.cfg.xml同上++++++++++++++++++++++++++++
===================================================================
綜上,Blob與Clob(字元串大對象)的寫入讀出的兩種方法都有了(Clob的兩種方法你自己捎帶看看),都是完整代碼,粘貼即可用,整理了兩個多小時,希望對你有幫助!
❸ 怎麼把數據保存到資料庫
1.要下載一個對應你復數據制庫的驅動包,如 sqlserver2008.java; 靈魂伴侶手寫.
2.然後寫個連接資料庫的類.如JDBC.(連接資料庫方法有很多種, 按照技術來分,首先學會JDBC連接資料庫,然後連接池,然後框架技術Hibernate.) 靈魂伴侶手寫.
3.每個資料庫的表對應一張實體類,實體類是干什麼用的? 1.用它可以OOP的思想的去操作資料庫.
(增刪改查), 表中的欄位就封裝成實體類裡面的一個屬性. 如表裡是name char(10),那麼實體類對應的是private String name;
❹ 怎樣在把jsp頁面中用戶輸入的數據的通過hibernate存入到資料庫
這個問題步驟比較多
首先你要通過頁面的表單提交到後台 後台或者直接用servlet 或者用回struts 或者用Springmvc 接受到答前台的參數
你想用hibernate來將數據存到資料庫 你要引入hibernate的jar包 然後配置好hibernate的配置文件和映射文件 然後調用hibernate的保存的api即可。。。。。。。。。。。。
不知道這個基本的操作流程你是否滿意
❺ hibernate如何實現 文件上傳到指定路徑 並將路徑保存到資料庫
文件上傳步驟:
1、創建文件上傳的Action....
在其方法(execute())中寫文件上傳代碼:
try {
UploadForm uploadForm = (UploadForm) form;// TODO Auto-generated method stub
//獲得文件
FormFile file1=uploadForm.getFile1();
//獲得webroot 文件夾的絕對路勁
String path=request.getRealPath("/");
//request.getSession().getServletContext().getRealPath("/")
FileOutputStream fos=
new FileOutputStream(path+"/"+file1.getFileName());
//保存文件
fos.write(file1.getFileData());
//關流
fos.close();
response.getWriter().print("ok");
} catch (Exception e) {
e.printStackTrace();
}
return null;
2、在jsp頁面中:
1、 表單的提交方式必須為post 提交;(必須的)
2 、 給表單加上一個屬性 enctype="multipart/form-data";(必須的)
3 、在formbean 加入 FormFile 類型 如 private FormFile file1;
❻ hibernate緩存的詳細配置
很多人對二級緩存都不太了解,或者是有錯誤的認識,我一直想寫一篇文章介紹一下hibernate的二級緩存的,今天終於忍不住了。
我的經驗主要來自hibernate2.1版本,基本原理和3.0、3.1是一樣的,請原諒我的頑固不化。
hibernate的session提供了一級緩存,每個session,對同一個id進行兩次load,不會發送兩條sql給資料庫,但是session關閉的時候,一級緩存就失效了。
二級緩存是SessionFactory級別的全局緩存,它底下可以使用不同的緩存類庫,比如ehcache、oscache等,需要設置hibernate.cache.provider_class,我們這里用ehcache,在2.1中就是
hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider
如果使用查詢緩存,加上
hibernate.cache.use_query_cache=true
緩存可以簡單的看成一個Map,通過key在緩存裡面找value。
Class的緩存
對於一條記錄,也就是一個PO來說,是根據ID來找的,緩存的key就是ID,value是POJO。無論list,load還是iterate,只要讀出一個對象,都會填充緩存。但是list不會使用緩存,而iterate會先取資料庫select id出來,然後一個id一個id的load,如果在緩存裡面有,就從緩存取,沒有的話就去資料庫load。假設是讀寫緩存,需要設置:
<cache usage="read-write"/>
如果你使用的二級緩存實現是ehcache的話,需要配置ehcache.xml
<cache name="com.xxx.pojo.Foo" maxElementsInMemory="500" eternal="false" timeToLiveSeconds="7200" timeToIdleSeconds="3600" overflowToDisk="true" />
其中eternal表示緩存是不是永遠不超時,timeToLiveSeconds是緩存中每個元素(這里也就是一個POJO)的超時時間,如果eternal="false",超過指定的時間,這個元素就被移走了。timeToIdleSeconds是發呆時間,是可選的。當往緩存裡面put的元素超過500個時,如果overflowToDisk="true",就會把緩存中的部分數據保存在硬碟上的臨時文件裡面。
每個需要緩存的class都要這樣配置。如果你沒有配置,hibernate會在啟動的時候警告你,然後使用defaultCache的配置,這樣多個class會共享一個配置。
當某個ID通過hibernate修改時,hibernate會知道,於是移除緩存。
這樣大家可能會想,同樣的查詢條件,第一次先list,第二次再iterate,就可以使用到緩存了。實際上這是很難的,因為你無法判斷什麼時候是第一次,而且每次查詢的條件通常是不一樣的,假如資料庫裡面有100條記錄,id從1到100,第一次list的時候出了前50個id,第二次iterate的時候卻查詢到30至70號id,那麼30-50是從緩存裡面取的,51到70是從資料庫取的,共發送1+20條sql。所以我一直認為iterate沒有什麼用,總是會有1+N的問題。
(題外話:有說法說大型查詢用list會把整個結果集裝入內存,很慢,而iterate只select id比較好,但是大型查詢總是要分頁查的,誰也不會真的把整個結果集裝進來,假如一頁20條的話,iterate共需要執行21條語句,list雖然選擇若干欄位,比iterate第一條select id語句慢一些,但只有一條語句,不裝入整個結果集hibernate還會根據資料庫方言做優化,比如使用mysql的limit,整體看來應該還是list快。)
如果想要對list或者iterate查詢的結果緩存,就要用到查詢緩存了
查詢緩存
首先需要配置hibernate.cache.use_query_cache=true
如果用ehcache,配置ehcache.xml,注意hibernate3.0以後不是net.sf的包名了
<cache name="net.sf.hibernate.cache.StandardQueryCache"
maxElementsInMemory="50" eternal="false" timeToIdleSeconds="3600"
timeToLiveSeconds="7200" overflowToDisk="true"/>
<cache name="net.sf.hibernate.cache.UpdateTimestampsCache"
maxElementsInMemory="5000" eternal="true" overflowToDisk="true"/>
然後
query.setCacheable(true);//激活查詢緩存
query.setCacheRegion("myCacheRegion");//指定要使用的cacheRegion,可選
第二行指定要使用的cacheRegion是myCacheRegion,即你可以給每個查詢緩存做一個單獨的配置,使用setCacheRegion來做這個指定,需要在ehcache.xml裡面配置它:
<cache name="myCacheRegion" maxElementsInMemory="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" overflowToDisk="true" />
如果省略第二行,不設置cacheRegion的話,那麼會使用上面提到的標准查詢緩存的配置,也就是net.sf.hibernate.cache.StandardQueryCache
對於查詢緩存來說,緩存的key是根據hql生成的sql,再加上參數,分頁等信息(可以通過日誌輸出看到,不過它的輸出不是很可讀,最好改一下它的代碼)。
比如hql:
from Cat c where c.name like ?
生成大致如下的sql:
select * from cat c where c.name like ?
參數是"tiger%",那麼查詢緩存的key*大約*是這樣的字元串(我是憑記憶寫的,並不精確,不過看了也該明白了):
select * from cat c where c.name like ? , parameter:tiger%
這樣,保證了同樣的查詢、同樣的參數等條件下具有一樣的key。
現在說說緩存的value,如果是list方式的話,value在這里並不是整個結果集,而是查詢出來的這一串ID。也就是說,不管是list方法還是iterate方法,第一次查詢的時候,它們的查詢方式很它們平時的方式是一樣的,list執行一條sql,iterate執行1+N條,多出來的行為是它們填充了緩存。但是到同樣條件第二次查詢的時候,就都和iterate的行為一樣了,根據緩存的key去緩存裡面查到了value,value是一串id,然後在到class的緩存裡面去一個一個的load出來。這樣做是為了節約內存。
可以看出來,查詢緩存需要打開相關類的class緩存。list和iterate方法第一次執行的時候,都是既填充查詢緩存又填充class緩存的。
這里還有一個很容易被忽視的重要問題,即打開查詢緩存以後,即使是list方法也可能遇到1+N的問題!相同條件第一次list的時候,因為查詢緩存中找不到,不管class緩存是否存在數據,總是發送一條sql語句到資料庫獲取全部數據,然後填充查詢緩存和class緩存。但是第二次執行的時候,問題就來了,如果你的class緩存的超時時間比較短,現在class緩存都超時了,但是查詢緩存還在,那麼list方法在獲取id串以後,將會一個一個去資料庫load!因此,class緩存的超時時間一定不能短於查詢緩存設置的超時時間!如果還設置了發呆時間的話,保證class緩存的發呆時間也大於查詢的緩存的生存時間。這里還有其他情況,比如class緩存被程序強制evict了,這種情況就請自己注意了。
另外,如果hql查詢包含select字句,那麼查詢緩存裡面的value就是整個結果集了。
當hibernate更新資料庫的時候,它怎麼知道更新哪些查詢緩存呢?
hibernate在一個地方維護每個表的最後更新時間,其實也就是放在上面net.sf.hibernate.cache.UpdateTimestampsCache所指定的緩存配置裡面。
當通過hibernate更新的時候,hibernate會知道這次更新影響了哪些表。然後它更新這些表的最後更新時間。每個緩存都有一個生成時間和這個緩存所查詢的表,當hibernate查詢一個緩存是否存在的時候,如果緩存存在,它還要取出緩存的生成時間和這個緩存所查詢的表,然後去查找這些表的最後更新時間,如果有一個表在生成時間後更新過了,那麼這個緩存是無效的。
可以看出,只要更新過一個表,那麼凡是涉及到這個表的查詢緩存就失效了,因此查詢緩存的命中率可能會比較低。
Collection緩存
需要在hbm的collection裡面設置
<cache usage="read-write"/>
假如class是Cat,collection叫children,那麼ehcache裡面配置
<cache name="com.xxx.pojo.Cat.children"
maxElementsInMemory="20" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200"
overflowToDisk="true" />
Collection的緩存和前面查詢緩存的list一樣,也是只保持一串id,但它不會因為這個表更新過就失效,一個collection緩存僅在這個collection裡面的元素有增刪時才失效。
這樣有一個問題,如果你的collection是根據某個欄位排序的,當其中一個元素更新了該欄位時,導致順序改變時,collection緩存裡面的順序沒有做更新。
緩存策略
只讀緩存(read-only):沒有什麼好說的
讀/寫緩存(read-write):程序可能要的更新數據
不嚴格的讀/寫緩存(nonstrict-read-write):需要更新數據,但是兩個事務更新同一條記錄的可能性很小,性能比讀寫緩存好
事務緩存(transactional):緩存支持事務,發生異常的時候,緩存也能夠回滾,只支持jta環境,這個我沒有怎麼研究過
讀寫緩存和不嚴格讀寫緩存在實現上的區別在於,讀寫緩存更新緩存的時候會把緩存裡面的數據換成一個鎖,其他事務如果去取相應的緩存數據,發現被鎖住了,然後就直接取資料庫查詢。
在hibernate2.1的ehcache實現中,如果鎖住部分緩存的事務發生了異常,那麼緩存會一直被鎖住,直到60秒後超時。
不嚴格讀寫緩存不鎖定緩存中的數據。
使用二級緩存的前置條件
你的hibernate程序對資料庫有獨占的寫訪問權,其他的進程更新了資料庫,hibernate是不可能知道的。你操作資料庫必需直接通過hibernate,如果你調用存儲過程,或者自己使用jdbc更新資料庫,hibernate也是不知道的。hibernate3.0的大批量更新和刪除是不更新二級緩存的,但是據說3.1已經解決了這個問題。
這個限制相當的棘手,有時候hibernate做批量更新、刪除很慢,但是你卻不能自己寫jdbc來優化,很郁悶吧。
SessionFactory也提供了移除緩存的方法,你一定要自己寫一些JDBC的話,可以調用這些方法移除緩存,這些方法是:
void evict(Class persistentClass)
Evict all entries from the second-level cache.
void evict(Class persistentClass, Serializable id)
Evict an entry from the second-level cache.
void evictCollection(String roleName)
Evict all entries from the second-level cache.
void evictCollection(String roleName, Serializable id)
Evict an entry from the second-level cache.
void evictQueries()
Evict any query result sets cached in the default query cache region.
void evictQueries(String cacheRegion)
Evict any query result sets cached in the named query cache region.
不過我不建議這樣做,因為這樣很難維護。比如你現在用JDBC批量更新了某個表,有3個查詢緩存會用到這個表,用evictQueries(String cacheRegion)移除了3個查詢緩存,然後用evict(Class persistentClass)移除了class緩存,看上去好像完整了。不過哪天你添加了一個相關查詢緩存,可能會忘記更新這里的移除代碼。如果你的jdbc代碼到處都是,在你添加一個查詢緩存的時候,還知道其他什麼地方也要做相應的改動嗎?
----------------------------------------------------
總結:
不要想當然的以為緩存一定能提高性能,僅僅在你能夠駕馭它並且條件合適的情況下才是這樣的。hibernate的二級緩存限制還是比較多的,不方便用jdbc可能會大大的降低更新性能。在不了解原理的情況下亂用,可能會有1+N的問題。不當的使用還可能導致讀出臟數據。
如果受不了hibernate的諸多限制,那麼還是自己在應用程序的層面上做緩存吧。
在越高的層面上做緩存,效果就會越好。就好像盡管磁碟有緩存,資料庫還是要實現自己的緩存,盡管資料庫有緩存,咱們的應用程序還是要做緩存。因為底層的緩存它並不知道高層要用這些數據干什麼,只能做的比較通用,而高層可以有針對性的實現緩存,所以在更高的級別上做緩存,效果也要好些吧。