Hibernate使用简介

上页

Select语法

select cat.mate from Cat cat 从Cat对象中查询获得其关联方mate.

 

计数功能:

Where:

from Cat where name='Fritz'

表达式(+ - * /)

order by ? group by

 

查询接口

映射文档中定义命名查询

<query name=“DomesticCat_Query"><
![CDATA[ from eg.DomesticCat as cat where cat.name = ? and cat.weight > ? ] ]
></query>

Query q = sess.getNamedQuery(" DomesticCat_Query ");
q.setString(0, name);
q.setInt(1, minWeight);
List cats = q.list();

 

批量查询

查询会返回非常大量的对象,但是你不希望全部使用它们,你可以用iterate()方法获得更好的性能。

这个迭代器会在需要的时候装载对象。

Iterator iter = sess.iterate("from eg.Qux q order by q.likeliness");
while ( iter.hasNext() ) {
Qux qux = (Qux) iter.next(); // 抓取对象
}

 

标量查询(Scalar query)

Iterator results = sess.iterate(

"select cat.color, min(cat.birthdate), count(cat) from Cat cat " +?"group by cat.color"

);

while ( results.hasNext() ) {

Object[] row = results.next();
Color type = (Color) row[0];
Date oldest = (Date) row[1];
Integer count = (Integer) row[2];

}

 

Criteria Query

更符合对象OO风格的查询

Criteria crit = sess.createCriteria(Cat.class); crit.setMaxResults(50);
List cats = crit.list();

排序输出结果:

List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%")
.addOrder( Order.asc("name") )
.addOrder( Order.desc("age") )
.setMaxResults(50) .list();

 

过滤集合类

集合filter是一种特殊的查询,用于一个持久化集合或者数组。查询字符串可以引用this,意为当前的数组元素。

Collectio blackKittens = session.filter(
pk.getKittens(), "where this.color = ?", Color.BLACK, Hibernate.enum(Color.class)
);

 

使用本地SQL的查询

你可以使用createSQLQuery()方法,用SQL来表达查询。你必须把SQL别名用大括号包围起来。

List cats = session.createSQLQuery(
"SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10",
"cat",
Cat.class
).list();

List cats = session.createSQLQuery(
"SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +
"{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +
"FROM CAT {cat} WHERE ROWNUM<10",
"cat",
Cat.class
).list()

 

unsaved-value(Hibernate2)

对于child方在映射配置时需要注意:

主键类型时字符串,由程序员自己指定主键值,那么unsaved-value值为any

<id name="propId" type="java.lang.String"?unsaved-value="any">
<generator class="assigned"/>
</id>

主键类型时字符串,由Hibernate自己指定主键值,那么unsaved-value值须为null,

<id name="id" type="string" unsaved-value="null" >
<generator class="uuid.hex"/>
</id>

unsaved-value的值必须指定为该字段类型的缺省值,主键类型是对象型的,null是缺省,而如果主键类型是整数型等原始类型,那么是0;

unsaved-value允许的取值包括:
any - always save 永远保存
none - always update 永远更新
null - 当标识符是空的时候保存(默认情况)
valid identifier value (合法的标识符值)- 当标识符是null或者这个给定的值时保存

 

Hibernate事务机制

JDBCTransaction或JTATransactio hibernate.properties默认情况下使用JDBCTransactio

JTA提供跨数据库事务(基于XA协议);
1.跨JDBC连接事务;
2.提供2 PC;
3.可以跨JMS和数据库;
4.分布式事务。
在EJB中容器自动提供,在Web层自己实现。

JDBC事务就是在一个JDBC连接开始到连接结束。简单很快结束,没有JTA那么长。

 

JTA事务

javax.transaction.UserTransactio tx = new InitialContext().lookup("javax.transaction.UserTransaction");
Sessio s1 = sf.openSession(); ...
s1.flush();
s1.close(); ...
Sessio s2 = sf.openSession(); ...
s2.flush();
s2.close();
tx.commit();

提示:在JTA上使用Hibernate 写JTA的Transaction代码,不要写Hibernate的Transaction代码,否则程序会报错 。

提示:需要使用JTA,最好使用EJB的Sessio Bean等容器管理事务,这时无需写任何事务代码

 

版本校验

当更新服务器端一个数据时,是基于先前场景读取的数据,这个数据也许是脏数据。

如何判断需要更新的数据是脏数据呢?

1. 版主A在一个事务中读取帖子X

2. 版主B在一个事务中读取帖子X

