日期:2014-05-16  浏览次数:20412 次

因session会话问题Hibernate的更新数据库操作无效
  最近一段时间做项目的时候,自已在修改一个功能点,要更改已获取的数据列表中的某行记录内容,并将更改保存到数据库中,已写好了所有业务代码,在调用DAO层Hibernate方法执行数据库更新时,发现一个有意思的问题,程序执行数据库更新操作无任何异常,但是在刷新列表时,却发现该行数据根本没有更改。
  经过分析与查找,确认是hibernate缓存问题,导制数据库操作无法真正的执行。具体原因是如何导制的,我没有正确答案,但可以肯定跟缓存有关。
具体代码如下:
     /**
	 * 修改指定流程下的节点内容
	 * @param lcbh
	 * @param lcmc
	 * @param oldJdbh
	 * @param oldJdmc
	 * @param newJdbh
	 * @param newJdmc
	 * @return
	 * @throws Exception
	 */
	public boolean updateLcjdDb(String lcbh,String lcmc,String oldJdbh,String oldJdmc,String newJdbh,String newJdmc) throws Exception {
		Session session = this.getSession();
		Transaction tx =null;
		String hql="update BFsYwbd set jdph=?,jdmc=? where lcph=? and lcmc=? and jdph=? and jdmc=?";
		int count=0;
		try{
			tx = session.beginTransaction();				
			Query query=this.getSession().createQuery(hql);
			query.setString(0, newJdbh);
			query.setString(1, newJdmc);
			query.setString(2, lcbh);
			query.setString(3, lcmc);
			query.setString(4, oldJdbh);
			query.setString(5, oldJdmc);
			count=query.executeUpdate();
			tx.commit();
			session.flush();
		}catch(Exception e){
			tx.rollback();
			throw e;
		}				
		return count>=1?true:false;	
	}



上面一段代码从运行角度来看无任何问题,但是就是无法将更新操作应用到数据库层面。

推测,可能是在执行数据库操作时由hibernate获取了缓存中的已有的数据列表,并对该列表中的指定对象进行了操作,但没有将该对象的操作执行到数据库中(注:所查询出的列表是经过数据库去掉重复项之后的集合,可能hibernate对缓存中的数据列表行进行了更新之后,却无法将该更改后的列表行缓存对象匹配到数据库中指定的记录行,导制无法真实的修改数据库中的记录,因为缓存中的对象已在数据库中找不到记录行或唯一行),hibernate的缓存策略是每个对应数据库表的缓存记录都具有会话ID,它保存了数据库中的记录与缓存中的对象一致,一但缓存中的记录发生更新操作,hibernate就会通过缓存机制找到对应数据库中表的数据,将其更改。代码中的是通过getSession()获取Session对象,在通过开启事务进行更新操作,最后提交事务,刷新缓存。 我们了解到通过getSession()所获取的Session对象,都是重新创建的一个新的会话对象,而我的功能中所获取的数据列表是通过getHibernateTemplate().find()执行得到的。getHibernateTemplate()方法是由spring通过在控制反转代理管理数据库连接sessionFactory所得到的,也就是说getHibernateTemplate方法中得到的会话对象是从sessionFactory会话工厂中得到,而sessionFactory会话工厂中会保持一个有用的Session会话,而不是每次通过调用getHibernateTemplate()方法都会创建的,这与getSession()正好相反。因此推测到,在通过getSession()所生成的会话对象更改数据库时,会从hibernate的所有缓存中找到数据列表,但该会话对象只对该数据列表中的数据进行更新之后,并提交了事务刷新了会话对象,实际上就是清空了该Session会话,而该操作之对当前从getSession()中所获取会话缓存数据有效,实际上并没有更新Spring控制反转代理数据库连接所产生的sessionFactory会话工厂中的缓存数据。其最后结果就是更新了getSession()方法生成的会话对象中的数据缓存,而getSession()所产生的Session会话对象不被Spring所代理管理,而是将一个原始的最新的会话,就导制了原有的通过getHibernateTemplate()所生成的Spring代理的sessionFactory会话工厂的数据表列记录不能修改,sessionFactory会话工厂中所缓存的数据列表记录不能修改,其对应的数据库表记录也就不会有更新操作。

经过以上推测和分析之后,就大致明白了些。不过只是个人的见解,不一定是正确的方向。

经过修改后的代码如下:

 /**
	 * 修改指定流程下的节点内容
	 * @param lcbh
	 * @param lcmc
	 * @param oldJdbh
	 * @param oldJdmc
	 * @param newJdbh
	 * @param newJdmc
	 * @return
	 * @throws Exception
	 */
	public boolean updateLcjdDb(String lcbh,String lcmc,String oldJdbh,String oldJdmc,String newJdbh,String newJdmc) throws Exception {
		Session session = this.getHibernateTemplate().getSessionFactory().openSession();
		Transaction tx =null;
		String hql="update BFsYwbd set jdph=?,jdmc=? where lcph=? and lcmc=? and jdph=? and jdmc=?";
		int count=0;
		try{
			tx = session.beginTransaction();				
			Query query=this.getSession().createQuery(hql);
			query.setString(0, newJdbh);
			query.setString(1, newJdmc);
			query.setString(2, lcbh);
			query.setString(3, lcmc);
			query.setString(4, oldJdbh);
			query.setString(5, oldJdmc);
			count=query.executeUpdate();
			tx.commit();
			session.flush();
		}catch(Exception e){
			tx.rollback();
			throw e;
		}				
		return count>=1?true:false;	
	}



从上面的代码中可以看出,其实就是将getSession()方法获取Session会话对象,改为通过this.getHibernateTemplate().getSessionFactory().openSession()获取Spring所代码管理的会话对象。