一、首先学习hibernate.cfg.xml配置文件的具体配置
<?xml version="1.0" encoding="UTF-8"?><!--指定该文件的官方dtd--><!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd" >
<hibernate-configuration>
<session-factory>
<!-- 显示sql语言 -->
<property name="show_sql">true</property>
<!-- sql语言 -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- jdbc驱动程式 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- jdbc url -->
<property name="connection.url">jdbc:mysql://localhost:3306/test</property>
<!-- 数据库用户名 -->
<property name="connection.username">root</property>
<!-- 数据库密码 -->
<property name="connection.password">wyq</property>
<!-- C3P0连接池设定 -->
<!--最小连接数-->
<property name="c3p0.min_size">5</property>
<!--最大连接数-->
<property name="c3p0.max_size">20</property>
<!--延迟所允许的时间-->
<property name="c3p0.timeout">1800</property>
<!--缓存所允许的最大连接数-->
<property name="c3p0.max_statements">50</property>
<!-- 每隔100笔资料送入资料库,清除缓存(定期清除缓存,减小压力) -->
<property name="hibernate.jdbc.batch_size">100</property>
<!-- 设定事务管理的工厂类 -->
<property name="hibernate.transaction.factiory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
<mapping resource="com/wyq/hibernate/pojo/User.hbm.xml"/>
<mapping resource="com/wyq/hibernate/pojo/TUser.hbm.xml"/>
<mapping resource="com/wyq/hibernate/pojo/Room.hbm.xml"/>
</session-factory>
</hibernate-configuration>
需要的jar包有c3p0.jar,hibernate3.jar,数据库.jar,log4j.jar ------------------------------------------------------------------------------------------------------------------------
1、读取配置文件获得连接
读取hibernate.cfg.xml配置文件,hibernate.cfg.xml文件放在Classpath下,使用下面的方式读入该文件 //Configuration 负责管理hibernate配置信息 Configuration config=new Configuration().configure();
//根据config建立SessionFactory
//SessionFactory用于建立Session
SessionFactory sessionFactory=config.buildSessionFactory();
//开启session,相当于jdbc的Connection
session = sessionFactory.openSession();
2、Criteria 基本资料查询
(1)标准查询:
//创建查询标准
Criteria criteria=session.creteCriteria(User.class);
//查询条件
criteria.add(Expression.eq("name","caterpillar"));
************************************************************************************
Expression.eq(String s1,String s2)---------->相等s1=s2
Expression.allEq(Map map) --------------->多个属性-值对应关系,多个Expression.eq叠加
Expression.gt(String s1,String s2)----------->大于s1>s2
Expression.ge(String s1,String s2)----------->大于等于s1>=s2
Expression.lt(String s1,String s2)------------>小于s1<s2
Expression.le(String s1,String s2)------------>小于等于s1<=s2
Expression.between(String s1,int s2,int s3)--->s2<s1<s3
Expression.like(String s1,String s2)------------>s1 like s2
比较2个属性
Expression.eqProperty(String s1,String s2)--->s1=s2
Expression.gtProperty(String s1,String s2)---->s1>s2
Expression.geProperty(String s1,String s2)---->s1>=s2
Expression.ltProperty(String s1,String s2)----->s1<s2
Expression.leProperty(String s1,String s2)----->s1<=s2
Expression.and()----->Expression.and(Expression.eq("String s1,String s2"),Expression.eq(String s3,String s4))
Expression.or()
************************************************************************************
(2)高级查询
一、可以使用Criteria进行查询,并用order对结果进行排序。
//设置从第几条开始取的记录
criteria.setFirstResult(100);
//最多取的几条记录
criteria.setMaxResults(20);
//对结果进行排序
criteria.addOrder(Order.asc(String s1));
criteria.addOrder(Order.desc(String s2));
二、可以对查询结果进行统计操作,使用Projections的rowCount(),count(),max(),min(),countDistinct()等方法:
例如:criteria.setProjection(Projections.max("age"));
三、还可以用Projections的groupProperty()来对结果进行分组
例如:criteria.setProjection(Projections.groupProperty("age"));
(***)四、结合统计与分组的功能,可以用ProjectionList
例如:ProjectionList projectionList =Projections.projectionList();
projectionList.add(Projections.groupProperty("age"));
projectionList.add(Projections.rowCount());
criteria.setProjection(projectionList);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//查询所有记录
List users=criteria.list();
Iterator iterator=users.iterator();
while(iterator.hasNext()){
User user=(User)iterator.next();
System.out.println(user.getId()+"\t"+user.getName()+"/"+user.getAge());
}
3、criteria的增、删、改(还不完善)
在用到增、删、改时,必须先声明事务
增加:
Transaction tx = session.beginTransaction();//Transaction表示一组会话操作
session.save(user);//将事物映射到数据库进行存储
tx.commit();
session.close();
删除:
Session session=this.getSession();
User user=(User)session.get(User.class, new Integer(1));
Transaction tx = session.beginTransaction();//Transaction表示一组会话操作
session.delete(user);//将事物映射到数据库进行存储
tx.commit();
session.close();
修改:
Session session=this.getSession();
User user =(User)session.get(User.class,new Integer(2));//创建持久化的事物
user.setName("wyqqqqqqqqqq");
user.setAge(new Integer(30));
Transaction tx = session.beginTransaction();//Transaction表示一组会话操作
session.update(user);//将事物映射到数据库进行存储
tx.commit();
session.close();
----------------------------------------------------------------------------------------------------------------------
一、Query查询可以先设定查询参数,之后通过set等方法,将指定的参数值添入.还可以使用命名参数
Session session = sessionFactory.openSession();
Query query = session.createQuery("select user.name from User as user where user.age>?( :minAge )");
query.setInteger(0,25);
query.setInteger("minAge",25);
List names=query.list();
Iterator iterator = names.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
session.close();
二、如果查询整个表直接使用from User如果针对某个属性使用select user.name from User as user
使用hql可以更接近我们平时的jdbc编程,和把sql语句写在程序中差不多,另外,也可以将sql语句写在配置文件中。 --------------------------------------------------------------------------------------------------------------------------
多表关联 一、多对一进行关联(多个学生对应同一间宿舍)---学生是主体,宿舍是附体,关联关系<many-to-one>在主体学生中设置,在学生类中设置宿舍类,由于宿舍类只有一个可以直接用类来设置,在映射学生类(User)中包含宿舍这个类(Room),在映射配置文件(User.hbm.xml)中定义
<many-to-one name="room" column="room_id" cascade="save-update" class="com.wyq.hibernate2.Room"></many-to-one>
哪个是主体类就在哪个配置文件定义关联关系.
cascade属性:表示关联对象的持久化,该属性也要设置在主体中,作用就是当主控方执行操作时,关联对象(被动方)是否同步执行同一操作.
cascade的值:all:表示所有情况下都进行级联操作.
none:所有情况下都不进行级联操作
save-update:在执行save-update时进行级联操作.
delete:在执行delete时进行级联操作.
注意:使用cascade自動持久化時,會先檢查被關聯物件的id屬性,未被持久化的物件之id屬性是由unsaved-value決定,預設是null,如果您使用long這樣的原生型態(primitive type)時,則必須自行指定預設值.
例如:<id name="id" column="ROOM_ID" unsaved-value="0">
<generator class="increment"/>
</id>
如果您不想額外設定unsaved-value資訊,則可以將long改為Long,這可以符合預設的unsaved-value為null的設定 .
二、一对多进行关联(一间宿舍对应多个学生)---宿舍是主体,学生是附体,关联关系<one-to-many>在主体宿舍中设置,由于要在宿舍类中设置学生类,一个宿舍包含多个学生,所以在宿舍类中要用Set类来进行设置,用set类(private Set users = new HashSet();)来存储多个学生类,在映射宿舍类(Room)中要包含<set>这个节点,用来与user相关联
例如:<set name="users" table="USER">
<key column="ROOM_ID"/>
<one-to-many class="onlyfun.caterpillar.User"/>
</set>
name:表示属性,table:表示关联的表名,key:表示通过什么字段进行关联,<one-to-many>:表示关联类。这里也可以使用cascade属性。
三、在表关联的设计中,不论是一对多还是多对一,都要将关联字段设置在多的那一方。
例如:user表格和room表格,要将关联字段room_id设置在user表格中。
四、一对一进行关联(一个人只有一个房间,一个房间也只有一个人)。
可以通过2中方式进行关联:
(1)、通过外键进行关联:在多对一的例子中就是通过外键进行关联的.
在user-room的设置中(user.hbm.xml):
<many-to-one name="room"
column="ROOM_ID"
class="onlyfun.caterpillar.Room"
cascade="all"
unique="true"/>
其中unique表示限制一個User有一獨有的 Room,这只是单向的,说明一个user只有一个room.
在room-user的设置中(room.hbm.xml):
<one-to-one name="user"
class="onlyfun.caterpillar.User"
property-ref="room"/>
这样就完成了双向的一对一关联,property-ref告诉hibernate,查询出user并将其参考至room。
(2)、通过主键进行关联:限制兩個資料表的主鍵使用相同的值,如此一個User與Room就是一對一關係
user.hbm.xml:
<one-to-one name="room"
class="onlyfun.caterpillar.Room"
cascade="all"/>
room.hbm.xml:
<one-to-one name="user"
class="onlyfun.caterpillar.User"
constrained="true"/>
使用constrained="true"告訴Hibernate參考至User的主鍵
五、双向关联,就是将一和二结合起来,如果將關聯的維護交給User的話會比較容易,因為每個User都對應至一個Room,在儲存時並用像Room一樣必須對Set中的每個物件作檢查,為了將關聯的維護交給User,我們可以在Room.hbm.xml中的<set>修改,加上inverse="true",表示將關聯的維護「反過來」交給User作
例如:<set name="users" table="users" iinverse="true" cascade="all">
<key column="room_id"/>
<one-to-many class="onlyfun.caterpillar.User"/>
在設立雙向關聯時,關聯由多對一中「多」的哪一方維護,會比由「一」的哪一方維護來的方便,在Hibernate可以藉由inverse來設定,不設定inverse基本上也可以運行,但是效能會較差。
------------------------------------------------------------------------------------------------------------------------
在Hibernate中,集合類的映射可以延遲初始(Lazy Initialization),在多对一或者一对多中,都可以使用延遲初始,例如:一个用户(user对应user表)有多个email地址(address对应address表),也就是在真正索取該物件的資料時,才向資料庫查詢,就上次例子來說,就是我們在讀取User時,先不取得其中的 addrs屬性中之物件資料,由於只需要讀取User的name屬性,此時我們只要執行一次select即可,真正需要addrs的資料時,才向資料庫要求。在含有集合类的user.hbm.xml中要如下设置:
<set name="addrs" table="ADDRS" lazy="true">
<key column="USER_ID"/>
<element type="string" column="ADDRESS" not-null="true"/>
</set> --------------------------------------------------------------------------------------------------------------------------
session是hibernate运做的核心,是有SessionFactory所创建,sessionFactory是线程安全的,你可以让多个线程同时存取SessionFactory,而不会有资源共用的问题,然而session不是设计为线程安全的,所以让多个线程共用一个session,将发生资料共用而发生混乱的问题.下面是一个标准类.
import java.io.Serializable;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.Transaction;
public class HibernateSessionUtil implements Serializable
{
//创建线程局部变量 tLocalsess
public static final ThreadLocal tLocalsess = new ThreadLocal();
//创建线程局部变量 tLocaltx
public static final ThreadLocal tLocaltx = new ThreadLocal();
//取得session
public static Session currentSession(){
//从线程变量tLocalsess中,取得当前session
Session session = (Session) tLocalsess.get();
//判断session是否为空,如果为空,将创建一个session,并付给线程变量tLocalsess
try{
if (session == null){
session = openSession();
tLocalsess.set(session);
}
}catch (HibernateException e){
throw new InfrastructureException(e);
}
return session;
}
//关闭当前session
public static void closeSession(){
//从线程变量tLocalsess中,取得当前session
Session session = (Session) tLocalsess.get();
//设置线程变量tLocalsess为空
tLocalsess.set(null);
try{
//关闭session
if (session != null && session.isOpen()){
session.close();
}
}catch (HibernateException e){
throw new InfrastructureException(e);
}
}
//事物处理
public static void beginTransaction(){
//从线程变量tLocaltx中取得事物管理对象Transaction
Transaction tx = (Transaction) tLocaltx.get();
try{
//如果为空就从session中创建一个tx
if (tx == null){
tx = currentSession().beginTransaction();
tLocaltx.set(tx);
}
}catch (HibernateException e){
throw new InfrastructureException(e);
}
}
//提交事物
public static void commitTransaction(){
//取得事物
Transaction tx = (Transaction) tLocaltx.get();
try{
//如果不为空就提交
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack())
tx.commit();
tLocaltx.set(null);
}catch (HibernateException e){
throw new InfrastructureException(e);
}
}
//事物回滚
public static void rollbackTransaction(){
//取得tx事物
Transaction tx = (Transaction) tLocaltx.get();
try{
//将变量清空
tLocaltx.set(null);
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()){
//事物回滚
tx.rollback();
}
}catch (HibernateException e){
throw new InfrastructureException(e);
}
}
//取得session
private static Session openSession() throws HibernateException{
return getSessionFactory().openSession();
}
//取得sessionFactory
private static SessionFactory getSessionFactory() throws HibernateException{
return SingletonSessionFactory.getInstance();
}
}
filter的代码:
public class HibernateSessionCloser implements Filter{
protected FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig)throws ServletException{
this.filterConfig = filterConfig;
}
public void destroy(){
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
try{
chain.doFilter(request, response);
}
finally{
try{
HibernateSessionUtil.commitTransaction();
}catch (InfrastructureException e){
HibernateSessionUtil.rollbackTransaction();
}finally{
HibernateSessionUtil.closeSession();
}
}
}
}
---------------------------------------------------------------------------------------
(1)、悲觀鎖定(Pessimistic Locking)一如其名稱所示,悲觀的認定每次資料存取時,其它的客戶端也會存取同一筆資料,因此對該筆資料進行鎖定,直到自己操作完成後解除鎖定。
悲觀鎖定通常透過系統或資料庫本身的功能來實現,依賴系統或資料庫本身提供的鎖定機制,Hibernate即是如此,可以利用Query或 Criteria的setLockMode()方法來設定要鎖定的表或列(Row)及其鎖定模式,可設定的鎖定模式有以下的幾個:
LockMode.WRITE:在insert或update時進行鎖定,Hibernate會在save()方法時自動獲得鎖定。
LockMode.UPGRADE:利用SELECT ... FOR UPDATE進行鎖定。
LockMode.UPGRADE_NOWAIT:利用SELECT ... FOR UPDATE NOWAIT進行鎖定,在Oracle環境下使用。
LockMode.READ:在讀取記錄時Hibernate會自動獲得鎖定。
LockMode.NONE:沒有鎖定。
(2)、樂觀鎖定(Optimistic locking)則樂觀的認為資料的存取很少發生同時存取的問題,因而不作資料庫層次上的鎖定,為了維護正確的資料,樂觀鎖定使用應用程式上的邏輯實現版本控制的解決。
在不實行悲觀鎖定策略的情況下,資料不一致的情況一但發生,有幾個解決的方法,一種是先更新為主,一種是後更新的為主,比較複雜的就是檢查發生變動的資料來實現,或是檢查所有屬性來實現樂觀鎖定。
要注意的是,由於樂觀鎖定是使用系統中的程式來控制,而不是使用資料庫中的鎖定機制,因而如果有人特意自行更新版本訊息來越過檢查,則鎖定機制就會無效,例如在上例中自行更改userV2的version屬性,使之與資料庫中的版本號相同的話就不會有錯誤,像這樣版本號被更改,或是由於資料是由外部系統而來,因而版本資訊不受控制時,鎖定機制將會有問題,設計時必須注意。