当前位置: 首页 > 图文教程 > Java技术 > 数据库持久层 > Hibernate基础

数据库持久层
hibernate的11大优势
Hibernate的级联删除
别让Hibernate偷走了你的标识符
数据持久层三种持久化方法
Hibernate使用总结
hibernate要点
Hibernate基础
优化Hibernate性能的几点建议
Hibernate优化方案
别让Hibernate偷走了您的身份(一)
别让Hibernate偷走了您的身份(二)
Hibernate的发展之路
Hibernate包作用详解
EJB 3.0是Hibernate的克隆吗?
分析Hibernate的事务处理机制
使用Hibernate处理数据
Hibernate 和 iBATIS
数据库持久层:如何学习Hibernate
数据库持久层:用hbm2java生成Hibernate类
数据库持久层:Hibernate延时加载和机制理解

数据库持久层 中的 Hibernate基础


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-08-14   浏览: 80 ::
收藏到网摘: n/a

Part 1

1.      持久化对象

Event event = new Event();

// populate the event

Session session = factory.openSession();

session.save(event);

session.flush();

session.close();

当保存一个对象的时候,hibernate出于性能的考虑不会马上将改对象写到db。如果想要强制写,就要用flush()方法。

经常将save和update合在一起使用,为saveOrUpdate()。Hibernate根据对象的id是null(或0),还是已经存在来判断应该save还是update。

 

 

2.      读取对象

根据id

Event event = (Event) session.load(Event.class, eventId);

session.close();

使用hql

Query query = session.createQuery("from Event");

List events = query.list();

 

 

3.      会话缓存session cache

出于性能的考虑,默认情况hibernate并不将一个操作立即反映到db中,而是做一些缓存的处理。

对一个会话期间的被load或save的每个对象,都能支持一个相应的缓存。

比如可以在一次会话中对对象a做load,update等多个处理,最后才flush提交,如

Session session = factory.openSession();

Event e = (Event) session.load(Event.class, myEventId);

e.setName("New Event Name");

session.saveOrUpdate(e);

// later, with the same Session instance

Event e = (Event) session.load(Event.class, myEventId);

e.setDuration(180);

session.saveOrUpdate(e);

session.flush();

缓存会带来一些问题:

初学者容易犯NonUniqueObjectException,即在一个会话中对同一个对象做了不同步的操作,比如:

Session session = factory.openSession();

Event firstEvent = (Event) session.load(Event.class, myEventId);

// ... perform some operation on firstEvent

Event secondEvent = new Event();

secondEvent.setId(myEventId);

session.save(secondEvent);

可以看到secondEvent是一个与firstEvent同ID的对象,最后却使用save,而不是update,显然不对了。

 

对每个“经过”了会话的对象,都会被加到会话的缓存中。

“经过”的含义:保存对象,读取对象。

session.contains()可以检查某个对象是否在缓存中。

session.evict()可以将对象从缓存中清除。

session.clear()可以将所有对象从缓存清除。

Session session = factory.openSession();

Event firstEvent = (Event) session.load(Event.class, myEventId);

// ... perform some operation on firstEvent

if (session.contains(firstEvent)) {

session.evict(firstEvent);

}

Event secondEvent = new Event();

secondEvent.setId(myEventId);

session.save(secondEvent);

 

 

Part 2

 

1.      连接池connection pools

出于性能的考虑,不能为每一个到数据库的请求,都给一个连接。而是使用连接池。

连接池保存了可以重用的一组到数据库的连接。

应用服务器通常通过JNDI数据源datasource,提供自己的连接池支持,hibernate利用了服务器的这个特性。并且对没有连接池支持的服务器也有相关支持,参见C3P0。

 

 

2.      事务

有的服务器支持简单的JDBC事务,有的则能支持java transaction api(JTA)。

Jdbc和jta是两种事务策略,到底使用哪种策略,可以在hibernate.cfg.xml设置。Jta的好处是可以允许你将多个独立的事务当作一个事务对待。

在Hibernate中,对多个事务的处理是这样的:

Transaction tx0 = session.beginTransaction();

Event event = new Event();

// ... populate the event instance

session.saveOrUpdate(event);

Transaction tx1 = session.beginTransaction();

Location location = new Location();

// ... populate the Location instance

session.saveOrUpdate(location);

tx0.commit();

tx1.commit();

上面,用一个会话创建了两个事务,但是无论哪个事务的操作都会当作是第一个事务的操作来处理。显然,是一个问题。(利用jta?)

<property name="transaction.factory_class">

org.hibernate.transaction.JTATransactionFactory

</property>

<property name="jta.UserTransaction">

java:comp/UserTransaction

</property>

当前,默认是使用jdbc的。

事务的一个示例:

Session session = factory.openSession();

Transaction tx = session.beginTransaction();

Event event = new Event();

// ... populate the Event instance

session.saveOrUpdate(event);

tx.commit();

注意:这里没有使用flush方法来强制将event写入db,因为提交操作commit时会完成写入。

 

Cache提供者provider

未完.........................................

 

 

 

Part 3 HQL

 

1.      Hql具有properties:

Id和class

使用id可以引用对象的primary key,而不论你实际使用的是什么名字,例如:

from MyObject m where m.id > 50

查询所有主健大于50的。

class是对象的完整java名字,如:

from Attendee a join a.payment p where p.class =

com.manning.hq.ch06.CashPayment

class属性在对象树结构中很有用。

 

2.      表达式

hql支持通常的sql表达式,比如:

size:返回子集合中的元素个数

    from Event e where size(e.attendees) > 0

对有序集合:

 

支持的逻辑操作:

and, any, between, exists, in,

like, not, or, and some

支持的比较操作:

=, >, <, >=,

<=, and <>

 

3.      条件查询criteria query

条件查询为查询提供了灵活性。当查询参数的数目不定时,使用。

但是,条件查询不支持聚集函数,并且只能得到这个对象,而不能只得到部分。

即,条件查询没有hql的全部功能,但是提高了灵活性。

两种使用方式:

List results = session.createCriteria(Event.class).

