假设从A表中读出所有数据,在在Hibernate的映射文件中A中包含B
映射文件中默认lazy="true"未改变.这时候如果查询后把Session关闭.当查处的数据被调用的时候调用到A中包含的B对象的时候就会出错.
如果把lazy="false"或者不关闭Session就没问题.
我的做法是等到数据用不到的时候再将Session关闭.
但是有的人说Session不关闭会占用资源而lazy="false"如果是大数据量会降低查询速度.
请问有什么更好的办法么?
谢谢

解决方案 »

  1.   

    现在是没有特别好的解决方案的,使用OpenSessionInViewFilter也还是存在很多问题的
      

  2.   

    lazy="false"他的意思是指你对数据库进行应用的时候,你指定代理机制来对他进行操作,使用了cglib.jar这个包做的。你操作完成后他会自动的关闭session的。
      

  3.   

    session 还是用完了就关闭的好,否则容易产生很多问题。Hibernate的一对多查询是个很大的问题(其实任何ORM都有这个问题),我的解决方案是lazy=true,需要的时候再访问数据库获取“多”那一端所需要的数据。这样做会增加数据库访问次数,但优点是不浪费资源,减缓系统缓存压力,增加程序速度。毕竟数据库查询的速度比程序快得多,而且当你获得了“一”的时候想查询“多”,通常都是索引范围内的查询,搜索数据数量控制在百或者千以内,不影响运行。
      

  4.   

    lazy=true 如果是查询完毕立刻关闭Session,在A中调用B的时候会报错
    但是如果设置成lazy=false 的话没问题了.貌似当你调用B的时候它会再次发出查询..就是这里困扰了我很久
      

  5.   

    关闭session后就不能延迟加载了,你需要另行查询。但大多数情况下另行查询要比直接获得一对多所有的子表相关内容要便宜得多。
      

  6.   

    其实lazy=true时,你调用B所进行的查询和你自己重新查询是一样的,而且你重新查询可能还会得到比直接调用B更精确的结果。比如A里有10000个B,那么你直接调用B的时候会返回10000条;而你另行查询可以用更精确的SQL得到其中的1条,或者有限的数条。
      

  7.   

    感谢大家,我还有最后最后一个问题,就是在lazy=true的时候一般什么时候关session(在需要调用A中的B的情况下)
    借鉴一下大家以往的经验
      

  8.   

    我也想问这个问题来着,
    在网上找了找,有人说是用Fitler 来控制Session.close()
    而有的人说是用完了就关
      

  9.   

    Fitler里面是这样处理的#  public void doFilter(ServletRequest arg0, ServletResponse arg1,    
    #             FilterChain chain) throws IOException, ServletException {    
    #         log.debug("HibernateSessionFilter start");    
    #           
    #         try{    
    #             //request 之前要处理的代码  
    #             chain.doFilter(arg0, arg1);       
    #            //response之后要要处理的代码      
    #              
    #         }catch (Exception e) {    
    #             e.printStackTrace();               
    #         }   finally{            
    #             HibernateSessionFactory.closeSession();              
    #         }    
    #     
    #     }  HibenrateSessionFacotroy是MyEclipse配置Hibernate的时候自动生成的,
    大家觉得这样写处理session关闭好吗?
      

  10.   

    为每个POJO 都写个DAO吧,要用的时候从DAO里拿了就行,不用从Detached对象里获取基本类型以外的对象
      

  11.   

    如果不想查询第二遍 就用OpenSessionInViewFilter吧!目前没什么特别好的办法
      

  12.   

    何时关session应该不是你显示层应该考虑的问题,要显示什么东西你的逻辑层得心里有数。在确定这回要读它的情况下,就在session结束前先取一下这个集合的size(),告诉hibernate我这次是动真格的加载了。
      

  13.   

    仁兄的意思是这样public void testSave(){
    Session session=HibernateSessionFactory.getSession();
    Transaction tx=null;
    try{
    tx=session.beginTransaction();
    Userinfo user=new Userinfo();
    user.setUserid(new Long(108));
    user.setUsername("李四");
    user.setUserpw("helo");
    user.setUserphone("13800138000");
    user.setCreatetime(new Date());
    session.save(user);
    tx.commit();
    }catch(Exception e){
    e.printStackTrace();
    tx.rollback();
    }finally{
    session.close();
    }

    }每个方法都加fianally,然后里面写session.close()?
    而非同意用filter来管理?
    可以说说为什么吗?
      

  14.   

    一般来说这种方法不推荐。若使用Hibernate 3.2(这也是众多IDE一致选用的稳定版)或更新版本,一般来说建议这么写:Session ses = HibernateSessionFactory.getSessionFactory.getCurrentSession();
    ses.beginTransaction();
    //.....(CRUD操作)...
    ses.getTransaction().commit();这样就可以获得当前线程的唯一session。
    这样做的优点:
    1)非常适合web程序,并发管理十分容易,session由线程产生,且能够保证一个线程总是只有一个session。
    2)资源回收变得轻松。session将在commit()或rollback()后自动释放,无需再写finally语句块。
    3)事务管理十分直观,一般来说在业务层或service层的方法前后用上面的三个语句包住,就可以让该方法的原子性得到保证。
    4)由于3)的方式应用十分普遍,用spring AOP对service层进行事务控制就更简单了,上面三行代码甚至都不必写。
    5)永远不用在DAO的方法内做开启session、打开事务、提交事务、释放session这些事了,一般来说这可不是什么好习惯。
    需要的额外配置:
    1) 在hibernate.cfg.xml的<sessionFactory>里添加这样一行:
    <property name="current_session_context_class">thread</property>
    也就是说让线程来管理
    2)没了。
      

  15.   


    19楼的兄弟就是我的意思,在用个不恰当的比喻: 上厕所包括 拉拉链,掏那活儿 ,吁吁 ,拉上拉链 这是一整套流程 应该放到transaction当中,否则不是一次成功的上厕所。
    而放到finally里面的意思就像不管中间发生什么 最后就是拉拉链。不能保证一次有质量的排泄行为。
      

  16.   


    仁兄的意思是Struts+Hibenate3.2的session应该是像如下写法:public void testSave(){
    Session session=HibernateSessionFactory.getSessionFactory().getCurrentSession();
    try{
    session.beginTransaction();
    Userinfo user=new Userinfo();
    user.setUserid(new Long(109));
    user.setUsername("109");
    user.setUserpw("helo");
    user.setUserphone("13800138000");
    user.setCreatetime(new Date());
    session.save(user);
    session.getTransaction().commit();
    }catch(Exception e){
    e.printStackTrace();
    session.getTransaction().rollback();

    }
    然后配置<property name="current_session_context_class">thread</property>这样session将会自动释放
      

  17.   

    我还有个问题想请教一下大家:
    public List findPage(final int currPage, final Map args, 
                final Class classname,final String order) { 
            if (args == null) { 
                return null; 
            } 
            final int currPate = currPage * 1; 
            final Map map = args; 
            String sql = "from " + classname.getName() + " entity where 1 = 1"; 
            for (Iterator iter = map.keySet().iterator(); iter.hasNext();) { 
                String key = (String) iter.next(); 
                sql = sql + " and " + key + " like '%" + map.get(key) + "%' "; 
            } 
            if(order!=null&&!"".equals(order)){ 
                sql+= " order by "+order+" desc"; 
            } 
            final String hql = sql; 
            List list = this.getHibernateTemplate().executeFind( 
                    new HibernateCallback() { 
                        public Object doInHibernate(Session session) 
                                throws HibernateException, SQLException { 
                            Query query = session.createQuery(hql); 
                            List result = query.setFirstResult( 
                                    currPage > 1 ? (currPage - 1) * 15 : 0) 
                                    .setMaxResults(15).list(); 
                            return result; 
                        } 
                    });         return list; 
        } 
    在这个例子中还有什么方法让他运行的快一点,因为我运行多了就变慢,我也不值到问题在哪里?
      

  18.   

    我怎么感觉仁兄的意思是这样处理:public void testSave(){
    Session session=HibernateSessionFactory.getSession();
    Transaction tx=null;
    try{
    tx=session.beginTransaction();
    Userinfo user=new Userinfo();
    user.setUserid(new Long(108));
    user.setUsername("李四");
    user.setUserpw("helo");
    user.setUserphone("13800138000");
    user.setCreatetime(new Date());
    session.save(user);
    tx.commit();
    }catch(Exception e){
    e.printStackTrace();
    tx.rollback();
    }finally{
    session.close();
    }
    }
    而非这样处理
    Session ses = HibernateSessionFactory.getSessionFactory.getCurrentSession();
    ses.beginTransaction();
    //.....(CRUD操作)...
    ses.getTransaction().commit(); 刚接触Hibernate,所以麻烦指教指教,呵呵
      

  19.   

    在深入浅出-Hibernate中5.3.2中有说:在Filter中获取和关闭Session,并在周期内运行的所有对象(Filter 链中其余的Filter,
    及其覆盖的Servlet和其他对象)对此Session的实例进行重用,保证了一个HttpRequest
    处理过程中只占用一个Session,提高了整体性能表现
    我想请教下,Filter处理Session.close()和21楼的写法比较,优缺点是什么?
      

  20.   


    也不仅仅是这样,finally也可以写,但是应该写在service层的方法两端而不是DAO里,如此以该service方法为顶端的整个调用栈里的CRUD操作都将被transaction管理,一损俱损一荣俱荣。不宜在DAO的方法里设定事务单元,颗粒度太细了。前面说过,只要设定:
    <property name="current_session_context_class">thread</property> 
    那么事务无论提交还是回滚,session都将自动释放,所以finally不是必要的,还降低了程序的可读性。此外,当你使用Hibernate3.2或以后的版本,如无要求对事务执行中抛出的异常进行特殊处理,try catch 甚至都不需要。Hibernate的回滚原理,是必须RuntimeException以上的异常级别才会自动回滚,如果是普通的Exception则不会回滚,需要程序员手工在catch里面回滚。但Hibernate3.2以后抛出的都是RuntimeException,所以try catch也无必要了。
      

  21.   


    这个问题要寻根究底,只有看Filter的实现源码才能知道。不过就我所知,使用getCurrentSession()的方式获取的session,是在commit()、rollback()或线程生命周期结束后会自动调用session.close()的,不出意外的话,两者是一致的。此外说明一点,如果使用JPA做事务管理,那么我的方法就不能用了。当然,一般来说不会有谁吃饱了撑着非要用JPA这样重型的事务机制,只有需要处理跨数据库事务时才有用。
      

  22.   

    应该是说 jeff2033333 很强,呵呵也就是所Hibernate3.2后,不用显示的定义事务,和try...catch
    怪不得MyEclipse自动生成的Dao里面没有定义事务的声明和回滚,但是有try catch   public void save(Ouser transientInstance) {
    log.debug("saving Ouser instance");
    try {
    getSession().save(transientInstance);
    log.debug("save successful");
    } catch (RuntimeException re) {
    log.error("save failed", re);
    throw re;
    }
    } public void delete(Ouser persistentInstance) {
    log.debug("deleting Ouser instance");
    try {
    getSession().delete(persistentInstance);
    log.debug("delete successful");
    } catch (RuntimeException re) {
    log.error("delete failed", re);
    throw re;
    }
    }
      

  23.   

    public void save(Ouser user) {
    Session session=HibernateSessionFactory.getSessionFactory().getCurrentSession();
    try{
    session.beginTransaction();
    session.save(user);
    session.getTransaction().commit();
    }catch(RuntimeException  e){
                            //session.getTransaction().rollback 自动个还是需手动
    e.printStackTrace();
    }
    }
    [/code]默认情况下Hibernate只有遇到运行时异常,数据库操作才会回滚,普通异常不会回滚
    那么这个回滚是自动回滚吗?还是说需要手动回滚
      

  24.   

    我是在过滤器中关闭session的!
      

  25.   


    RuntimeException 是自动回滚,普通Exception需要你自己在catch里面回滚。比如,有的时候异常是transaction活动期间的其它非数据库操作引起的,但由于某方面的考虑,需要回滚该transaction内的所有数据库操作,这个时候“可能”需要你手动回滚。
      

  26.   

    33楼,万万不敢自称高手,我接触Hibernate也就两星期,项目需要而已。
    PS:学习框架,直接做项目才是王道。框架其实都不难的,难的话还用它做什么呢,框架的目的就是让工作变得简单。不过最难的是没有机会用在项目里,毕竟大多数时候项目要用到什么技术、框架,都是老板说了算而不是你。
      

  27.   


    catch(RuntimeException  e){
     session.getTransaction().rollback 
     e.printStackTrace();


    也就是说在catch里面手动回滚是最保险的操作?

    确实是做项目才是最好的方法
      

  28.   


    说是这么说,谁也都知道,荷包里的钞票要自己亲手数过的才放心,可是你也看到,银行柜台数大票往往还要借助于点钞机,为啥呢,因为人是动物啊,同一个机械动作做久了,你的故障率比机器可高多了。Hibernate实现自动回滚也是这个道理啊,一般情况下我们不用去管了,我们有限的精力应该放在特别不敢放心的地方,在那里手动控制回滚。
      

  29.   

    这个帖子还真是火呀.我也遇到了跟session有关的问题:
    我就是往数据库里插入记录,前几条还没事,但是后来就出现了问题了
    点添加等好长时间然后就出错了
    我怀疑是session占用的过多没有释放,而没有可用的session资源了
    不知道这个问题怎么解决 ?
      

  30.   

    在web应用中
    从SessionFactory获取Session时可以把它放在ThreadLocal里面
    这样可以不用管Session的关闭
    服务器一般采用一个请求一个线程的方式,意味着只要还是一个请求你拿到的Session是同一个
    服务器响应请求的时候会关闭线程
    ThreadLocal中的变量将会自动剥离private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
    public static Session getSession() throws HibernateException {
            Session session = (Session) threadLocal.get(); if (session == null || !session.isOpen()) {
    if (sessionFactory == null) {
    rebuildSessionFactory();
    }
    //如果当前线程中没有session,开启一个session,并把它与当前线程关联
    session = (sessionFactory != null) ? sessionFactory.openSession()
    : null;
    threadLocal.set(session);
    }        return session;
        }
      

  31.   

    我想问一下,这样还用自己手动写 session.set(null),s.close(); 吗? 如果要写的话放在哪里最合适?