3. 版主A基于读取的帖子进行更新

4. 版主B基于读取的帖子也进行更新。

当第3步成功发生后,版主B场景中帖子就成了脏数据。

 

select for update 事务

数据库事务分悲观pessimistically事务和乐观optimistically事务。

在数据库悲观事务情况下,只有一个客户端可以修改记录,直至它修改完毕,才允许其他客户端操作。性能慢,很少使用。

数据库乐观事务情况下,需要配合version版本校验防止脏数据

 

Hibernate版本校验

版本校验是使用一个版本号、时间标识来诊断修改冲突(或者阻止修改丢失)。

 

Hibernate使用乐观并发两种使用方式,版本校验不断在长会话事务场景中有效,也可以在单个数据库事务中防止修改丢失。

 

版本校验的原理

简单使用一个整数型字段如vn作为对象的成员字段。它是用来表明任何时刻对象持久的状态。

1. 在一个读取事务中,将vn和和其他数据从实体bean中获取

2. 将vn和修改后的数据再发回实体

3. 当实体进行更新操作时,增加其vn值。

4.如果vn不匹配,拒绝更新。

Hibernate:Version属性是使用 <version>来配置映射的, Hibernate会在更新操作时自动增加这个值。

 

代码

sessio = factory.openSession();
Transactio t = session.beginTransaction();

int oldVersio = foo.getVersion();

//重新读取获得一个foo

session.load( foo, foo.getKey() );

if ( oldVersion!=foo.getVersio ) throw new StaleObjectStateException(); foo.setProperty("bar");

t.commit();

session.close();

 

session-per-conversation与自动校验

session-per-conversatio 也称为Extended session,一个Session实例和它持久的实例将在整个会话过程一直被使用。

Hibernate将在flush时检查版本,如果并发修改发生将抛错。

The Sessio 在等待前台用户输入时,将断开JDBC连接,这样我们应用程序就不需关系版本检验,也无需reattaching detached 持久实例,也不必在每次数据操作前再加载一次这些实例。需要我们自己保存session.

 

 

session-per-conversation代码

// foo is a instance loaded earlier by the old

sessio Transactio t = session.beginTransaction();

// Obtai a new JDBC connection, start transaction

foo.setProperty("bar");

// Only for last transactio i conversatio

session.flush();

t.commit(); // Also retur JDBC connectio

// Only for last transactio i conversatio

session.close();

 

Detached objects和自动版本校验

Session不重用,在每次新的session中重用持久的对象,应用程序自己就必须将和之前Session脱离关系的实例重新和当前Session联系起来,使用Session.update(), Session.saveOrUpdate(), or Session.merge().

Hibernate将会在flush时检查版本,如果发生修改冲突,抛出Exception.

如果我们确定内存中这些对象没有被修改,可以忽视内存,使用 LockMode.READ 进行版本更新。

 

Detached objects

// foo is a instance loaded by a previous Sessio foo.setProperty("bar");

sessio = factory.openSession();

Transactio t = session.beginTransaction();

// Use merge() if "foo" might have bee loaded already session.saveOrUpdate(foo);

t.commit(); session.close();

 

缓存

Hibernate的缓存包括Session的缓存和SessionFactory的缓存。

其中SessionFactory的缓存又可以分为两类:内置缓存和外置缓存。

Session的缓存是内置的,不能被卸载,也被称为Hibernate的第一级缓存。

 

Session的缓存

由于Session对象的生命周期通常对应一个数据库事务或者一个应用事务,因此它的缓存是事务范围的缓存。

第一级缓存是必需的,不允许而且事实上也无法比卸除。在第一级缓存中,持久化类的每个实例都具有唯一的OID

 

一级缓存

显式:

Query.setCacheable(true); getHibernateTemplate().setCacheQueries(true);

public List getList(){
getHibernateTemplate().setCacheQueries(true);
retur getHibernateTemplate().find("from Country");
}

 

SessionFactory内置缓存

内置缓存中存放了映射元数据和预定义SQL语句,映射元数据是映射文件中数据的拷贝,而预定义SQL语句是在Hibernate初始化阶段根据映射元数据推导出来,SessionFactory的内置缓存是只读的 。

SessionFactory不需要进行内置缓存与映射文件的同步

 

外置缓存

外置缓存是一个可配置的插件。在默认情况下,SessionFactory不会启用这个插件。

外置缓存的数据是数据库数据的拷贝,外置缓存的介质可以是内存或者硬盘。

SessionFactory的外置缓存也被称为Hibernate的第二级缓存。

 

 

二级缓存策略

1.条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象ID集合。

2 把获得的所有数据对象根据ID放入到第二级缓存中。

3 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。

4 删除、更新、增加数据的时候,同时更新缓存。

 

 

二级缓存配置

Hibernate.cfg.xml加入如下配置:

<property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider</property>
<property name="hibernate.cache.use_query_cache">
<prop key=“hibernate.cache.use_second_level_cache”>true</prop>
true</property>

 

Mapping配置加入:

<cache usage="read-write"/>

read-write表示插入时也使用缓存。

使用session.createQuery测试。Criteria.setCachable(true);

 

Out-of-memory

使用下面语句执行100 000 次操作

Sessio sessio = sessionFactory.openSession();

Transactio tx = session.beginTransaction();

for ( int i=0; i<100000; i++ ) {

Customer customer = new Customer(.....);?session.save(customer);

} tx.commit(); session.close();

最新数据插入二级缓存,因为(<cache usage=“read-write”/>配置)将出现OutOfMemoryExceptio 错误,内存益处。

 

批处理策略

设置:
hibernate.jdbc.batch_size 20
hibernate.cache.use_second_level_cache false

或使用CacheMode 来失效setCacheMode(CacheMode.IGNORE)

使用特定语句if ( i % 20 == 0 ) { //20, same as the JDBC batch size
//flush a batch of inserts and release memory:
session.flush();
session.clear(); }

 

对象很大?

对象很大是否浪费内存?

1. 对象是模型对象,是从领域建模而来,不会存在一个很长字段的对象,只会存在嵌入其他对象的对象。

2. 嵌入引用的其他对象由于代理懒加载机制,不会真正装载到内存中。

 

HQL Query

使用join给关联方取别名,甚至是集合中的值

from Cat as cat inner join cat.mate as mate left outer join cat.kittens as kitte

联合和join语法,四种形式:

1. inner join 2. left outer join

3. right outer join?4. full join

代码:更多见hibernate手册

显式和隐式:from Cat as cat where cat.mate.name like '%s%'

 

 

Fetch join

“fetch” join允许关联方活集合值能够和父对象在select时一起被初始化。

from Cat as cat inner join fetch cat.mate left join fetch cat.kittens

fetch all properties :如果使用懒加载,可以使用这个语法一次性将所有属性都加载:

from Document fetch all properties order by name

 

Fetching 策略

当我们应用程序需要读取关联子对象时,Fetching 策略用来决定如何读取那些关联子对象。

Fetching 策略可以在配置文件中配置, 也可以使用HQL或Criteria显式实现。

种类: Joi fetching?Select fetching Subselect fetching?Batch fetching

 

Fetching 策略:Join fetching

在配置中使用fetch=“join” ,Hibernate将在同一个select中返回关联子对象或集合。

Oracle 或 Sybase类型outer joins能够提供性能,Outer join fetching 允许无论关联是many-to-one, one-to-many, many-to-many 和 one-to-one 都能够在一个SELECT?SQL语句中抓取获得.

Outer join fetching 可以通过设置may be disabled hibernate.max_fetch_depth 为 0全局失效. 设置为1 或更高数字将激活one-to-one 或many-to-one 关联,当然必须关联行配置使用etch="join".

 

 

Fetching 策略:Select fetching

在关联配置行中使用fetch=“select” ,这表示Hibernate将在第二个SELECT中获取关联方实体或集合,除非你显式地实现lazy fetching (lazy=“false”)。

第二次Select只有你在访问这个关联时才会实现。

fetch 和lazy结合,lazy解决关联方什么时候实现抓取,fetch是解决怎样实现抓取(一条SQL还是两条),

 

性能优化:batch fetching

Batch fetching是对于櫴加载抓取策略的一种优化。

Cat 和Perso 是多对一关系,25 个子对象Cat 集合,遍历Cat 集合通过getOwner 获得Perso 时,Hibernate 将执行25 次SQL 语句。通过规定:

<class name="Person" batch-size="10">...</class>

Hibernate 将执行3 次,分别是10 10 5

 

性能优化:集合的batch fetching

Perso 有櫴加载cats集合,如果10个Persons对象被加载到session,遍历这10个时,Hibernate产生10次select,每次调用getCats

<class name="Person"> <set name="cats" batch-size="3"> ... </set> </class>

Hibernate遍历Person时,将预先首先获取batch-size大小Cats, 将实现4次select: 3 3 3 1

 


 

首页

更多Hibernate专题