??xml version="1.0" encoding="utf-8" standalone="yes"?> 另外Q你不愿意你的DAO试代码每次都打开关系SessionQ因此,我们一般会采用OpenSessionInView模式?
Z么绑定以后,可以防止每ơ不会新开一个Session呢?看看HibernateDaoSupport的情况: 我们的DAO用这个templateq行操作Q? public Object doInHibernate(Session session) }); public void save(BaseEntityObject entity) { public void remove(BaseEntityObject entity) { getHibernateTemplate().delete(entity); public void refresh(final BaseEntityObject entity) { public Object doInHibernate(Session session) }); public void replicate(final Object entity) { public Object doInHibernate(Session session) }); 使用同样的方法,q两个Interceptor可以用来解决问题。但是关键的不同之处在于Q它们的力度只能定义在DAO或业务方法上Q而不是在我们的TestҎ上,除非我们把它们应用到TestCase的方法上Q但你不大可能ؓTestCased义一个接口,然后把Interceptor应用到这个接口的某些Ҏ上。直接用HibernateTransactionManager也是一L。因此,如果我们有这L试Q?
Category childCategory = new Category(); parentCategory.addChild(childCategory); Category savedParent = dao.getCategory("parent"); 一U方法是对TestCase应用Interceptor或者TransactionManagerQ但q个恐怕会造成很多ȝ。除非是使用增强方式的AOP.我前期采用这U方?Aspectwerkz)Q在Eclipse里面也跑得含好?
另一U方法是在TestCase的setup和teardown里面实现和Filter完全一L处理Q其他的TestCase都从q个TestCasel承Q这U方法是我目前所使用的?
它有两种配置方式OpenSessionInViewInterceptor?span style="font-size: 10pt; color: #4b4b4b; font-family: Verdana;">OpenSessionInViewFilter(具体参看SpringSide)
Hibernate的Lazy初始?:n关系Ӟ你必M证是在同一个Session内部使用q个关系集合Q不然Hiernate抛Z外?
OpenSessionInViewFilter解决Web应用E序的问?
如果E序是在正常的WebE序中运行,那么Spring?strong style="color: black; background-color: #ffff66;">OpenSessionInViewFilter能够解决问题Q它Q?
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
SessionFactory sessionFactory = lookupSessionFactory();
logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
Session session = getSession(sessionFactory);
TransactionSynchronizationManager.bindResource(sessionFactory,
new SessionHolder(session));
try {
filterChain.doFilter(request, response);
}
finally {
TransactionSynchronizationManager.unbindResource(sessionFactory);
logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
closeSession(session, sessionFactory);
}
}public final void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
protected final HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}public abstract class BaseHibernateObjectDao
extends HibernateDaoSupport
implements BaseObjectDao {
protected BaseEntityObject getByClassId(final long id) {
BaseEntityObject obj =
(BaseEntityObject) getHibernateTemplate()
.execute(new HibernateCallback() {
throws HibernateException {
return session.get(getPersistentClass(),
new Long(id));
}
return obj;
}
getHibernateTemplate().saveOrUpdate(entity);
}
try {
} catch (Exception e) {
throw new FlexEnterpriseDataAccessException(e);
}
}
getHibernateTemplate().execute(new HibernateCallback() {
throws HibernateException {
session.refresh(entity);
return null;
}
}
getHibernateTemplate().execute(new HibernateCallback() {
throws HibernateException {
session.replicate(entity,
ReplicationMode.OVERWRITE);
return null;
}
}public Object execute(HibernateCallback action) throws DataAccessException {
Session session = (!this.allowCreate ?
SessionFactoryUtils.getSession(getSessionFactory(),
false) :
SessionFactoryUtils.getSession(getSessionFactory(),
getEntityInterceptor(),
getJdbcExceptionTranslator()));
boolean existingTransaction =
TransactionSynchronizationManager.hasResource(getSessionFactory());
if (!existingTransaction && getFlushMode() == FLUSH_NEVER) {
session.setFlushMode(FlushMode.NEVER);
}
try {
Object result = action.doInHibernate(session);
flushIfNecessary(session, existingTransaction);
return result;
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
catch (SQLException ex) {
throw convertJdbcAccessException(ex);
}
catch (RuntimeException ex) {
// callback code threw application exception
throw ex;
}
finally {
SessionFactoryUtils.closeSessionIfNecessary(
session, getSessionFactory());
}
}public static void closeSessionIfNecessary(Session session,
SessionFactory sessionFactory)
throws CleanupFailureDataAccessException {
if (session == null ||
TransactionSynchronizationManager.hasResource(sessionFactory)) {
return;
}
logger.debug("Closing Hibernate session");
try {
session.close();
}
catch (JDBCException ex) {
// SQLException underneath
throw new CleanupFailureDataAccessException(
"Cannot close Hibernate session", ex.getSQLException());
}
catch (HibernateException ex) {
throw new CleanupFailureDataAccessException(
"Cannot close Hibernate session", ex);
}
}HibernateInterceptor和OpenSessionInViewInterceptor的问?
Category parentCategory = new Category ();
parentCategory.setName("parent");
dao.save(parentCategory);
childCategory.setName("child");
dao.save(childCategory);
Category savedChild = (Category ) savedParent.getChildren().get(0);
assertEquals(savedChild, childCategory);
解决Ҏ
]]>
Open Session In View?/span>request?/span>sessionl定到当?/span>thread期间一直保?/span>hibernate session?/span>open状态,?/span>session?/span>request的整个期间都可以使用Q如?/span>View层里PO也可?/span>lazy loading数据Q如 ${ company.employees }。当View 层逻辑完成后,才会通过Filter?/span>doFilterҎ?/span>Interceptor?/span>postHandleҎ自动关闭session?/span>
OpenSessionInViewInterceptor配置
<beans> <bean name="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor"> <property name="sessionFactory"> <ref bean="sessionFactory"/> </property> </bean> <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="interceptors"> <list> <ref bean="openSessionInViewInterceptor"/> </list> </property> <property name="mappings"> ... </property> </bean> ... </beans>
OpenSessionInViewFilter配置
<web-app> ... <filter> <filter-name>hibernateFilter</filter-name> <filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter </filter-class> <!-- singleSession默认为true,若设为false则等于没?span class="me1">OpenSessionInView --> <init-param> <param-name>singleSession</param-name> <param-value>true</param-value> </init-param> </filter> ... <filter-mapping> <filter-name>hibernateFilter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> ... </web-app>
很多人在使用OpenSessionInViewq程中提及一个错误:
org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into FlushMode.AUTO or remove 'readOnly' marker from transaction definition
看看OpenSessionInViewFilter里的几个Ҏ
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,FilterChain filterChain)
throws ServletException, IOException {
SessionFactory sessionFactory = lookupSessionFactory();
logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
Session session = getSession(sessionFactory);
TransactionSynchronizationManager.bindResource(
sessionFactory, new SessionHolder(session));
try {
filterChain.doFilter(request, response);
}
finally {
TransactionSynchronizationManager.unbindResource(sessionFactory);
logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
closeSession(session, sessionFactory);
}
}
protected Session getSession(SessionFactory sessionFactory)
throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
session.setFlushMode(FlushMode.NEVER);
return session;
}
protected void closeSession(Session session, SessionFactory sessionFactory)
throws CleanupFailureDataAccessException {
SessionFactoryUtils.closeSessionIfNecessary(session, sessionFactory);
}
可以看到OpenSessionInViewFilter在getSession的时?会把获取回来的session的flush mode 设ؓFlushMode.NEVER。然后把该sessionFactoryl定? TransactionSynchronizationManagerQrequest的整个过E都使用同一个sessionQ在hq后再接除该 sessionFactory的绑定,最?span class="me1">closeSessionIfNecessaryҎ? session是否已和transactionl定来决定是否关闭session。在q个q程中,若HibernateTemplate 发现自当前session有不是readOnly的transactionQ就会获取到FlushMode.AUTO SessionQҎ拥有写权限?/p>
public static void closeSessionIfNecessary(Session session, SessionFactory sessionFactory) throws CleanupFailureDataAccessException { if (session == null ||
TransactionSynchronizationManager.hasResource(sessionFactory)) { return; } logger.debug("Closing Hibernate session"); try { session.close(); } catch (JDBCException ex) { // SQLException underneath throw new CleanupFailureDataAccessException("Could not close Hibernate session", ex.getSQLException()); } catch (HibernateException ex) { throw new CleanupFailureDataAccessException("Could not close Hibernate session", ex); } }
也即是,如果有不是readOnly的transaction可以由Flush.NEVER转ؓFlush.AUTO,拥有insert, update,delete操作权限Q如果没有transactionQƈ且没有另外h为地设flush model的话Q则doFilter的整个过E都是Flush.NEVER。所以受transaction保护的方法有写权限,没受保护的则没有?/p>
采用spring的事务声?使方法受transaction控制
<bean id="baseTransaction"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManager"/>
<property name="proxyTargetClass" value="true"/>
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
- <bean id="userService" parent="baseTransaction">
<property name="target">
<bean class="com.phopesoft.security.service.impl.UserServiceImpl"/>
</property>
</bean>
? 于上例,则以save,add,update,remove开头的Ҏ拥有可写的事务,如果当前有某个方法,如命名ؓimportExcel()Q则因没 有transaction而没有写权限Q这时若Ҏ内有insert,update,delete操作的话Q则需要手动设|flush model为Flush.AUTO,?/p>
session.setFlushMode(FlushMode.AUTO); session.save(user); session.flush();
管Open Session In View看v来还不错Q其实副作用不少。看回上面OpenSessionInViewFilter的doFilterInternalҎ代码Q这个方? 实际上是被父cȝdoFilter调用的,因此Q我们可以大U了解的OpenSessionInViewFilter调用程: request(h)->open sessionq开始transaction->controller->View(Jsp)->l束transactionq? close session.
一切看h很正,其是在本地开发测试的时候没出现问题Q但试想下如果流E中的某一步被d的话Q那在这期间connection׃直被占用而不? 放。最有可能被d的就是在写Jspq步Q一斚w可能是页面内容大Qresponse.write的时间长Q另一斚w可能是网速慢Q服务器与用户间传输? 间久。当大量q样的情况出现时Q就有连接池q接不Q造成面假死现象?/p>
Open Session In View是个双刃剑,攑֜公网上内容多量大的|站h用?/p>
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1441664
1、未被持久化的VO
此时是一个内存对象VOQ由JVM理生命周期
2、已被持久化的POQƈ且在Session生命周期?
此时映射数据库数据,由数据库理生命周期
3、曾被持久化q,但现在和Session已经detached了,以VO的n份在q行
q种和Session已经detached的POq能够进入另一个SessionQl进行PO状态管理,此时它就成ؓPO的第二种状态了?span style="color: red">q种PO实际上是跨了Sessionq行了状态维护的?/span>
在传l的JDO1.x中,PO只有前面两种状态,一个PO一旦脱PMQ就丧失了状态了Q不再和数据库数据关联,成ؓ一个纯_的内存VOQ它即ɘq入一个新的PMQ也不能恢复它的状态了?/p>
Hibernate强的地方在于,一个POqSession之后Q还能保持状态,再进入一个新的Session之后Q就恢复状态管理的能力Q但此时状态管理需要用session.update或者session.saveOrUpdateQ这是Hibernate Reference中提到的“requires a slightly different programming model ”
现在正式q入本话题:
单的来说Qupdate和saveOrUpdate是用来对跨Session的POq行状态管理的?/span>
假设你的PO不需要跨Session的话Q那么就不需要用刎ͼ例如你打开一个SessionQ对POq行操作Q然后关闭,之后q个PO你也不会再用CQ那么就不需要用update?/p>
因此Q我们来看看Q?
PO对象foo的操作都在一个Session生命周期内完成,因此不需要显式的q行sess.update(foo)q样的操作。Hibernate会自动监到foo对象已经被修改过Q因此就向数据库发送一个update的sql。当然如果你非要加上sess.update(foo)也不会错Q只不过q样做没有Q何必要?/p>
而跨Session的意思就是说q个PO对象在Session关闭之后Q你q把它当做一个VO来用Q后来你在Session外面又修改了它的属性,然后你又x开一个SessionQ把VO的属性修改保存到数据库里面,那么你就需要用update了?/p>
cat和mate对象是在W一个session中取得的Q在W一个session关闭之后Q他们就成了PO的第三种状态,和Session已经detached的POQ此时他们的状态信息仍然被保留下来了。当他们q入W二个session之后Q立d可以q行状态的更新。但是由于对cat的修Ҏ作:cat.setMate(potentialMate); 是在Session外面q行的,Hibernate不可能知道cat对象已经被改q了Q第二个Sessionq不知道q种修改Q因此一定要昑ּ的调用secondSession.update(cat); 通知HibernateQcat对象已经修改了,你必d送update的sql了?/p>
所以update的作用就在于此,它只会被用于当一个PO对象跨Sessionq行状态同步的时候才需要写。而一个PO对象当它不需要跨Sessionq行状态管理的时候,是不需要写update的?/p>
再谈谈saveOrUpdate的用场:
saveOrUpdate和update的区别就在于在跨Session的PO状态管理中QHibernate对PO采取何种{略?/p>
例如当你写一个DAOImpl的时候,让cat对象增加一个mateQ如下定义:
昄你是需要把Hibernate的操作封装在DAO里面的,让业务层的程序员和Web层的E序员不需要了解HibernateQ直接对DAOq行调用?/p>
此时问题来了:上面的代码运行正有一个必要的前提Q那是Ҏ调用参数cat对象必须是一个已l被持久化过的POQ也是来说Q它应该首先从数据库查询出来Q然后才能这L。但是业务层的程序员昄不知道这U内部的玄妙Q如果他的业务是现在增加一个catQ然后再增加它的mateQ他昄会这栯用,new一个cat对象出来Q然后就addMateQ?/p>
但是h意看Q这个cat对象只是一个VOQ它没有被持久化q,它还不是POQ它没有资格调用addMateҎQ因此调用addMateҎ不会真正往数据库里面发送update的sqlQ这个cat对象必须先被save到数据库Q在真正成ؓ一个PO之后Q才具备addMate的资根{?/p>
你必这h操作Q?/p>
先持久化catQ然后才能对catq行其他的持久化操作。因此要求业务层的程序员必须清楚cat对象处于何种状态,到底是第一U,q是W三U。如果是W一U,p先saveQ再addMateQ如果是W三U,q接addMate?/p>
但是最致命的是Q如果整个Y件分层很多,业务层的E序员他拿到q个cat对象也可能是上层Web应用层传递过来的catQ他自己也不知道q个catI竟是VOQ没有被持久化过Q还是已l被持久化过Q那么他Ҏ没有办法写E序了?/p>
所以这LDAOImpl昄是有问题的,它会对业务层的程序员造成很多~程上的陷阱Q业务层的程序员必须深刻的了解他调用的每个DAO对PO对象q行了何U状态管理,必须深刻的了解他的PO对象在Q何时候处于什么确切的状态,才能保证~程的正性,昄q是做不到的Q但是有了saveOrUpdateQ这些问题就q刃而解了?/p>
现在你需要修改addMateҎQ?/p>
如上Q如果业务层的程序员传进来的是一个已l持久化q的PO对象Q那么Hibernate会更新cat对象(假设业务层的E序员在Session外面修改qcat的属?Q如果传q来的是一个新new出来的对象,那么向数据库saveq个PO对象?/p>
BTW: Hibernate此时I竟采取更新cat对象Q还是save cat对象Q取决于unsave-value的设定?/p>
q样Q业务层的程序员׃必再操心PO的状态问题了Q对于他们来_不管cat是new出来的对象,只是一个VO也好Q还是从数据库查询出来的的PO对象也好Q全部都是直接addMateOK了:
q便是saveOrUpdate的作用?br />
Robbin老大的精?. http://www.javaeye.com/topic/2712?page=1