`

Hibernate基础

 
阅读更多

Hibernate基础
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);
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics