- 浏览: 413276 次
- 性别:
- 来自: 广州
文章分类
最新评论
-
liyuanhoa_:
...
struts2.0中struts.xml配置文件详解 -
chenmingde:
...
Velocity应用(一) -
weizhikai_ai:
第二十六,当一个线程进入一个对象的一个synchronized ...
Java常见面试题(含答案) -
Aurora_lr:
...
Spring宠物商店学习笔记(一) - -
zs911zs:
all copy from http://www.iteye ...
Mule入门文档
相关文章:
hibernate更新实体的问题
Hibernate in action (one-to-many/many-to-one)(疑惑)
总结一下最近关于domain object以及相关的讨论
推荐圈子: JBPM @net
更多相关推荐 OpenSessionInViewFilter是Spring提供的一个针对Hibernate的一个支持类,其主要意思是在发起一个页面请求时打开Hibernate的Session,一直保持这个Session,直到这个请求结束,具体是通过一个Filter来实现的。
由于Hibernate引入了Lazy Load特性,使得脱离Hibernate的Session周期的对象如果再想通过getter方法取到其关联对象的值,Hibernate会抛出一个LazyLoad的Exception。所以为了解决这个问题,Spring引入了这个Filter,使得Hibernate的Session的生命周期变长。
首先分析一下它的源码,可以发现,它所实现的功能其实比较简单:
Java代码
SessionFactory sessionFactory = lookupSessionFactory(request);
Session session = null;
boolean participate = false;
if (isSingleSession()) {
// single session mode
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
// Do not modify the Session: just set the participate flag.
participate = true;
} else {
logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
session = getSession(sessionFactory);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
} else {
// deferred close mode
if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
// Do not modify deferred close: just set the participate flag.
participate = true;
} else {
SessionFactoryUtils.initDeferredClose(sessionFactory);
}
}
try {
filterChain.doFilter(request, response);
} finally {
if (!participate) {
if (isSingleSession()) {
// single session mode
TransactionSynchronizationManager.unbindResource(sessionFactory);
logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");
closeSession(session, sessionFactory);
}else {
// deferred close mode
SessionFactoryUtils.processDeferredClose(sessionFactory);
}
}
}
SessionFactory sessionFactory = lookupSessionFactory(request);
Session session = null;
boolean participate = false;
if (isSingleSession()) {
// single session mode
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
// Do not modify the Session: just set the participate flag.
participate = true;
} else {
logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
session = getSession(sessionFactory);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
} else {
// deferred close mode
if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
// Do not modify deferred close: just set the participate flag.
participate = true;
} else {
SessionFactoryUtils.initDeferredClose(sessionFactory);
}
}
try {
filterChain.doFilter(request, response);
} finally {
if (!participate) {
if (isSingleSession()) {
// single session mode
TransactionSynchronizationManager.unbindResource(sessionFactory);
logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");
closeSession(session, sessionFactory);
}else {
// deferred close mode
SessionFactoryUtils.processDeferredClose(sessionFactory);
}
}
}
在上述代码中,首先获得SessionFactory,然后通过SessionFactory获得一个Session。然后执行真正的Action代码,最后根据情况将Hibernate的Session进行关闭。整个思路比较清晰。
注意,在这里有个2个Tips:
1)通过getSession()获得的这个Session做了一次
session.setFlushMode(FlushMode.NEVER); 有关FlushMode可以参考一下这篇文章。http://www2.matrix.org.cn/resource/article/2006-10-08/Hibernate+FlushMode+NEVER_312bca85-5699-11db-91a0-d98dff0aec60.html
2)Spring对拿到的Session做了一次绑定到当前线程的做法,使得这个Session是线程安全的。
从上述代码其实可以得到一些对我们的开发有帮助的结论:
1)如果使用了OpenSessionInView模式,那么Spring会帮助你管理Session的开和关,从而你在你的DAO中通过HibernateDaoSupport拿到的getSession()方法,都是绑定到当前线程的线程安全的Session,即拿即用,最后会由Filter统一关闭。
2)由于拿到的Hibernate的Session被设置了session.setFlushMode(FlushMode.NEVER); 所以,除非你直接调用session.flush(),否则Hibernate session无论何时也不会flush任何的状态变化到数据库。因此,数据库事务的配置非常重要。(我们知道,在调用org.hibernate.Transaction.commit()的时候会触发session.flush())我曾经见过很多人在使用OpenSessionInView模式时,都因为没有正确配置事务,导致了底层会抛出有关FlushMode.NEVER的异常。
OpenSessionInView这个模式使用比较简单,也成为了大家在Web开发中经常使用的方法,不过它有时候会带来一些意想不到的问题,这也是在开发中需要注意的。
1. 在Struts+Spring+Hibernate环境中,由于配置的问题导致的模式失效
这个问题以前论坛曾经讨论过,可以参考一下下面这个帖子:
http://www.iteye.com/topic/15057
2. OpenSessionInView的效率问题
这个问题也有人在论坛提出过,Robbin曾经做过具体的测试,可以具体参考一下下面这个帖子:
http://www.iteye.com/topic/17501
3. 由于使用了OpenSessionInView模式后造成了内存和数据库连接问题
这个问题是我在生产环境中碰到的一个问题。由于使用了OpenSessionInView模式,Session的生命周期变得非常长。虽然解决了Lazy Load的问题,但是带来的问题就是Hibernate的一级缓存,也就是Session级别的缓存的生命周期会变得非常长,那么如果你在你的Service层做大批量的数据操作时,其实这些数据会在缓存中保留一份,这是非常耗费内存的。还有一个数据库连接的问题,存在的原因在于由于数据库的Connection是和Session绑在一起的,所以,Connection也会得不到及时的释放。因而当系统出现业务非常繁忙,而计算量又非常大的时候,往往数据连接池的连接数会不够。这个问题我至今非常头痛,因为有很多客户对数据连接池的数量会有限制,不会给你无限制的增加下去。
4. 使用了OpenSessionInView模式以后取数据的事务问题
在使用了OpenSessionInView以后,其实事务的生命周期比Session的生命周期来得短,就以为着,其实有相当一部分的查询是不被纳入到事务的范围内的,此时是否会读到脏数据?这个问题我至今不敢确认,有经验的朋友请指教一下。
最后提一下OpenSessionInView模式的一些替代方案,可以使用OpenSessionInViewInterceptor来代替这个Filter,此时可以使用Spring的AOP配置,将这个Interceptor配置到你所需要的层次上去。另外就是只能使用最古老的Hibernate.initialize()方法进行初始化了。
2)由于拿到的Hibernate的Session被设置了session.setFlushMode(FlushMode.NEVER); 所以,除非你直接调用session.flush(),否则Hibernate session无论何时也不会flush任何的状态变化到数据库。因此,数据库事务的配置非常重要。(我们知道,在调用org.hibernate.Transaction.commit()的时候会触发session.flush())我曾经见过很多人在使用OpenSessionInView模式时,都因为没有正确配置事务,导致了底层会抛出有关FlushMode.NEVER的异常。
这点我详细解释一下, HibernteTransactionManager 中会根据事务设置改变 session 的刷新方式, 具体代码参见 HibernteTransactionManager 411 行 (Spring 1.2.8)
Java代码
if (definition.isReadOnly() && txObject.isNewSessionHolder()) {
// Just set to NEVER in case of a new Session for this transaction.
session.setFlushMode(FlushMode.NEVER);
}
if (!definition.isReadOnly() && !txObject.isNewSessionHolder()) {
// We need AUTO or COMMIT for a non-read-only transaction.
FlushMode flushMode = session.getFlushMode();
if (FlushMode.NEVER.equals(flushMode)) {
session.setFlushMode(FlushMode.AUTO);
txObject.getSessionHolder().setPreviousFlushMode(flushMode);
}
}
if (definition.isReadOnly() && txObject.isNewSessionHolder()) {
// Just set to NEVER in case of a new Session for this transaction.
session.setFlushMode(FlushMode.NEVER);
}
if (!definition.isReadOnly() && !txObject.isNewSessionHolder()) {
// We need AUTO or COMMIT for a non-read-only transaction.
FlushMode flushMode = session.getFlushMode();
if (FlushMode.NEVER.equals(flushMode)) {
session.setFlushMode(FlushMode.AUTO);
txObject.getSessionHolder().setPreviousFlushMode(flushMode);
}
}
具体的含义, 从代码看已经很清晰了, 不用再赘述
4. 使用了OpenSessionInView模式以后取数据的事务问题
在使用了OpenSessionInView以后,其实事务的生命周期比Session的生命周期来得短,就以为着,其实有相当一部分的查询是不被纳入到事务的范围内的,此时是否会读到脏数据?这个问题我至今不敢确认,有经验的朋友请指教一下。
这个问题根据我的理解, 应该不会存在脏数据的问题, 从 Hibernate 的说明上可以得知, hibernate 保证在同一个 session 中调用 load, get 或 query 时, 得到的是同一个对象, 因此事务结束后得到的对象是已经被更新过的对象
前些日子发现了一个连 OpenSessionInView 都解决不了的 Lazy 问题, 甚是郁闷, 原因是这样的
架构中使用 OpenSessionInView 和 HibernateTransactionManager, 本来相安无事, 结果因为我的异常处理让
OpenSessionInView 失效了, 异常处理方式是出现异常后继续返回到异常前的页面, HibernateTransactionManager 的处理使得 OpenSessionInView 实效
Java代码
protected void doRollback(DefaultTransactionStatus status) {
... 以上省略
finally {
if (!txObject.isNewSessionHolder()) {
// Clear all pending inserts/updates/deletes in the Session.
// Necessary for pre-bound Sessions, to avoid inconsistent state.
txObject.getSessionHolder().getSession().clear();
}
}
}
protected void doRollback(DefaultTransactionStatus status) {
... 以上省略
finally {
if (!txObject.isNewSessionHolder()) {
// Clear all pending inserts/updates/deletes in the Session.
// Necessary for pre-bound Sessions, to avoid inconsistent state.
txObject.getSessionHolder().getSession().clear();
}
}
}
它这一 clear, 把 session 的状态全清楚了, 包括 lazy load 的对象, 这个问题有时间我准备发到 spring 的 bug list 上去
另外今天发现,OpenSessionInView这个Filter,其实问题还是多多啊。
Java代码
protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
session.setFlushMode(FlushMode.NEVER);
return session;
}
protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
session.setFlushMode(FlushMode.NEVER);
return session;
}
这个地方它使用了SessionFactoryUtils.getSession(sessionFactory, true);的方式去拿到Session,这样存在的一个很重大的问题,通过这个方法拿到的Session,是不带任何的interceptor的。看看另外一个OpenSessionInViewInterceptor的实现,它去取Session的时候,却是以这样的方式:
Java代码
Session session = SessionFactoryUtils.getSession(getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
Session session = SessionFactoryUtils.getSession(getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
所以如果要使用Hibernate自带的那些Interceptor的同学们也注意了,使用OpenSessioniInViewFilter的话,那些Interceptor其实是用不上的。
对ORM来说这是个难以避免的问题. 类似的, TOB虽然也用关系数据库作为物理存储, 但是自己实现了内存对象级别的事务管理, 这样就只有在检索和提交事务的时候才需要从连接池里去一个connection来用, 之后会立即释放. Session时间再长, 也只是阻塞其他线程对已读或已写的对象的并发修改, 而不会占用关系数据库的连接资源.
而且在对象级别上TOB的事务也是很轻量的, 用TOB的网站可以给所有request加一个filter, 执行完具体逻辑以后根据是否有exception, rollback或者commit.
返回顶楼 0 0
downpour 等级:
性别:
文章: 832
积分: 1748
发表时间:2006-11-10 收藏 楼上的话说得缺乏依据,对于Hibernate来说,只要Session存在,Connection就一直保持。这一点有源码为证。
只是在Transaction提交或者rollback之后,这个数据库连接是不会占用数据库资源的,但是连接依然保持,尤其是采用数据连接池的时候,这个连接是不会被连接池回收的。
返回顶楼 0 0
nihongye 等级:
性别:
文章: 826
积分: 1473
来自: 深圳
发表时间:2006-11-10 收藏 downpour 写道
楼上的话说得缺乏依据,对于Hibernate来说,只要Session存在,Connection就一直保持。这一点有源码为证。
只是在Transaction提交或者rollback之后,这个数据库连接是不会占用数据库资源的,但是连接依然保持,尤其是采用数据连接池的时候,这个连接是不会被连接池回收的。
不知道你看的是不是hibernat2,在hibernate3中,连接的管理有更灵活的方式。默认是在事务提交后,关闭连接。至于说openSessionInView占用连接,是因为这个时候session是工作在extend模式,所以连接一直保持到session的关闭(但这个连接与事务所用的连接可能是不同的)。另外spring对连接所做的管理是否影响连接的使用就不是很清楚了。
返回顶楼 0 0
nihongye 等级:
性别:
文章: 826
积分: 1473
来自: 深圳
发表时间:2006-11-10 收藏 Feiing 写道
它这一 clear, 把 session 的状态全清楚了, 包括 lazy load 的对象, 这个问题有时间我准备发到 spring 的 bug list 上去
不知道你的问题具体是怎样的,但印象中maillist讨论过这个问题,clear()这句是在1.1之前加上的,因为不这样处理出现了不期望的状态修改。java persistence中也按照这样的方式管理context了。
返回顶楼 0 0
歆渊 等级:
性别:
文章: 427
积分: 1189
发表时间:2006-11-11 收藏 downpour 写道
楼上的话说得缺乏依据,对于Hibernate来说,只要Session存在,Connection就一直保持。这一点有源码为证。
只是在Transaction提交或者rollback之后,这个数据库连接是不会占用数据库资源的,但是连接依然保持,尤其是采用数据连接池的时候,这个连接是不会被连接池回收的。
说我的帖子吗? 不太清楚你的意思.
我的意思是说, ORM还是通过关系数据库来控制事务的, 这样ORM的事务期间, 就必须把持住一个RDB的connection不放来保证事务完整.
而TOB这样的对象数据库是自己控制内存对象级别的事务的, 所以可以只在查询和提交TOB事务的时候才需要RDB连接, 这样一个TOB事务持续时间再长, 也不会一直占用RDB连接, 从而不会出现本帖讨论的性能陷阱问题.
其实ORM这个性能陷阱的直接原因是关系数据库的并发连接数限制, 而这个原因的根源是当在关系模型上要维护数据的一致性和事务的独立性时, 如果发生插入或者删除则经常须要在表级别以致整个持久方案级别上隔离(这个可以在一定程度上进行优化,但条件查询条件复杂是非常困难甚至找不到合适的优化方案), 以防止其他连接的事务用被影响了的查询结果去更新其他数据, 进而存在破坏数据一致性的可能. 所以关系数据库的事务维护成本非常之高, 使可能的并发连接/事务数量受到严格限制. 而这个问题的本源又是因为关系数据库所有的数据访问都是基于查询的.
下面我举一个简化的竞拍例子来说明. (例子里的英文比较简单, 我就不翻译成中文了)
返回顶楼 0 0
歆渊 等级:
性别:
文章: 427
积分: 1189
发表时间:2006-11-11 收藏
Simplified Bidding Rule:
*Each item has a hidden maxprice, the first bid reaches this price wins;
*However, the maxprice can be changed whenever, and if lowered to below
any price of existing bids, the bid with highest price wins.
Relational Database
Java代码
TABLE Item (id AUTO INCREASE, maxprice);
TABLE Bid (id AUTO INCREASE, itemid, price, win DEFAULT FALSE);
TABLE Item (id AUTO INCREASE, maxprice);
TABLE Bid (id AUTO INCREASE, itemid, price, win DEFAULT FALSE);
TX1:
Java代码
--found item id of interest: 321
--check item not already sold
SELECT id FROM Bid WHERE itemid = 321 AND win = TRUE
#IF EXIST id
COMMIT;
return;
#ENDIF
--try lower its price by 10.0
UPDATE Item SET maxprice = maxprice - 10.0 WHERE id = 321;
--find any bid can win agains the new price
SELECT id FROM Bid WHERE itemid = 321 AND price = MAX(price);
--if bid id found, it wins
#IF EXIST id (assume bid id: 591)
UPDATE Bid SET win = TRUE WHERE id = 591;
#ENDIF
--done
COMMIT;
--found item id of interest: 321
--check item not already sold
SELECT id FROM Bid WHERE itemid = 321 AND win = TRUE
#IF EXIST id
COMMIT;
return;
#ENDIF
--try lower its price by 10.0
UPDATE Item SET maxprice = maxprice - 10.0 WHERE id = 321;
--find any bid can win agains the new price
SELECT id FROM Bid WHERE itemid = 321 AND price = MAX(price);
--if bid id found, it wins
#IF EXIST id (assume bid id: 591)
UPDATE Bid SET win = TRUE WHERE id = 591;
#ENDIF
--done
COMMIT;
TX2:
Java代码
--found item id of interest: 321
--see all existing bids, check whether allow new bids (nobody wins)
SELECT id, price, win FROM Bid WHERE itemid = 321;
--bid a new price: 160.0
INSERT INTO Bid (itemid, price) VALUES (321, 160.0);
--check if can win the bid, if greater than maxprices, wins
#IF 160.0 >= (SELECT maxprice FROM Item WHERE id = 321)
UPDATE Bid SET win = TRUE WHERE id = 601; --assume generated bid id: 601
#ENDIF
--done
COMMIT;
--found item id of interest: 321
--see all existing bids, check whether allow new bids (nobody wins)
SELECT id, price, win FROM Bid WHERE itemid = 321;
--bid a new price: 160.0
INSERT INTO Bid (itemid, price) VALUES (321, 160.0);
--check if can win the bid, if greater than maxprices, wins
#IF 160.0 >= (SELECT maxprice FROM Item WHERE id = 321)
UPDATE Bid SET win = TRUE WHERE id = 601; --assume generated bid id: 601
#ENDIF
--done
COMMIT;
The Object Base
Java代码
public class Item extends TheObject
{
@Kin
protected final Collection<Persistent<? extends Bid>> bids =
new ArrayList<Persistent<? extends Bid>>();
private double maxprice;
public Item(double maxprice)
{
this.maxprice = maxprice;
}
@Reading
public Collection<Persistent<? extends Bid>> getBids()
{
return this.bids;
}
@Reading
public double getMaxPrice()
{
return this.maxPrice;
}
@Writing
public void setMaxPrice(double maxPrice)
{
this.maxPrice = maxPrice;
}
}
public class Bid extends TheRelation
{
public class ForItem extends Role<Item>
{
protected ForItem(Persistent<? extends Item> item)
{
super(item);
}
}
@Tie
protected ForItem item;
private double price;
private boolean win = false;
public Bid(Persistent<? extends Item> item, double price)
{
this.item = new ForItem(item);
this.price = price;
}
protected Bid()
{
}
public ForItem getItem()
{
return this.item;
}
@Reading
public double getPrice()
{
return this.price;
}
@Reading
public boolean hasWon()
{
return this.win;
}
@Writing
public void win()
{
this.win = true;
}
}
public class Item extends TheObject
{
@Kin
protected final Collection<Persistent<? extends Bid>> bids =
new ArrayList<Persistent<? extends Bid>>();
private double maxprice;
public Item(double maxprice)
{
this.maxprice = maxprice;
}
@Reading
public Collection<Persistent<? extends Bid>> getBids()
{
return this.bids;
}
@Reading
public double getMaxPrice()
{
return this.maxPrice;
}
@Writing
public void setMaxPrice(double maxPrice)
{
this.maxPrice = maxPrice;
}
}
public class Bid extends TheRelation
{
public class ForItem extends Role<Item>
{
protected ForItem(Persistent<? extends Item> item)
{
super(item);
}
}
@Tie
protected ForItem item;
private double price;
private boolean win = false;
public Bid(Persistent<? extends Item> item, double price)
{
this.item = new ForItem(item);
this.price = price;
}
protected Bid()
{
}
public ForItem getItem()
{
return this.item;
}
@Reading
public double getPrice()
{
return this.price;
}
@Reading
public boolean hasWon()
{
return this.win;
}
@Writing
public void win()
{
this.win = true;
}
}
TX1:
Java代码
//found item id of interest: 321
Persistent<Item> item = tob.get(321);
//check item not already sold
for(Persistent<? extends Bid> bid : item.o.getBids())
if(bid.o.hasWon())
{
tob.commitAllTransactions();
return;
}
//try lower its price by 10.0
item.o.setMaxPrice(item.o.getMaxPrice() - 10.0);
//find any bid can win agains the new price
Persistent<? extends Bid> maxBid = null;
for(Persistent<? extends Bid> bid : getBids())
{
if(bid.o.getPrice() >= this.maxPrice)
{
if(maxBid != null && maxBid.o.getPrice() > bid.o.getPrice())
continue;
maxBid = bid;
}
}
//if bid id found, it wins
if(maxBid != null)
maxBid.o.win();
//done
tob.commitAllTransactions();
//found item id of interest: 321
Persistent<Item> item = tob.get(321);
//check item not already sold
for(Persistent<? extends Bid> bid : item.o.getBids())
if(bid.o.hasWon())
{
tob.commitAllTransactions();
return;
}
//try lower its price by 10.0
item.o.setMaxPrice(item.o.getMaxPrice() - 10.0);
//find any bid can win agains the new price
Persistent<? extends Bid> maxBid = null;
for(Persistent<? extends Bid> bid : getBids())
{
if(bid.o.getPrice() >= this.maxPrice)
{
if(maxBid != null && maxBid.o.getPrice() > bid.o.getPrice())
continue;
maxBid = bid;
}
}
//if bid id found, it wins
if(maxBid != null)
maxBid.o.win();
//done
tob.commitAllTransactions();
TX2:
Java代码
//found item id of interest: 321
Persistent<Item> item = tob.get(321);
//see all existing bids, check whether allow new bids (nobody wins)
for(Persistent<? extends Bid> bid : item.o.getBids())
if(bid.o.hasWon())
{
tob.commitAllTransactions();
return;
}
//bid a new price: 160.0
Persistent<Bid> bid = tob.birth(new Bid(item, 160.0));
//check if can win the bid, if greater than maxprices, wins
if(160.0 >= item.o.getMaxPrice())
bid.o.win();
//done
tob.commitAllTransactions();
TX1 和 TX2 是两个并发事务:
TX1 是给一个物品降价10块钱, 看是不是已经有合适的买家.
TX2 是出价160块, 看是不是竞拍到了这个物品.
在两个事务开始的时候都要先查询一下该物品是否已经被拍下.
这个查询对于未经任何优化的关系数据库来说, 要保证数据一致性, 就必须在这个查询提交之后, 进行查询的事务结束之前, 锁定 Item 和 Bid 表, 一方面防止符合查询条件的Item表记录被修改,新增或者删除, 另一方面防止TX1和TX2这两个事务冲突, 造成数据一致性受损, 比如出来两个Bid记录的win都是TRUE的可能.
因为这个例子比较简单, 所以进行优化相对容易, 就是不锁定整个Item 和Bid表, 而是锁定Item表里id为321和Bid表里itemid为321的所有记录, 包括不允许插入也不允许删除这样的记录.
在这个简单的例子里, 经过这样优化以后, 所达到的效果和TOB相当, 也就是只锁定item 321相关的数据, 允许对其他无关记录的并发操作. 因为TOB是在调用以@Reading标准的方法时对该对象加读取锁(TOB可以配置为使用乐观锁也可以使用悲观锁). 也就是调用 item.o.getBids() 的时候对item加了读取锁, 另一个并发事务如果想向item.o.bids这个集合中加对象进行修改的话, 就要等先获得了这个锁的事务提交或者回滚以后才能继续执行(用悲观锁时), 或者在最后提交的时候抛出一致性异常,自动回滚(用乐观锁时).
但是现实应用中, 常常有很多复杂的组合或者广泛引用的查询条件, 对这些查询条件的类似并发控制优化就体现了不同数据库产品的真正差别. 另外优化也会增加更多的陷阱, 需要这样或者那样的workaround, 同时错误使用甚至正常使用时碰到了优化的bug, 数据也可能被破坏, 这时候数据恢复特性又被提上日程. 其实像Oracle这种CRUD裸性能不知道比MySQL慢多少, 但是就是更值钱, 其中部分就应该包含这里的原因.
但是这些问题的终极源头还是关系模型, 数据以记录条形式进行物理存储, 数据之间的关系只是存在于逻辑层次上, 也就决定了必须通过动态的JOIN查询来临时关联构造出数据拓扑结构来, 因为JOIN的复杂性导致难以维护格记录条的权威版本.
而以加上了面向对象特性的ORK模型为基础, TOB的持久模型是容器维护的一个物理的对象拓扑结构图, 不仅各个节点有了权威版本的控制中心, 从而并发控制的成本戏剧性降低, 直接进行拓扑结构遍历的业务逻辑更是得到极端的性能提升 (比ORM数据都在缓存中的情况都要快得多).
返回顶楼 0 0
downpour 等级:
性别:
文章: 832
积分: 1748
发表时间:2006-11-12 收藏 你和我说的根本不是一个东西。。。。。
好好的一个帖子又偏题了。
返回顶楼 0 0
Feiing 等级:
性别:
文章: 684
积分: 1241
来自: 上海
发表时间:2006-11-12 收藏 nihongye 写道
Feiing 写道
它这一 clear, 把 session 的状态全清楚了, 包括 lazy load 的对象, 这个问题有时间我准备发到 spring 的 bug list 上去
不知道你的问题具体是怎样的,但印象中maillist讨论过这个问题,clear()这句是在1.1之前加上的,因为不这样处理出现了不期望的状态修改。java persistence中也按照这样的方式管理context了。
我已经提交到 spring jira , 具体情况可以看 http://opensource.atlassian.com/projects/spring/browse/SPR-2785
Juergen Hoeller 给了答复, 我的问题还是没办法解决, 只能绕过去了
返回顶楼 0 0
歆渊 等级:
性别:
文章: 427
积分: 1189
发表时间:2006-11-14 收藏 downpour 写道
你和我说的根本不是一个东西。。。。。
好好的一个帖子又偏题了。
呵呵, 过来凑了个热闹, 数落一下 RDB/ORM 的缺陷, 请不要介意.
hibernate更新实体的问题
Hibernate in action (one-to-many/many-to-one)(疑惑)
总结一下最近关于domain object以及相关的讨论
推荐圈子: JBPM @net
更多相关推荐 OpenSessionInViewFilter是Spring提供的一个针对Hibernate的一个支持类,其主要意思是在发起一个页面请求时打开Hibernate的Session,一直保持这个Session,直到这个请求结束,具体是通过一个Filter来实现的。
由于Hibernate引入了Lazy Load特性,使得脱离Hibernate的Session周期的对象如果再想通过getter方法取到其关联对象的值,Hibernate会抛出一个LazyLoad的Exception。所以为了解决这个问题,Spring引入了这个Filter,使得Hibernate的Session的生命周期变长。
首先分析一下它的源码,可以发现,它所实现的功能其实比较简单:
Java代码
SessionFactory sessionFactory = lookupSessionFactory(request);
Session session = null;
boolean participate = false;
if (isSingleSession()) {
// single session mode
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
// Do not modify the Session: just set the participate flag.
participate = true;
} else {
logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
session = getSession(sessionFactory);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
} else {
// deferred close mode
if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
// Do not modify deferred close: just set the participate flag.
participate = true;
} else {
SessionFactoryUtils.initDeferredClose(sessionFactory);
}
}
try {
filterChain.doFilter(request, response);
} finally {
if (!participate) {
if (isSingleSession()) {
// single session mode
TransactionSynchronizationManager.unbindResource(sessionFactory);
logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");
closeSession(session, sessionFactory);
}else {
// deferred close mode
SessionFactoryUtils.processDeferredClose(sessionFactory);
}
}
}
SessionFactory sessionFactory = lookupSessionFactory(request);
Session session = null;
boolean participate = false;
if (isSingleSession()) {
// single session mode
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
// Do not modify the Session: just set the participate flag.
participate = true;
} else {
logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
session = getSession(sessionFactory);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
} else {
// deferred close mode
if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
// Do not modify deferred close: just set the participate flag.
participate = true;
} else {
SessionFactoryUtils.initDeferredClose(sessionFactory);
}
}
try {
filterChain.doFilter(request, response);
} finally {
if (!participate) {
if (isSingleSession()) {
// single session mode
TransactionSynchronizationManager.unbindResource(sessionFactory);
logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");
closeSession(session, sessionFactory);
}else {
// deferred close mode
SessionFactoryUtils.processDeferredClose(sessionFactory);
}
}
}
在上述代码中,首先获得SessionFactory,然后通过SessionFactory获得一个Session。然后执行真正的Action代码,最后根据情况将Hibernate的Session进行关闭。整个思路比较清晰。
注意,在这里有个2个Tips:
1)通过getSession()获得的这个Session做了一次
session.setFlushMode(FlushMode.NEVER); 有关FlushMode可以参考一下这篇文章。http://www2.matrix.org.cn/resource/article/2006-10-08/Hibernate+FlushMode+NEVER_312bca85-5699-11db-91a0-d98dff0aec60.html
2)Spring对拿到的Session做了一次绑定到当前线程的做法,使得这个Session是线程安全的。
从上述代码其实可以得到一些对我们的开发有帮助的结论:
1)如果使用了OpenSessionInView模式,那么Spring会帮助你管理Session的开和关,从而你在你的DAO中通过HibernateDaoSupport拿到的getSession()方法,都是绑定到当前线程的线程安全的Session,即拿即用,最后会由Filter统一关闭。
2)由于拿到的Hibernate的Session被设置了session.setFlushMode(FlushMode.NEVER); 所以,除非你直接调用session.flush(),否则Hibernate session无论何时也不会flush任何的状态变化到数据库。因此,数据库事务的配置非常重要。(我们知道,在调用org.hibernate.Transaction.commit()的时候会触发session.flush())我曾经见过很多人在使用OpenSessionInView模式时,都因为没有正确配置事务,导致了底层会抛出有关FlushMode.NEVER的异常。
OpenSessionInView这个模式使用比较简单,也成为了大家在Web开发中经常使用的方法,不过它有时候会带来一些意想不到的问题,这也是在开发中需要注意的。
1. 在Struts+Spring+Hibernate环境中,由于配置的问题导致的模式失效
这个问题以前论坛曾经讨论过,可以参考一下下面这个帖子:
http://www.iteye.com/topic/15057
2. OpenSessionInView的效率问题
这个问题也有人在论坛提出过,Robbin曾经做过具体的测试,可以具体参考一下下面这个帖子:
http://www.iteye.com/topic/17501
3. 由于使用了OpenSessionInView模式后造成了内存和数据库连接问题
这个问题是我在生产环境中碰到的一个问题。由于使用了OpenSessionInView模式,Session的生命周期变得非常长。虽然解决了Lazy Load的问题,但是带来的问题就是Hibernate的一级缓存,也就是Session级别的缓存的生命周期会变得非常长,那么如果你在你的Service层做大批量的数据操作时,其实这些数据会在缓存中保留一份,这是非常耗费内存的。还有一个数据库连接的问题,存在的原因在于由于数据库的Connection是和Session绑在一起的,所以,Connection也会得不到及时的释放。因而当系统出现业务非常繁忙,而计算量又非常大的时候,往往数据连接池的连接数会不够。这个问题我至今非常头痛,因为有很多客户对数据连接池的数量会有限制,不会给你无限制的增加下去。
4. 使用了OpenSessionInView模式以后取数据的事务问题
在使用了OpenSessionInView以后,其实事务的生命周期比Session的生命周期来得短,就以为着,其实有相当一部分的查询是不被纳入到事务的范围内的,此时是否会读到脏数据?这个问题我至今不敢确认,有经验的朋友请指教一下。
最后提一下OpenSessionInView模式的一些替代方案,可以使用OpenSessionInViewInterceptor来代替这个Filter,此时可以使用Spring的AOP配置,将这个Interceptor配置到你所需要的层次上去。另外就是只能使用最古老的Hibernate.initialize()方法进行初始化了。
2)由于拿到的Hibernate的Session被设置了session.setFlushMode(FlushMode.NEVER); 所以,除非你直接调用session.flush(),否则Hibernate session无论何时也不会flush任何的状态变化到数据库。因此,数据库事务的配置非常重要。(我们知道,在调用org.hibernate.Transaction.commit()的时候会触发session.flush())我曾经见过很多人在使用OpenSessionInView模式时,都因为没有正确配置事务,导致了底层会抛出有关FlushMode.NEVER的异常。
这点我详细解释一下, HibernteTransactionManager 中会根据事务设置改变 session 的刷新方式, 具体代码参见 HibernteTransactionManager 411 行 (Spring 1.2.8)
Java代码
if (definition.isReadOnly() && txObject.isNewSessionHolder()) {
// Just set to NEVER in case of a new Session for this transaction.
session.setFlushMode(FlushMode.NEVER);
}
if (!definition.isReadOnly() && !txObject.isNewSessionHolder()) {
// We need AUTO or COMMIT for a non-read-only transaction.
FlushMode flushMode = session.getFlushMode();
if (FlushMode.NEVER.equals(flushMode)) {
session.setFlushMode(FlushMode.AUTO);
txObject.getSessionHolder().setPreviousFlushMode(flushMode);
}
}
if (definition.isReadOnly() && txObject.isNewSessionHolder()) {
// Just set to NEVER in case of a new Session for this transaction.
session.setFlushMode(FlushMode.NEVER);
}
if (!definition.isReadOnly() && !txObject.isNewSessionHolder()) {
// We need AUTO or COMMIT for a non-read-only transaction.
FlushMode flushMode = session.getFlushMode();
if (FlushMode.NEVER.equals(flushMode)) {
session.setFlushMode(FlushMode.AUTO);
txObject.getSessionHolder().setPreviousFlushMode(flushMode);
}
}
具体的含义, 从代码看已经很清晰了, 不用再赘述
4. 使用了OpenSessionInView模式以后取数据的事务问题
在使用了OpenSessionInView以后,其实事务的生命周期比Session的生命周期来得短,就以为着,其实有相当一部分的查询是不被纳入到事务的范围内的,此时是否会读到脏数据?这个问题我至今不敢确认,有经验的朋友请指教一下。
这个问题根据我的理解, 应该不会存在脏数据的问题, 从 Hibernate 的说明上可以得知, hibernate 保证在同一个 session 中调用 load, get 或 query 时, 得到的是同一个对象, 因此事务结束后得到的对象是已经被更新过的对象
前些日子发现了一个连 OpenSessionInView 都解决不了的 Lazy 问题, 甚是郁闷, 原因是这样的
架构中使用 OpenSessionInView 和 HibernateTransactionManager, 本来相安无事, 结果因为我的异常处理让
OpenSessionInView 失效了, 异常处理方式是出现异常后继续返回到异常前的页面, HibernateTransactionManager 的处理使得 OpenSessionInView 实效
Java代码
protected void doRollback(DefaultTransactionStatus status) {
... 以上省略
finally {
if (!txObject.isNewSessionHolder()) {
// Clear all pending inserts/updates/deletes in the Session.
// Necessary for pre-bound Sessions, to avoid inconsistent state.
txObject.getSessionHolder().getSession().clear();
}
}
}
protected void doRollback(DefaultTransactionStatus status) {
... 以上省略
finally {
if (!txObject.isNewSessionHolder()) {
// Clear all pending inserts/updates/deletes in the Session.
// Necessary for pre-bound Sessions, to avoid inconsistent state.
txObject.getSessionHolder().getSession().clear();
}
}
}
它这一 clear, 把 session 的状态全清楚了, 包括 lazy load 的对象, 这个问题有时间我准备发到 spring 的 bug list 上去
另外今天发现,OpenSessionInView这个Filter,其实问题还是多多啊。
Java代码
protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
session.setFlushMode(FlushMode.NEVER);
return session;
}
protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
session.setFlushMode(FlushMode.NEVER);
return session;
}
这个地方它使用了SessionFactoryUtils.getSession(sessionFactory, true);的方式去拿到Session,这样存在的一个很重大的问题,通过这个方法拿到的Session,是不带任何的interceptor的。看看另外一个OpenSessionInViewInterceptor的实现,它去取Session的时候,却是以这样的方式:
Java代码
Session session = SessionFactoryUtils.getSession(getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
Session session = SessionFactoryUtils.getSession(getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
所以如果要使用Hibernate自带的那些Interceptor的同学们也注意了,使用OpenSessioniInViewFilter的话,那些Interceptor其实是用不上的。
对ORM来说这是个难以避免的问题. 类似的, TOB虽然也用关系数据库作为物理存储, 但是自己实现了内存对象级别的事务管理, 这样就只有在检索和提交事务的时候才需要从连接池里去一个connection来用, 之后会立即释放. Session时间再长, 也只是阻塞其他线程对已读或已写的对象的并发修改, 而不会占用关系数据库的连接资源.
而且在对象级别上TOB的事务也是很轻量的, 用TOB的网站可以给所有request加一个filter, 执行完具体逻辑以后根据是否有exception, rollback或者commit.
返回顶楼 0 0
downpour 等级:
性别:
文章: 832
积分: 1748
发表时间:2006-11-10 收藏 楼上的话说得缺乏依据,对于Hibernate来说,只要Session存在,Connection就一直保持。这一点有源码为证。
只是在Transaction提交或者rollback之后,这个数据库连接是不会占用数据库资源的,但是连接依然保持,尤其是采用数据连接池的时候,这个连接是不会被连接池回收的。
返回顶楼 0 0
nihongye 等级:
性别:
文章: 826
积分: 1473
来自: 深圳
发表时间:2006-11-10 收藏 downpour 写道
楼上的话说得缺乏依据,对于Hibernate来说,只要Session存在,Connection就一直保持。这一点有源码为证。
只是在Transaction提交或者rollback之后,这个数据库连接是不会占用数据库资源的,但是连接依然保持,尤其是采用数据连接池的时候,这个连接是不会被连接池回收的。
不知道你看的是不是hibernat2,在hibernate3中,连接的管理有更灵活的方式。默认是在事务提交后,关闭连接。至于说openSessionInView占用连接,是因为这个时候session是工作在extend模式,所以连接一直保持到session的关闭(但这个连接与事务所用的连接可能是不同的)。另外spring对连接所做的管理是否影响连接的使用就不是很清楚了。
返回顶楼 0 0
nihongye 等级:
性别:
文章: 826
积分: 1473
来自: 深圳
发表时间:2006-11-10 收藏 Feiing 写道
它这一 clear, 把 session 的状态全清楚了, 包括 lazy load 的对象, 这个问题有时间我准备发到 spring 的 bug list 上去
不知道你的问题具体是怎样的,但印象中maillist讨论过这个问题,clear()这句是在1.1之前加上的,因为不这样处理出现了不期望的状态修改。java persistence中也按照这样的方式管理context了。
返回顶楼 0 0
歆渊 等级:
性别:
文章: 427
积分: 1189
发表时间:2006-11-11 收藏 downpour 写道
楼上的话说得缺乏依据,对于Hibernate来说,只要Session存在,Connection就一直保持。这一点有源码为证。
只是在Transaction提交或者rollback之后,这个数据库连接是不会占用数据库资源的,但是连接依然保持,尤其是采用数据连接池的时候,这个连接是不会被连接池回收的。
说我的帖子吗? 不太清楚你的意思.
我的意思是说, ORM还是通过关系数据库来控制事务的, 这样ORM的事务期间, 就必须把持住一个RDB的connection不放来保证事务完整.
而TOB这样的对象数据库是自己控制内存对象级别的事务的, 所以可以只在查询和提交TOB事务的时候才需要RDB连接, 这样一个TOB事务持续时间再长, 也不会一直占用RDB连接, 从而不会出现本帖讨论的性能陷阱问题.
其实ORM这个性能陷阱的直接原因是关系数据库的并发连接数限制, 而这个原因的根源是当在关系模型上要维护数据的一致性和事务的独立性时, 如果发生插入或者删除则经常须要在表级别以致整个持久方案级别上隔离(这个可以在一定程度上进行优化,但条件查询条件复杂是非常困难甚至找不到合适的优化方案), 以防止其他连接的事务用被影响了的查询结果去更新其他数据, 进而存在破坏数据一致性的可能. 所以关系数据库的事务维护成本非常之高, 使可能的并发连接/事务数量受到严格限制. 而这个问题的本源又是因为关系数据库所有的数据访问都是基于查询的.
下面我举一个简化的竞拍例子来说明. (例子里的英文比较简单, 我就不翻译成中文了)
返回顶楼 0 0
歆渊 等级:
性别:
文章: 427
积分: 1189
发表时间:2006-11-11 收藏
Simplified Bidding Rule:
*Each item has a hidden maxprice, the first bid reaches this price wins;
*However, the maxprice can be changed whenever, and if lowered to below
any price of existing bids, the bid with highest price wins.
Relational Database
Java代码
TABLE Item (id AUTO INCREASE, maxprice);
TABLE Bid (id AUTO INCREASE, itemid, price, win DEFAULT FALSE);
TABLE Item (id AUTO INCREASE, maxprice);
TABLE Bid (id AUTO INCREASE, itemid, price, win DEFAULT FALSE);
TX1:
Java代码
--found item id of interest: 321
--check item not already sold
SELECT id FROM Bid WHERE itemid = 321 AND win = TRUE
#IF EXIST id
COMMIT;
return;
#ENDIF
--try lower its price by 10.0
UPDATE Item SET maxprice = maxprice - 10.0 WHERE id = 321;
--find any bid can win agains the new price
SELECT id FROM Bid WHERE itemid = 321 AND price = MAX(price);
--if bid id found, it wins
#IF EXIST id (assume bid id: 591)
UPDATE Bid SET win = TRUE WHERE id = 591;
#ENDIF
--done
COMMIT;
--found item id of interest: 321
--check item not already sold
SELECT id FROM Bid WHERE itemid = 321 AND win = TRUE
#IF EXIST id
COMMIT;
return;
#ENDIF
--try lower its price by 10.0
UPDATE Item SET maxprice = maxprice - 10.0 WHERE id = 321;
--find any bid can win agains the new price
SELECT id FROM Bid WHERE itemid = 321 AND price = MAX(price);
--if bid id found, it wins
#IF EXIST id (assume bid id: 591)
UPDATE Bid SET win = TRUE WHERE id = 591;
#ENDIF
--done
COMMIT;
TX2:
Java代码
--found item id of interest: 321
--see all existing bids, check whether allow new bids (nobody wins)
SELECT id, price, win FROM Bid WHERE itemid = 321;
--bid a new price: 160.0
INSERT INTO Bid (itemid, price) VALUES (321, 160.0);
--check if can win the bid, if greater than maxprices, wins
#IF 160.0 >= (SELECT maxprice FROM Item WHERE id = 321)
UPDATE Bid SET win = TRUE WHERE id = 601; --assume generated bid id: 601
#ENDIF
--done
COMMIT;
--found item id of interest: 321
--see all existing bids, check whether allow new bids (nobody wins)
SELECT id, price, win FROM Bid WHERE itemid = 321;
--bid a new price: 160.0
INSERT INTO Bid (itemid, price) VALUES (321, 160.0);
--check if can win the bid, if greater than maxprices, wins
#IF 160.0 >= (SELECT maxprice FROM Item WHERE id = 321)
UPDATE Bid SET win = TRUE WHERE id = 601; --assume generated bid id: 601
#ENDIF
--done
COMMIT;
The Object Base
Java代码
public class Item extends TheObject
{
@Kin
protected final Collection<Persistent<? extends Bid>> bids =
new ArrayList<Persistent<? extends Bid>>();
private double maxprice;
public Item(double maxprice)
{
this.maxprice = maxprice;
}
@Reading
public Collection<Persistent<? extends Bid>> getBids()
{
return this.bids;
}
@Reading
public double getMaxPrice()
{
return this.maxPrice;
}
@Writing
public void setMaxPrice(double maxPrice)
{
this.maxPrice = maxPrice;
}
}
public class Bid extends TheRelation
{
public class ForItem extends Role<Item>
{
protected ForItem(Persistent<? extends Item> item)
{
super(item);
}
}
@Tie
protected ForItem item;
private double price;
private boolean win = false;
public Bid(Persistent<? extends Item> item, double price)
{
this.item = new ForItem(item);
this.price = price;
}
protected Bid()
{
}
public ForItem getItem()
{
return this.item;
}
@Reading
public double getPrice()
{
return this.price;
}
@Reading
public boolean hasWon()
{
return this.win;
}
@Writing
public void win()
{
this.win = true;
}
}
public class Item extends TheObject
{
@Kin
protected final Collection<Persistent<? extends Bid>> bids =
new ArrayList<Persistent<? extends Bid>>();
private double maxprice;
public Item(double maxprice)
{
this.maxprice = maxprice;
}
@Reading
public Collection<Persistent<? extends Bid>> getBids()
{
return this.bids;
}
@Reading
public double getMaxPrice()
{
return this.maxPrice;
}
@Writing
public void setMaxPrice(double maxPrice)
{
this.maxPrice = maxPrice;
}
}
public class Bid extends TheRelation
{
public class ForItem extends Role<Item>
{
protected ForItem(Persistent<? extends Item> item)
{
super(item);
}
}
@Tie
protected ForItem item;
private double price;
private boolean win = false;
public Bid(Persistent<? extends Item> item, double price)
{
this.item = new ForItem(item);
this.price = price;
}
protected Bid()
{
}
public ForItem getItem()
{
return this.item;
}
@Reading
public double getPrice()
{
return this.price;
}
@Reading
public boolean hasWon()
{
return this.win;
}
@Writing
public void win()
{
this.win = true;
}
}
TX1:
Java代码
//found item id of interest: 321
Persistent<Item> item = tob.get(321);
//check item not already sold
for(Persistent<? extends Bid> bid : item.o.getBids())
if(bid.o.hasWon())
{
tob.commitAllTransactions();
return;
}
//try lower its price by 10.0
item.o.setMaxPrice(item.o.getMaxPrice() - 10.0);
//find any bid can win agains the new price
Persistent<? extends Bid> maxBid = null;
for(Persistent<? extends Bid> bid : getBids())
{
if(bid.o.getPrice() >= this.maxPrice)
{
if(maxBid != null && maxBid.o.getPrice() > bid.o.getPrice())
continue;
maxBid = bid;
}
}
//if bid id found, it wins
if(maxBid != null)
maxBid.o.win();
//done
tob.commitAllTransactions();
//found item id of interest: 321
Persistent<Item> item = tob.get(321);
//check item not already sold
for(Persistent<? extends Bid> bid : item.o.getBids())
if(bid.o.hasWon())
{
tob.commitAllTransactions();
return;
}
//try lower its price by 10.0
item.o.setMaxPrice(item.o.getMaxPrice() - 10.0);
//find any bid can win agains the new price
Persistent<? extends Bid> maxBid = null;
for(Persistent<? extends Bid> bid : getBids())
{
if(bid.o.getPrice() >= this.maxPrice)
{
if(maxBid != null && maxBid.o.getPrice() > bid.o.getPrice())
continue;
maxBid = bid;
}
}
//if bid id found, it wins
if(maxBid != null)
maxBid.o.win();
//done
tob.commitAllTransactions();
TX2:
Java代码
//found item id of interest: 321
Persistent<Item> item = tob.get(321);
//see all existing bids, check whether allow new bids (nobody wins)
for(Persistent<? extends Bid> bid : item.o.getBids())
if(bid.o.hasWon())
{
tob.commitAllTransactions();
return;
}
//bid a new price: 160.0
Persistent<Bid> bid = tob.birth(new Bid(item, 160.0));
//check if can win the bid, if greater than maxprices, wins
if(160.0 >= item.o.getMaxPrice())
bid.o.win();
//done
tob.commitAllTransactions();
TX1 和 TX2 是两个并发事务:
TX1 是给一个物品降价10块钱, 看是不是已经有合适的买家.
TX2 是出价160块, 看是不是竞拍到了这个物品.
在两个事务开始的时候都要先查询一下该物品是否已经被拍下.
这个查询对于未经任何优化的关系数据库来说, 要保证数据一致性, 就必须在这个查询提交之后, 进行查询的事务结束之前, 锁定 Item 和 Bid 表, 一方面防止符合查询条件的Item表记录被修改,新增或者删除, 另一方面防止TX1和TX2这两个事务冲突, 造成数据一致性受损, 比如出来两个Bid记录的win都是TRUE的可能.
因为这个例子比较简单, 所以进行优化相对容易, 就是不锁定整个Item 和Bid表, 而是锁定Item表里id为321和Bid表里itemid为321的所有记录, 包括不允许插入也不允许删除这样的记录.
在这个简单的例子里, 经过这样优化以后, 所达到的效果和TOB相当, 也就是只锁定item 321相关的数据, 允许对其他无关记录的并发操作. 因为TOB是在调用以@Reading标准的方法时对该对象加读取锁(TOB可以配置为使用乐观锁也可以使用悲观锁). 也就是调用 item.o.getBids() 的时候对item加了读取锁, 另一个并发事务如果想向item.o.bids这个集合中加对象进行修改的话, 就要等先获得了这个锁的事务提交或者回滚以后才能继续执行(用悲观锁时), 或者在最后提交的时候抛出一致性异常,自动回滚(用乐观锁时).
但是现实应用中, 常常有很多复杂的组合或者广泛引用的查询条件, 对这些查询条件的类似并发控制优化就体现了不同数据库产品的真正差别. 另外优化也会增加更多的陷阱, 需要这样或者那样的workaround, 同时错误使用甚至正常使用时碰到了优化的bug, 数据也可能被破坏, 这时候数据恢复特性又被提上日程. 其实像Oracle这种CRUD裸性能不知道比MySQL慢多少, 但是就是更值钱, 其中部分就应该包含这里的原因.
但是这些问题的终极源头还是关系模型, 数据以记录条形式进行物理存储, 数据之间的关系只是存在于逻辑层次上, 也就决定了必须通过动态的JOIN查询来临时关联构造出数据拓扑结构来, 因为JOIN的复杂性导致难以维护格记录条的权威版本.
而以加上了面向对象特性的ORK模型为基础, TOB的持久模型是容器维护的一个物理的对象拓扑结构图, 不仅各个节点有了权威版本的控制中心, 从而并发控制的成本戏剧性降低, 直接进行拓扑结构遍历的业务逻辑更是得到极端的性能提升 (比ORM数据都在缓存中的情况都要快得多).
返回顶楼 0 0
downpour 等级:
性别:
文章: 832
积分: 1748
发表时间:2006-11-12 收藏 你和我说的根本不是一个东西。。。。。
好好的一个帖子又偏题了。
返回顶楼 0 0
Feiing 等级:
性别:
文章: 684
积分: 1241
来自: 上海
发表时间:2006-11-12 收藏 nihongye 写道
Feiing 写道
它这一 clear, 把 session 的状态全清楚了, 包括 lazy load 的对象, 这个问题有时间我准备发到 spring 的 bug list 上去
不知道你的问题具体是怎样的,但印象中maillist讨论过这个问题,clear()这句是在1.1之前加上的,因为不这样处理出现了不期望的状态修改。java persistence中也按照这样的方式管理context了。
我已经提交到 spring jira , 具体情况可以看 http://opensource.atlassian.com/projects/spring/browse/SPR-2785
Juergen Hoeller 给了答复, 我的问题还是没办法解决, 只能绕过去了
返回顶楼 0 0
歆渊 等级:
性别:
文章: 427
积分: 1189
发表时间:2006-11-14 收藏 downpour 写道
你和我说的根本不是一个东西。。。。。
好好的一个帖子又偏题了。
呵呵, 过来凑了个热闹, 数落一下 RDB/ORM 的缺陷, 请不要介意.
发表评论
-
Hibernate中Criteria的完整用法
2009-01-04 15:21 1010Hibernate中Criteria的完整 ... -
Hibernate中DetachedCriteria的使用(转)
2008-12-11 10:37 1422Criteria對SQL進行封裝,讓開發人員可以用物件的方式來 ... -
Java开源项目Hibernate快速入门
2008-11-27 17:27 954其实Hibernate本身是个独立的框架,它不需要任何web ... -
hibernate文档API
2008-11-26 12:25 1387http://www.redsaga.com/hibernat ... -
Hibernate 总结
2008-11-26 12:07 1351第一部分:简介 Hibernat ...
相关推荐
#### 一、OpenSessionInViewFilter详解 `OpenSessionInViewFilter`是Spring为配合Hibernate设计的一个过滤器,其主要作用是在Web请求处理过程中打开并维持一个Hibernate Session,直到视图渲染完成。这样做的好处...
### Open_Session_In_View详解 #### 一、背景与概念 在使用Hibernate进行对象持久化时,经常遇到的一个问题是关于懒加载(lazy loading)的处理。懒加载是一种优化技术,允许在真正需要某个关联对象的数据时才加载...
2. **预加载**:使用OpenSessionInView模式,避免因多次打开和关闭Session导致的性能损失。 3. **懒加载**:对于关联对象,可采用懒加载策略,减少数据加载量。 4. **动态SQL**:使用MyBatis或JPA等框架,可以方便...
### SSH全注解环境搭建详解 #### 一、概述 SSH框架是指Struts2、Spring、Hibernate这三种技术的组合,常被用于Java Web开发中。全注解配置相较于XML配置更加简洁,易于维护,因此在现代Web项目开发中越来越受到...
- OpenSessionInView:在Web层统一管理数据库会话的开启与关闭,避免数据库连接泄露。 2. 拦截器配置 在SpringMVC的配置文件中,可以配置多个拦截器。这些拦截器会按顺序执行。在配置文件中定义拦截器时,通常...
《Spring+SpringMVC+Hibernate整合详解》 在Java Web开发中,Spring、SpringMVC和Hibernate是三个非常重要的框架,它们分别负责依赖注入、Web请求处理和对象关系映射。将这三个框架整合在一起,可以构建出高效、...
#### 二、Spring配置详解 ##### 1. **web.xml中的Spring配置** - **载入Log4j配置**: 在web.xml中,我们可以通过`<context-param>`标签来载入Log4j配置。这是因为Spring应用通常需要日志记录功能,而Log4j是...
【Spring框架1】知识点详解: 1. **Spring AOP(面向切面编程)**:Spring AOP 是一种用于实现横切关注点(如日志、事务管理)的技术,它允许我们在不修改源代码的情况下,向应用程序中添加额外的功能。Spring AOP ...
### `lazy`属性详解 `lazy`属性是Hibernate中用于控制数据加载策略的一个关键参数,主要用于延迟加载关联对象。当一个对象被加载时,与其关联的对象不一定立即加载到内存中,而是等到真正需要使用时才加载,这就是...
### SSH面试题详解 #### 一、Struts的Action线程安全性 **问题:** Struts的Action是否线程安全?如果不是,如何确保Action的线程安全? - **Struts 1**: 非线程安全。Struts 1中Action采用的是单例模式,即整个...
### SSH配置总结与部署步骤详解 #### 一、SSH框架简介 SSH框架是Java Web开发领域内非常流行的一种组合框架,分别由Struts2、Spring和Hibernate三个技术组成。其中Struts2负责处理Web层逻辑;Spring则主要作为业务...
### Java程序员SSH面试常见题详解 #### 1. Action是什么?如何证明Action是线程安全的? - **知识点概述**:在Struts框架中,Action是处理用户请求的核心组件之一,它负责执行业务逻辑并返回相应的结果。由于...
《韩顺平Spring雇员管理系统中的Hibernate ORM框架详解》 在现代软件开发中,数据库管理和对象关系映射(ORM)框架扮演着至关重要的角色。韩顺平老师的"Spring雇员管理系统"教学资源,重点关注了使用Hibernate ORM...
#### Java面试宝典与SSH常问问题详解 在IT行业中,尤其是对于专注于Java开发的专业人士来说,掌握SSH框架(即Struts、Spring和Hibernate)的相关知识是至关重要的。这份“SSH面试总结”不仅涵盖了广泛的Java面试...
### Struts、Spring、Hibernate 面试题详解 #### 1. Action 是否线程安全?如果不是,如何保证 Action 的线程安全? - **答案**:通常情况下,Struts 的 Action 不是线程安全的。因为同一个 Action 实例可能会被多...
### Spring管理Struts与Hibernate详解 #### 一、Spring整合Struts 在Web开发中,Struts是一个基于MVC设计模式的开源框架,它能够帮助开发者构建可维护性高、结构清晰的应用系统。Spring框架则提供了强大的依赖注入...
### Java SSH面试题详解 #### 1. Action 是否线程安全? **背景介绍:** - **Struts1:** 在 Struts1 中,Action 对象是单例模式的,这意味着所有的请求都共享同一个 Action 实例。因此,在 Struts1 中 Action 是*...
### 二、web.xml配置详解 `web.xml`是Java Web应用的核心配置文件,它定义了应用的初始化参数、监听器、过滤器、Servlet映射等信息。在给定的部分内容中,我们可以看到以下关键配置: #### 1. 应用基础信息 ```...
### Java并发编程与多线程知识点详解 #### 1. 线程安全与锁定机制 - **确保线程安全**: 在Java并发编程中,确保线程安全是至关重要的。通常有三种方法来实现这一点: - 使用`synchronized`关键字:这是最基本的...