`

OpenSessionInView详解

阅读更多
相关文章:  
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 的缺陷, 请不要介意.

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics