hibernate进阶改造

 

 

原帖内容已删除,对描述部分做新的修改,将不针对hibernate这一个orm框架:

 

我将会致力于从根本上解决脏数据读取问题。

 

对于我们现在的大多数框架来说,任何操作都是基于数据库的。相当于把sql语言转换形式进行实现:

 

"update User set level = level +1;" 等价于 "User user= get(User); user.setlevel(user.getLevel+1);" ?

 

我们的应用都是基于数据库的数据操作,前者是告诉数据库自行修改数据操作,而后者着是直接替换数据库中的值!

 

有严重的并发修改问题。所谓并发修改不仅仅是同一时间修改同一条记录的时候的问题,这种情况数据库有锁等方式解决。

但是对于后者这种方式,数据库将无能为力,因为orm框架每次update到数据库中的值全部都是全新的!全部都是替换操作。

 

在并发修改的情况下(从 User user= get(User);把user信息取出来到update(user);这个过程中,任何一个其他线程的 get(User) update(user) ;操作都会照成潜在的并发问题。第一个取出的user的对象是可靠的,其他的非指向user引用的对象全部都为不可靠数据!)user的信息会被任意的覆盖,因为所有的user信息都是取得保存在内存中,数据库中uid为1的user记录在数据库中永远只有一个,并且有锁机制保证修改顺序。

 

但是在orm框架中会存在多个user对象,只不过这些user对象的uid为1,而且可怕的是这些user互不相关!在你update(user);的时候实质上是在不停的插入全新的user信息,内存中的user值和数据库中的值没有任何关系。但是,从业务逻辑上来说,你对user对象属性操作仅仅是代替数据库操作。

 

本质上user的属性在整个内存中应当只有1个,因为你只是代替数据库去修改某个属性。你不应该具有多个存储能力,每个实体实质上都存储了一条记录的信息。

 

举例:

 

	@Transactional
	public void a(){
		User user1=baseDAO.get(User.class,1);		
		user1.setLevel(user1.getLevel()+1);
		
		User user2=baseDAO.get(User.class,1);		
		user2.setLevel(user2.getLevel()+1);
		
		baseDAO.update(user1);
		baseDAO.update(user2);
	}

 在这个方法里面,任意对user的操作都是可靠的,因为hibernate的一级缓存将后面取出来的user2指向的是user1的引用,user1、user2的修改实质上仅仅修改内存中的唯一user对象。在update之后没有任何问题,因为从始至终只修改的是一个记录。

 

典型的并发修改例子:

 

@Transactional
	public void b(){
		User user1=baseDAO.get(User.class,1);		
		user1.setLevel(user1.getLevel()+1);
		
		User user2=baseDAO.get(User.class,1);		
		user2.setLevel(user2.getLevel()+1);

		baseDAO.update(user1);
		baseDAO.update(user2);
	}

	@Transactional
	public void c(){
		User user3=baseDAO.get(User.class,1);		
		user3.setLevel(user3.getLevel()+1);	
		
		try {
			Thread.sleep(5000);//模拟业务操作需要5秒时间
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		baseDAO.update(user3);
	}
 

 

b方法比c方法晚一秒钟调用,假设user的leve原本的值为1;在method b,method c执行完毕之后 数据库中实际值为2,而不是3。

因为b方法修改的数据库记录被c方法的修改结果覆盖了。

 

这类问题根本原因不是用加锁或者修改业务逻辑去解决的。而是为什么在内存中会有多个不一样的user。数据应该都是唯一的,数据库中找不到id为1的2条记录,内存中也不应该出现id为1的2个对象。

 

既然我们是orm框架用面向对象的方式去修改数据,操作数据的本质应该不能改变:既永远只操作一条记录。可以参照method a是如何实现的,无论你update(user);多少次,最终修改的只有一条记录。只不过hibernate这种方式仅仅在一个事物中是这样做的,没有做到整个内存中只存在一个user对象。

 

 

针对,hibernate这个orm框架来说。他可以用一级缓存保证同一个事物内数据的一致性,但是处理不了不同事物的一致。那么还有一个二级缓存可能让内存中的user指向同一个对象(应该是有的),具体有没有这么做,还需要测试、研究下才能最终确认。

 

如果我的推断是正确的,那么在hibernate开启二级缓存之后,才能算是一个完整的orm框架。否则将是一个错误设计,简单的说:数据中只有一条记录,那么内存中只能有一个实体,update操作只能由这个实体操作,其他的任意实体都不可取。你以为你按照产品说明书上一步步操作没有问题,实际上问题一直存在!只不过你没有碰。

 

------

ps:

1、我是在接触游戏开发才有这个想法,至于一些设计可能你觉得有问题,这个不想多争论,除非你指出我设计上的错误。

2、并发问题、并发压力之类的,一般都说的是数据库底层操作的问题,但是实际在业务方法上你可能悄无声息的制造了并发问题。

3、这里请不要说xx不适合开发xx之类的,这里说的和开发什么东西没太大关系,任何一个使用hibernate的应用都会有这样的问题,除非你从业务上就避免了多个地方修改同一条记录的逻辑。一旦有,你就应该有这个思考。

4、如果错,请狂喷。在下洗耳恭听、虚心学习。

你可能感兴趣的:(Hibernate)