.add( Restrictions.between("duration", new Integer(60),

new Integer(90) )

.add( Restrictions.like("name", "Presen%") )

.addOrder( Order.asc("name") )

.list();

以及:

Criteria criteria = session.createCriteria(Event.class);

criteria.add(Restrictions.between("duration",

new Integer(60), new Integer(90) );

criteria.add( Restrictions.like("name", "Presen%") );

criteria.addOrder( Order.asc("name") );

List results = criteria.list();

 

 

Part 4 利用spring和dao

 

1.      Dao

为了将所有的hql(好处显然:管理),有如下分解,将对象与db见加入一个专门处理持久化的对象dao。

可以为每一个类建立一个dao,可以为一个应用建立一个dao,建议前者。

 

简单dao:

为具体对象承担了如下责任:

每一个操作一个会话;

每一个操作一个事务,并负责打开和关闭事务;

处理异常;

客户代码不必考虑对象cast。

从Dao程序片断体会上面的责任:

public class SimpleEventDao {

Log log = LogFactory.getLog(SimpleEventDao.class);

private Session session;

private Transaction tx;

public SimpleEventDao() {

HibernateFactory.buildIfNeeded();

}

public void delete(Event event)

throws DataAccessLayerException {

try {

startOperation();

session.delete(event);

tx.commit();

} catch (HibernateException e) {

handleException(e);

} finally {

HibernateFactory.close(session);

}

}

...

}

注意:其他真正的功能代码只有一行session.delete(event),其他代码被称为excise税,消费税。编程时的内存管理是典型的税代码,java帮我们上了税,程序员就用再管了。

 

2.      层次化的dao

其他的CRUD操作都是类似上面的结构,

因此:可以在简单dao中不同的方法里看到结构和内容重复的代码。

所以,需要简化dao,见下:

将公共行为提取到父类。

父类中的delete片断,可以对比前面的delete,区别只在参数一个是具体对象,一个是Object,而对象的cast问题就交给子类dao。

protected void delete(Object obj) {

try {

startOperation();

session.delete(obj);

tx.commit();

} catch (HibernateException e) {

handleException(e);

} finally {

HibernateFactory.close(session);

}

}

 

 

dao还要一些问题:因为每个操作一个会话,一个事务,因此一个按id号更新对象的过程,因为有两个操作find和update,所以使用了两个会话(每个会话又分别使用一事务)来完成,如下:

Event foundEvent = eventDao.find(event.getId());

foundEvent.setDuration(30);

eventDao.update(foundEvent);

但是,从效率上来说,一个会话,一个事务就可以了。

 

 

3.      Spring的HibernateTemplate

Spring对hibernate的支持体现在为hibernate和重要的jdbc需求处理了资源管理税代码resource management excise。

前面谈到dao中有重复代码,重复代码可以通过重构的手段解决,而dao中的重复代码跟资源管理有关,因此Spring引入template来完成所有的资源处理部分,

Spring的HibernateTemplate帮我们完成了如下工作:

获取会话,

开始事务,

处理异常,

显式提交变化到db,

关闭会话。

可以看到,上面的流程就却那个起到功能作用的方法(如CRUD了,因此dao中可以简化为

protected void create(Event event) {

SessionFactory sf = HibernateFactory.getSessionFactory();

HibernateTemplate template = new HibernateTemplate(sf);

template.saveOrUpdate(event);

}

但是可以注意到上面的代码还是使用的一个操作一个事务的模式。

 

 

有两种方式与HibernateTemplate交互:

持久化方法和回调:

使用持久化方法的片断:

SessionFactory sessionFactory =

HibernateFactory.getSessionFactory();

HibernateTemplate template =

new HibernateTemplate(sessionFactory);

Event event1 = new Event();

event1.setName("Event 1");

Event event2 = new Event();

event2.setName("Event 2");

try {

template.save (event1);

template.save (event2);

Event obj = (Event) template.load(Event.class,

event1.getId());

System.out.println("Loaded the event" + obj.getName());

List events = (List) template.find("from Event");

System.out.println("# of Events " + events.size());

} finally {

template.delete(event1);

template.delete(event2);

}

 

不是所有的操作(如非CRUD)都可以简化为事务中的一个query。这时spring提供回调接口,写将在 HibernateTemplate中调用的回调函数。比如,有这样一个操作根据一个复杂查询的结果,更新结果集中的对象的属性,最后保存。这是一个复杂的操作,要前面的CRUD是无法完成的,因此利用HibernateTemplate可以这样做:

template.execute(new HibernateCallback() {

public Object doInHibernate(Session session)

throws HibernateException, SQLException {

Query query = session.createQuery("from Event");

query.setMaxResults(2);

List events = query.list();

for (Iterator it = events.iterator(); it.hasNext();) {

Event event = (Event) it.next();

event.setDuration(60);

}

return null;

}

});

HibernateTemplate的接口就是一个实现doInHibernate方法的

HibernateCallback对象。

Execute方法以HibernateCallback对象为参数,应该还是替客户代码处理了资源处理税代码的。

 

 

4.      Spring对java bean的配置和管理功能

Spring擅长配置和使用简单的java bean。Spring可以做为一个工厂factory来配置和建造bean。

基于上面的功能,spring可以用来配置configure很多已存在的结构和类库,比如hibernate。

 

Spring通过配置文件来管理bean。

配置文件指定了如何创建各种对象,包括Datasource,SessionFactory,所有的dao。

因此,可以从配置文件中:查找dao。

典型的spring配置文件ApplicaitonContext.xml如下:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC

"-//SPRING//DTD BEAN//EN"

"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

<bean id="dataSource"

class="org.apache.commons.dbcp.BasicDataSource"

destroy-method="close">

<property name="driverClassName">

<value>com.mysql.jdbc.Driver</value>

</property>

<property name="url">

<value>jdbc:mysql://localhost/events_calendar</value>

</property>

<property name="username">

<value>root</value>

</property>

<property name="password">

<value></value>

</property>

</bean>

<bean id="factory"

class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

<property name="mappingResources">

<list>

<value>com/manning/hq/ch07/Event.hbm.xml</value>

<value>com/manning/hq/ch07/Location.hbm.xml</value>

</list>

</property>

<property name="hibernateProperties">

<props>

<prop key="hibernate.dialect">

org.hibernate.dialect.MySQLDialect

</prop>

<prop key="hibernate.show_sql">false</prop>

</props>

</property>

<property name="dataSource">

<ref bean="dataSource"/>

</property>

</bean>

<bean id="eventDao"

class="com.manning.hq.ch07.EventSpringDao">

<property name="sessionFactory">

<ref bean="factory" />

</property>

</bean>

</beans>

如上,来分析spring的配置文件:

1处利用Apache Commons database connection pool (DBCP)定义了使用的数据源,DBCP是在hibernate中集成了的。

2处利用spring内建的LocalSessionFactoryBean创建一个SessionFactory。并在3处连接到数据源。

4处配置dao,并在5处将dao连接到SessionFactory,这让dao能够打开会话,处理查询。

 

Spring的applicationContext.xml可以替代hibernate的

hibernate.cfg.xml。

另外,有点spring比hibernate进步的地方:

对比前面的dao,在dao的构造函数中有这样的代码(来自hibernate quickly,其中HibernateFactory是hibernate quickly自己写的。)

public SimpleEventDao() {

HibernateFactory.buildIfNeeded();

}

dao利用这段代码会调用一个configureSessionFactory方法,根据hibernate的配置文件创建session,如下

private static SessionFactory configureSessionFactory() throws HibernateException {

        Configuration configuration = new Configuration();

        configuration.configure();

        sessionFactory = configuration.buildSessionFactory();

        return sessionFactory;

}

spring中不需要用代码显式创建SessionFactory,只要读

applicationContext.xml,其中有LocalSessionFactoryBean来处理创建

SessionFactory的问题。

 

结合下面的代码和上面SimpleEventDao的构造器

Event event = new Event();

event.setName("A new Event");

EventDao eventDao = new EventDao();

eventDao.create(event);

可以看到:使用dao的步骤是,先创建dao对象,然后dao对象会创建SessionFactory。

Spring中不用显示创建SessionFactory(这是第一点),

ClassPathXmlApplicationContext ctx = new

ClassPathXmlApplicationContext("applicationContext.xml");

EventSpringDao eventDao = (EventSpringDao) ctx.getBean("eventDao",

EventSpringDao.class);

Event event = new Event();

eventDao.saveOrUpdate(event);

注意:applicationContext.xml应该放在classpath里指定的path的root(似乎)。

 

总结一下就是:

在Hibernate中用户代码的责任:

创建dao对象,用new的方式,

dao对象创建SessionFactory对象,供dao中的CRUD操作使用。

而在spring中用户代码的责任:

创建dao对象,用getBean的方式(这似乎是一种create method方法,参见《重构与模式》中6.1《用Creation Method替换构造函数》)。

 

其实,上面提到“spring擅长配置和使用简单的java bean”,所以使用上面代码中的

ClassPathXmlApplicationContext.getBean()方法来完成。

 

 

Spring对层次化的dao的支持:

org.springframework.orm.hibernate3.support.HibernateDaoSupport。

其代码:

public abstract class HibernateDaoSupport

implements InitializingBean {

protected final Log logger;

private HibernateTemplate hibernateTemplate;

public final void

setSessionFactory(SessionFactory sessionFactory);

public final SessionFactory getSessionFactory();

public final void

setHibernateTemplate(HibernateTemplate hibernateTemplate);

public final HibernateTemplate getHibernateTemplate();

protected final Session getSession()

throws DataAccessResourceFailureException,

IllegalStateException;

protected final void closeSessionIfNecessary(Session session);

}

可以看到:HibernateDaoSupport的支持support体现在:

提供logger,以支持日志功能;

管理HibernateTemplate(有一个私有的hibernateTemplate);

管理SessionFactory(HibernateTemplate从HibernateAccessor继承了SessionFactory);

上面的代码虽然表明HibernateDaoSupport是抽象类,但是每个方式实际上有实现的(为什么?)。所以,关于 SessionFactory,Session,HibernateTemplate的操作可以直接在客户代码中使用,而不用重载然后实现之。所以从该类继承一个dao的话,可以简化操作,如:

public abstract class AbstractSpringDao

extends HibernateDaoSupport{

public AbstractSpringDao() { }

protected void saveOrUpdate(Object obj) {

getHibernateTemplate().saveOrUpdate(obj);

}

protected void delete(Object obj) {

getHibernateTemplate().delete(obj);

}

protected Object find(Class clazz, Long id) {

return getHibernateTemplate().load(clazz, id);

}

protected List findAll(Class clazz) {

return getHibernateTemplate().find(

"from " + clazz.getName());

}

}

其实,上面的类已经是可以实例化的类了,不必标记为abstract。

上面的dao再被继承为eventDao:一个dao应该有一个对应的session,dao的所有对数据库的操作都要基于这个session 里来完成(考察最初形式的dao可以看到Session类的变量),这个问题在配置文件中指明了一个SessionFactory来创建需要的 Session:

<bean id="eventDao" class="com.manning.hq.ch07.EventSpringDao>

<property name="sessionFactory">

<ref bean="factory" />

</property>

</bean>

 

 

5.      集成spring对hibernate的支持,简化代码

下面用一个类来集成spring对hibernate的支持

public class CalendarRegistry {

private static ApplicationContext ctx;

static {

ctx = new ClassPathXmlApplicationContext(

"applicationContext.xml");

}

private CalendarRegistry() {

}

public static SessionFactory getSessionFactory() {

return (SessionFactory) ctx.getBean(

"factory", SessionFactory.class);

}

public static EventSpringDao getEventDao() {

return (EventSpringDao)ctx.getBean(

"eventDao", EventSpringDao.class);

}

}

客户代码:

EventSpringDao eventDao = CalendarRegistry.getEventDao();

eventDao.saveOrUpdate(event);