Hibernate入門 - 基礎配置
?
Hibernate配置文件可以有兩種格式,一種是 hibernate.properties ,另一種是 hibernate.cfg.xml
后者稍微方便一些,當增加hbm映射文件的時候,可以直接在 hibernate.cfg.xml 里面增加,不必像 hibernate.properties 必須在初始化代碼中加入。
但不管怎么說,兩種的配置項都是一樣的,下面詳細介紹:
在Hibernate的src目錄下有一個 hibernate.properties 模板,我們不必自己從頭寫,修改模板就可以了:)
hibernate.query.substitutions true 1, false 0, yes 'Y', no 'N'
這個配置意思是當你在Hibernate里面輸入true的時候,Hibernate會轉化為1插入數據庫,當你在Hibernate里面輸入false的時候,Hibernate會轉化為0插入數據庫,后面的Y,N同理。
對于某些數據庫,例如Oracle來說,沒有boolean數據類型,就是采用1代表true,0代表false,因此使用這個配置在Hibernate里面直接用true/false會非常直觀。
hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class com.mysql.jdbc.Driver
hibernate.connection.url jdbc:mysql:///test
hibernate.connection.username root
hibernate.connection.password
這是一個連接MySQL數據庫的例子,很直觀,不必解釋,不同的數據庫的連接參數模板中全部給出了。
hibernate.connection.pool_size 1
hibernate.statement_cache.size 25
這是Hibernate自帶的連接池的配置參數,在默認情況下將采用。意義很直觀,不多解釋。
只是提醒一點,Hibernate這個連接池是非常原始非常簡單的連接池,如果你在項目中用Hibernate的話,建議你首選App Server的連接池,次選Hibernate帶的DBCP連接池。自帶的連接池應該做為末選。
如果你采用DBCP連接池,除了要配置DBCP連接池以外,還需要取消掉下行的注釋:
hibernate.connection.provider_class net.sf.hibernate.connection.DBCPConnectionProvider
其它的連接池同理。
如果采用App Server的連接池,假設App Server連接池的DataSource的JNDI名稱為"mypool"的話,配置應該如下:
hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
hibernate.connection.datasource mypool
hibernate.connection.provider_class net.sf.hibernate.connection.DatasourceConnectionProvider
其它參數就不必寫了,因為已經在App Server配置連接池的時候指定好了。
如果你不是在App Server環境中使用Hibernate,例如遠程客戶端程序,但是你又想用App Server的數據庫連接池,那么你還需要配置JNDI的參數,例如Hibernate連接遠程Weblogic上的數據庫連接池:
hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
hibernate.connection.datasource mypool
hibernate.connection.provider_class net.sf.hibernate.connection.DatasourceConnectionProvider
hibernate.jndi.class weblogic.jndi.WLInitialContextFactory
hibernate.jndi.url t3://servername:7001/
最后,如果你需要在EJB或者JTA中使用Hibernate,需要取消下行的注釋:
hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory
雜項配置:
hibernate.show_sql false
是否將Hibernate發送給數據庫的sql顯示出來,這是一個非常非常有用處的功能。當你在調試Hibernate的時候,讓Hibernate打印sql語句,可以幫助你迅速解決問題。
#hibernate.connection.isolation 4
指定數據庫的隔離級別,往往不同的數據庫有自己定義的隔離級別,未必是Hibernate的設置所能更改的,所以也不必去管它了。
hibernate.jdbc.fetch_size 50
hibernate.jdbc.batch_size 25
這兩個選項非常非常非常重要!!!將嚴重影響Hibernate的CRUD性能!
C = create, R = read, U = update, D = delete
Fetch Size 是設定JDBC的Statement讀取數據的時候每次從數據庫中取出的記錄條數。
例如一次查詢1萬條記錄,對于Oracle的JDBC驅動來說,是不會1次性把1萬條取出來的,而只會取出Fetch Size條數,當紀錄集遍歷完了這些記錄以后,再去數據庫取Fetch Size條數據。
因此大大節省了無謂的內存消耗。當然Fetch Size設的越大,讀數據庫的次數越少,速度越快;Fetch Size越小,讀數據庫的次數越多,速度越慢。
這有點像平時我們寫程序寫硬盤文件一樣,設立一個Buffer,每次寫入Buffer,等Buffer滿了以后,一次寫入硬盤,道理相同。
Oracle數據庫的JDBC驅動默認的Fetch Size=10,是一個非常保守的設定,根據我的測試,當Fetch Size=50的時候,性能會提升1倍之多,當Fetch Size=100,性能還能繼續提升20%,Fetch Size繼續增大,性能提升的就不顯著了。
因此我建議使用Oracle的一定要將Fetch Size設到50。
不過并不是所有的數據庫都支持Fetch Size特性,例如MySQL就不支持。
MySQL就像我上面說的那種最壞的情況,他總是一下就把1萬條記錄完全取出來,內存消耗會非常非常驚人!這個情況就沒有什么好辦法了 :(
Batch Size是設定對數據庫進行批量刪除,批量更新和批量插入的時候的批次大小,有點相當于設置Buffer緩沖區大小的意思。
Batch Size越大,批量操作的向數據庫發送sql的次數越少,速度就越快。我做的一個測試結果是當Batch Size=0的時候,使用Hibernate對Oracle數據庫刪除1萬條記錄需要25秒,Batch Size = 50的時候,刪除僅僅需要5秒!!!
可見有多么大的性能提升!很多人做Hibernate和JDBC的插入性能測試會奇怪的發現Hibernate速度至少是JDBC的兩倍,就是因為Hibernate使用了Batch Insert,而他們寫的JDBC沒有使用Batch的緣故。
以我的經驗來看,Oracle數據庫 Batch Size = 30 的時候比較合適,50也不錯,性能會繼續提升,50以上,性能提升的非常微弱,反而消耗內存更加多,就沒有必要了。
#hibernate.jdbc.use_scrollable_resultset true
設定是否可以使用JDBC2.0規范的可滾動結果集,這對Hibernate的分頁顯示有一定的作用,默認就好了。
#hibernate.cglib.use_reflection_optimizer false
默認打開,啟用cglib反射優化。cglib是用來在Hibernate中動態生成PO字節碼的,打開優化可以加快字節碼構造的速度。
不過,當你在調試程序過程中,特別是和proxy,lazy loading相關的應用中,代碼出錯,但是出錯提示信息有語焉不詳,那么你可以把cglib優化關掉,這樣Hibernate會輸出比較詳細的調試信息,幫助你debug。
Hibernate 入門 - 實戰演練
?
本文配置環境:
JBuilder X
jdk 1.4.2
Mysql 4.0.11 驅動:mm.mysql-2.0.4-bin.jar(org.gjt.mm.mysql.Driver)
Hibernate 2.1
解壓Hibernate
打開JB,新建工程,名為:hibernate
加入Hibernate需要的包與MYSQL驅動
步驟:file->new project->name中輸入hibernate,directory選擇你要存放本工程的路徑->next
->required libraries->add->new->name中輸入你要設置的hibernate包名
->add->選擇你hibernate解壓到的目錄,選中該目錄下的hibernate2.jar與lib目錄下的所有jar包,再把你的MYSQL驅動包也加進去
然后一直按OK,next。
新建一個類,名為Hello_Bean.java,代碼如下:
package hibernate;
import java.io.Serializable;
public class Hello_Bean implements Serializable {
private String name;//這里name與address和id的名字可以自己定,不會有什么影響.但get與set方法不可以。因為那得跟數據庫與配置文件對應。
private String address;
private int id;
public Hello_Bean() {
}
public Hello_Bean(String name, String address) {//構造函數,看完本章以后相信你會明白的了
this.name = name;
this.address = address;
}
public String getName() {//此方法名必須與Hello_Bean.hbm.xml文件里的對應的名字一樣,下面會詳細講
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getId() {//必須的方法
return id;
}
public void setId(int id)//必須的方法
{
this.id = id;
}
}
完成這一步以后編譯
將hibernate解壓后的目錄下的src文件夾里的hibernate.properties與log4j.properties文件復制到你的工程目錄的classes目錄下
(例如hibernate\classes\目錄下),
打開hibernate.properties文件,找到
## HypersonicSQL
hibernate.dialect net.sf.hibernate.dialect.HSQLDialect
hibernate.connection.driver_class org.hsqldb.jdbcDriver
hibernate.connection.username sa
hibernate.connection.password
hibernate.connection.url jdbc:hsqldb:hsql://localhost
hibernate.connection.url jdbc:hsqldb:test
hibernate.connection.url jdbc:hsqldb:.
改為
## HypersonicSQL
#hibernate.dialect net.sf.hibernate.dialect.HSQLDialect
#hibernate.connection.driver_class org.hsqldb.jdbcDriver
#hibernate.connection.username sa
#hibernate.connection.password
#hibernate.connection.url jdbc:hsqldb:hsql://localhost
#hibernate.connection.url jdbc:hsqldb:test
#hibernate.connection.url jdbc:hsqldb:.
再找到
## MySQL
#hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
#hibernate.connection.driver_class org.gjt.mm.mysql.Driver
#hibernate.connection.driver_class com.mysql.jdbc.Driver
#hibernate.connection.url jdbc:mysql:///test
#hibernate.connection.username root
#hibernate.connection.password
改為
## MySQL
hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class org.gjt.mm.mysql.Driver
hibernate.connection.url jdbc:mysql://localhost:3306/test
hibernate.connection.username root
hibernate.connection.password
上面的URL請改為你自己的
完成以后創建一個空的文件,保存在你工程的類文件同個文件夾里(例如hibernate\classes\hibernate\目錄下),文件名為:Hello_Bean.hbm.xml
內容如下:
?
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="hibernate.Hello_Bean" table="test_hibernate" >
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="name" type="string" update="true" insert="true" column="name" />
<property name="address" type="string" update="true" insert="true" column="address" />
</class>
</hibernate-mapping>
?
稍微解釋一下,<class name="hibernate.Hello_Bean" table="test_hibernate" >里的name指你的生成表的類,
table則指定你要創建的數據庫表的名字,可以自由修改,沒有影響.
<id name="id" column="id">設置主鍵ID,這里name的值id跟Hello_Bean.java里的方法getId與setId對應,不用管這個,hibernate會自動調用,配置好就可以了,column的值為要生成的字段名,可以自由修改,沒有影響。
<generator class="identity"/>屬性讓主鍵的ID自增(插入數據的時候自動加1)
<property name="name" type="string" update="true" insert="true" column="name" />這里的name值"name"跟Hello_Bean.java里的getName方法對應,column為要生成的字段名
<property name="address" type="string" update="true" insert="true" column="address" />
分別添加一個字段name與address,注意這里的type屬性類型為string,如果這里的類型與Hello_Bean.java里設置的類型不一樣會出錯。
修改完以后保存。
最后在JB里新建一個類,名字為Hello.java,我會一步步解釋,代碼如下:
package hibernate;
import net.sf.hibernate.cfg.Configuration;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.tool.hbm2ddl.SchemaExport;
import net.sf.hibernate.Session;
import net.sf.hibernate.Query;
import net.sf.hibernate.Hibernate;
import net.sf.hibernate.type.LongType;
import net.sf.hibernate.Transaction;
import net.sf.hibernate.ScrollableResults;
import java.util.*;
public class Hello {
public Hello() {
}
public static void main(String[] args) throws Exception {
Configuration cfg = new Configuration().addClass(Hello_Bean.class);//用Hello_Bean.class類初始化
SessionFactory sessions = cfg.buildSessionFactory();//用buildSessionFactory方法得到一個SessionFactory對象
Session session = sessions.openSession();//再用SessionFactory的openSession方法得到一個session
new SchemaExport(cfg).create(true, true);//這句意思是創建表,第一次運行以后,就是創建完表以后再把這行加上注釋。如果建表以后不把這一句注釋掉的話會刪掉以前創建的表再重新建一個。
Hello_Bean my_hibernate = new Hello_Bean();//得到一個Hello_Bean對象
my_hibernate.setName("my_name");//設置Hello_Bean對象的name值為my_name,這里其實就是說把字符串my_name當作數據庫字段name的值.數據庫字段name與Hello_Bean類里的getName,setName方法是對應的。形成一個映射關系。
my_hibernate.setAddress("my_address");//如上
session.save(my_hibernate);//這句很重要,將my_hibernate對象寫進數據庫(my_hibernate對象里的name與address我們剛剛已經設置了值了,會直接把name,address的值寫進數據庫去)
session.flush();
session.close();
//上面是一個簡單的插入數據與第一次運行建表的介紹,下面我再介紹刪除與修改的方法,下面的代碼我
//都加了注釋,自己需要什么方法(刪除,修改,循環數據庫的值)就把注釋去掉就可以了
??????? HSQL比較簡單,大家看一下例子就應該明白了,這里就不講了。
??????? 遍歷數據庫的方法有三種,分別是Query,find,iterate,Query和find返回一個List接口,iterate返回一個Iterator,具體方法可以查看這些類得知。
//刪除數據
/*
int a=session.delete("from Hello_Bean where id=1");//如果沒有找到id為1的數據那么返回0,如果找到返回1,這
//里的Hello_Bean是我們的Hello_Bean類,他跟數據庫表對應,所以我們在這里是直接用Hello_Bean來代碼
//數據庫表的。
System.out.println(a);
session.flush();
session.close();
*/
//Query方法查詢數據
/*
Hello_Bean my_hibernate = null;
Query q = session.createQuery("from Hello_Bean");
// Query q = session.createQuery("from Hello_Bean where name=?");//這里的?跟JDBC的PreparedStatement方法的
//差不多,只不過這里的是以0開始,jdbc的是1開始。
// q.setString(0,"my_name");
// q.setFirstResult(0);//這句話的意思是說查詢結果從第幾行開始列出數據
// q.setMaxResults(3);//這句話的意思是取多少條數據,就跟SQL SERVER的TOP方法和MYSQL的LIMIT方
//法一樣的意思。
// ScrollableResults sc=q.scroll();//得到一個ScrollableResults,可滾動的,如果你的數據庫支持游標自由移動的
//話可以加上,也就是說可以判斷查詢結果有沒有值,或者移動到下一行記錄等等。
// if(!sc.next())
// {
// System.out.println("沒有找到你需要的數據");
// }
session.flush();//如果使用了ScrollableResults的話請把這行注釋掉
session.close();//如果使用了ScrollableResults的話請把這行注釋掉
List l=q.list();//返回一個List接口,用來遍歷結果集
for(int i=0;i<l.size();i++){
my_hibernate = (Hello_Bean) l.get(i);//從List中取得一個my_hibernate對象
System.out.println(my_hibernate.getName());//調用my_hibernate對象的getName方法取得數據庫name字段的值
}
*/
//find方法查詢數據
/*
Hello_Bean my_hibernate = null;
List q = session.find("from Hello_Bean");
session.flush();
session.close();
for(int i=0;i<q.size();i++)
{
my_hibernate = (Hello_Bean) q.get(i);
System.out.println(my_hibernate.getName());
}
*/
//iterate方法查詢數據
/*
Hello_Bean my_hibernate = null;
Iterator q = session.iterate("from Hello_Bean");
while(q.hasNext())
{
my_hibernate = (Hello_Bean) q.next();
System.out.println(my_hibernate.getName());
}
*/
//修改數據
/*
Query qq=session.createQuery("from Hello_Bean");
Hello_Bean my_hibernate=(Hello_Bean)session.load(Hello_Bean.class,new Integer(2));
//這里的new Integer(2)意思是修改表中id為2的那一行數據,必須是一個包裝類的對象,如果使用int的話會出錯。
my_hibernate.setName("geezer");//把id為2的那一行數據的name字段值改為"geezer"
session.flush();
session.close();
*/
}
}
最后運行就可以了。
如果大家有什么不清楚的,可以下載這篇文章的例子看看,運行一下。
下載完以后用JB打開hibernate.jpx就可以了。用之前把hibernate和MYSQL的驅動加進去,方法如果不知道再看看本章開頭部分。
常見錯誤:
Caused by: org.dom4j.DocumentException: Invalid byte 2 of 2-byte UTF-8 sequence. Nested exception: Invalid byte 2 of 2-byte UTF-8 sequence.
如果出現這行錯誤說明你的xml配置文件有不規范的字符,檢查下。
net.sf.hibernate.MappingException: Error reading resource: hibernate/Hello_Bean.hbm.xml
如果出現這行錯誤說明你的hibernate的XML配置文件有錯
net.sf.hibernate.MappingException: Resource: hibernate/Hello_Bean.hbm.xml not found
如果出現這行錯誤說明hibernate的XML配置文件沒有找到,你應該把XML文件放在與你的類文件同個目錄下,本文中是放在hibernate\classes\hibernate\目錄下,也就是跟Hello_Bean.class類文件一起。
net.sf.hibernate.PropertyNotFoundException: Could not find a setter for property name in class hibernate.Hello_Bean
如果出現這行錯誤說明你的xml文件里設置的字段名name的值與Hello_Bean.java類里的getXXX或setXXX方法不一致。
net.sf.hibernate.HibernateException: JDBC Driver class not found: org.gjt.mm.mysql.Driver
如果出現這行錯誤說明你的MYSQL驅動沒有加進JB庫里或者不在CLASSPATH里。