Hibernate 配置详解(7)

hibernate.order_updates:

Hibernate文档中提到,该配置用于在刷新一级缓存,提交UPDATE的时候,按照每类对象的主键顺序排序后再提交,可以在高并发情况下减少事务死锁的可能。这个配置默认为false,但是非常建议在可能存在高并发情况下开启,因为其实按照类型ID排序(在内存中),并不会消耗过多性能。那么这个配置到底什么含义呢?做个简单的测试。

首先准备一个对象User,完成映射(略),完成下面的测试:

  @Before
  	public void save(){
  		Session session=sf.openSession();
  		session.beginTransaction();
  		for(int i=0;i<10;i++){
  			User u=new User();
  			u.setName(Math.random()+"");
  			session.save(u);
  		}
  		session.getTransaction().commit();
  		session.close();
  	}

首先任意的保存10个对象,然后模拟高并发修改:

  @Test
  	public void testUpdate() throws Exception{
  		for(int i=0;i<10;i++){
  			Thread t=new Thread(new Runnable() {
  				Random random=new Random();
  				public void run() {
  					Session session=sf.openSession();
  					session.beginTransaction();
  					for(int i=0;i<5;i++){
  						Long id= new Long(random.nextInt(10)+1);
  						System.out.println(Thread.currentThread().getName()+"    "+id);
  						User u=(User)session.get(User.class,id);
  						u.setName(Math.random()+"");
  					}
  					session.getTransaction().commit();
  					session.close();
  				}
  			},"thread"+i);
  			t.start();
  		}
  		Thread.sleep(10000);
  	}

该测试开启10个线程,在每个线程中的同一个事务中,随机得到5个User对象,并修改名字,然后提交事务。运行测试,99%的情况下都会报错:

  Caused by: com.mysql.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
  	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:941)
  	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2941)
  	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1623)
  	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1715)
  	at com.mysql.jdbc.Connection.execSQL(Connection.java:3249)
  	at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1268)
  	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1541)
  	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1455)
  	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1440)
  	at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:133)
	... 16 more

造成了死锁,异常产生原因简单分析如下:

假设Thread1得到的User对象的顺序为1,4,6,9,3;Thread2得到的User对象的顺序为2,3,9,8,4;那么假设当Thread1运行到update4的时候,锁住ID:4这行数据,Thread2运行到update9,锁住ID:9这行数据,Thread1运行到update 9的时候等待Thread2释放ID:9这行数据的锁,Thread2运行到update 4的时候,等待Thread1释放ID:4这行数据的锁,造成死锁。

如果修改hibernate配置文件:

  <property name="hibernate.order_updates">true</property>

再次运行,运行测试成功。

在update的时候,ID排序了。执行过程简单分析如下:

Thread1得到User对象的顺序为1,4,6,9,3;但是在更新的时候顺序调整为1,3,4,6,9;

Thread2得到User对象的顺序为2,3,9,8,4;但是在更新的时候顺序调整为2,3,4,9,8;

那么当Thread2更新ID:3的时候,就会等着Thread1释放锁,而不会锁住任何ID:3之前的数据,所以不会造成Thread1的死锁,所以能正常运行。(MySQL总是一条一条执行SQL)。

个人认为这个选项建议设置为true。


原文地址:https://www.cnblogs.com/riskyer/p/3281450.html