锁是在开发中常见的一个问题,锁住的数据可以保证访问的排他性,可以说只要当同一个资源有可能被同时访问时,就应当考虑到锁的问题。Hibernate中锁有两种:悲观锁和乐观锁。
悲观锁
悲观锁的意思是,对数据访问、修改可能出现的问题持悲观的态度,所以要防范于最初的未然:在数据的整个访问过程中都将数据锁定。悲观锁的实现,依靠数据库提供的锁机制,因为在数据一旦被访问就被锁定,所以可以保证实现真正的访问排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据。
最常见的方式为:
select * from t_Student where name='tgb' for update
在Hibernate中,我们可以使用的方式为:
Inventory实体类
public class Inventory {
private String itemNo;
private String itemName;
private int quantity;
//省略getter和setter……
}
测试public void testLoad1()
{
Session session=null;
try {
session=HibernateUtils.getSession();
session.getTransaction().begin();
Inventory inventory =(Inventory)session.load(Inventory.class, "10001", LockMode.UPGRADE);
System.out.println("user1——itemNo: "+inventory.getItemNo());
System.out.println("user1——itemName: "+inventory.getItemName());
System.out.println("user1——quantity: "+inventory.getQuantity());
inventory.setQuantity(inventory.getQuantity()-200);
session.getTransaction().commit();
} catch (Exception e) {
session.getTransaction().rollback();
e.printStackTrace();
}
finally
{
HibernateUtils.closeSession(session);
}
}
文中,LockMode.UPGRADE 的意思是利用数据库的 for update 子句加锁。在System.out.println("user1——quantity: "+inventory.getQuantity());行加断点,可以看到发出的语句为:
Hibernate: select inventory0_.itemNo as itemNo0_0_, inventory0_.itemName as itemName0_0_, inventory0_.quantity as quantity0_0_ from t_inventory inventory0_ where inventory0_.itemNo=? for update
user1——itemNo: 10001
user1——itemName: 神农三鹿
user1——quantity: 1000
在事务提交前,其它程序是无法访问此条数据的。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但这会增大数据库性能的大量开销,特别对长事务而言,这样的开销会非常高。
乐观锁
与悲观相对,乐观锁是更宽松的加锁机制,对数据的访问、修改持乐观的态度:程序们想怎么改就怎么改吧,能保证数据访问不冲突、没有脏数据就好……乐观锁的实现靠的是对版本号的控制,一般为表增加一个版本version字段,判断程序中跟随数据读出来的版本号是否小于数据库中保存的版本号,如果小于则不可以修改数据,否则可以修改。Hibernate中的乐观锁的实现,需要映射文件的属性修改。
映射文件
<hibernate-mapping >
<class name="com.tgb.hibernate.Inventory" table="t_inventory" optimistic-lock="version">
<id name="itemNo">
<generator class="assigned" />
</id>
<property name="itemName" />
<property name="quantity"/>
<property name="version"/>
</class>
</hibernate-mapping>
测试
public void testLoad1()
{
Session session=null;
try {
session=HibernateUtils.getSession();
session.getTransaction().begin();
Inventory inventory =(Inventory)session.load(Inventory.class, "10001", LockMode.UPGRADE);
System.out.println("user1——itemNo: "+inventory.getItemNo());
System.out.println("user1——itemName: "+inventory.getItemName());
System.out.println("user1——quantity: "+inventory.getQuantity());
inventory.setQuantity(inventory.getQuantity()-200);
session.getTransaction().commit();
} catch (Exception e) {
session.getTransaction().rollback();
e.printStackTrace();
}
finally
{
HibernateUtils.closeSession(session);
}
}
此时,保持此测试程序对数据的加锁,先使用其他测试程序对此数据修改后,再执行此测试程序会发生错误:乐观锁机制避免了长事务中的数据库加锁开销,大大提升了大并发量下的系统整体性能表现;但因为乐观锁机制往往与系统的数据存储逻辑有关系,例如其他系统对数据的同时修改,可能会将脏数据存入数据库。
总结
二者的使用要根据实际情况,悲观锁和乐观锁就先说到这里。