并发访问
可能引发的问题:更新丢失
(A线程把name属性改了,B线程马上改age属性,但是B线程中的name属性仍然是改之前的值,这就造成了A线程对name的更新丢失了)
------------------------
解决方案
*悲观锁 LockOptions.UPGRADE 虽然可以保证多线程下并发访问数据不会造成数据混乱---for update
但是,锁定记录的规则太不灵活,不建议使用
[一旦被某个线程锁定,其它线程无法进行,将被挂起,直到其释放锁其它线程才能进入]
行级锁,当A线程在更新此行时,其它线程无法查询到此行的数据,只有等到A线程操作完成,其它线程才能对该行进行查询,这样其它线程查询到的结果就是A线程更新后的结果!
------------------------
*乐观锁 (解决并发访问通用方法!)
<!-- 版本标识,用于解决并发访问 -->
<version name="versionNumber"></version>
定义在<id></id>与<property></property>之间
实体类中添加属性 private int versionNumber;
数据库中该字段的值由hibernate维护,程序员不用管理;
好处:提高性能,避免死锁
对于读操作没有限制,即读操作线程不受限制
通过比较线程所操纵的版本号与数据库记录的版本号是否一致来决定更新是否成功
版本号相同的记录才能被更新,
否则更新失败,抛出异常 Row was updated or deleted by another transaction
可以在捕获异常的时候,转化异常为"更新失败",提示重新更新
package org.leadfar.hibernate.model;
import java.util.Set;
public class Group {
private int id;
private String name;
private Set<ContactPerson> persons;
public Set<ContactPerson> getPersons() {
return persons;
}
public void setPersons(Set<ContactPerson> persons) {
this.persons = persons;
}
public Group() {
// TODO Auto-generated constructor stub
}
public Group(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="org.hibernate.auction"> <!-- name为实体类 table为映射到数据库中的表 lazy默认为true 延迟发出select语句,直到真正用到对象的属性(非id属性)--> <class name="org.leadfar.hibernate.model.Group" table="t_group" batch-size="25"> <!-- id为数据库标识,作为主键 --> <id name="id"> <generator class="native"/> </id> <property name="name"/> <!-- <set name="persons" fetch="select" batch-size="20"> <key column="gid"></key> <one-to-many class="org.leadfar.hibernate.model.ContactPerson"/> </set> --> <set name="persons" fetch="subselect"> <key column="gid"></key> <one-to-many class="org.leadfar.hibernate.model.ContactPerson"/> </set> </class> </hibernate-mapping>
package org.leadfar.hibernate.model;
import java.util.Date;
public class ContactPerson {
private int id;
private String name;
private int age;
private Date birthday;
private Group group;
private int versionNumber;//版本字段
public ContactPerson() {
}
public ContactPerson(String name) {
this.name = name;
}
public ContactPerson(int id,String name) {
this.id = id;
this.name = name;
}
public ContactPerson(int id,int age,String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Group getGroup() {
return group;
}
public void setGroup(Group group) {
this.group = group;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public int getVersionNumber() {
return versionNumber;
}
public void setVersionNumber(int versionNumber) {
this.versionNumber = versionNumber;
}
}
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="org.hibernate.auction"> <!-- name为实体类 table为映射到数据库中的表 lazy默认为true 延迟发出select语句,直到真正用到对象的属性(非id属性)--> <class name="org.leadfar.hibernate.model.ContactPerson" table="t_person"> <id name="id"> <generator class="native"/> </id> <!-- 版本标识,用于解决并发访问 --> <version name="versionNumber"></version> <property name="name"/> <property name="age"></property> <property name="birthday" type="date"></property> <many-to-one name="group" column="gid" fetch="join"></many-to-one> </class> </hibernate-mapping>
测试
package org.leadfar.hibernate.model;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.Set;
import junit.framework.TestCase;
import org.hibernate.LockOptions;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class TestHQL extends TestCase {
public void save_person() throws Exception {
Configuration cfg = new Configuration().configure();
SessionFactory sfactory = cfg.buildSessionFactory();
Session session = sfactory.openSession();
try {
//开启事务
session.beginTransaction();
Random r = new Random();
for(int i=0;i<100;i++) {
Group g = new Group("组"+i);
session.save(g);
ContactPerson cp = new ContactPerson("用户"+i);
cp.setAge(r.nextInt(99));
cp.setBirthday(new Date());
cp.setGroup(g);
session.save(cp);
}
//提交事务
session.getTransaction().commit();
} catch(Exception e) {
e.printStackTrace();
//出现异常,回滚事务
session.getTransaction().rollback();
} finally {
//关闭session
session.close();//session关闭之后,user对象处于离线Detached状态
}
}
/**
* 悲观锁 upgrade
* 利用数据库对记录进行锁定,需要等待更新操作完成后才能对其进行读取和更新
*/
public void lock_upgrade_01() throws Exception {
Configuration cfg = new Configuration().configure();
SessionFactory sfactory = cfg.buildSessionFactory();
Session session = sfactory.openSession();
try {
session.beginTransaction();
ContactPerson cp = (ContactPerson)session.get(ContactPerson.class, 100,LockOptions.UPGRADE);
System.out.println(cp.getName());
cp.setName("AA");
session.getTransaction().commit();
} catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
session.close();
}
}
public void lock_upgrade_02() throws Exception {
Configuration cfg = new Configuration().configure();
SessionFactory sfactory = cfg.buildSessionFactory();
Session session = sfactory.openSession();
try {
session.beginTransaction();
ContactPerson cp = (ContactPerson)session.get(ContactPerson.class, 100,LockOptions.UPGRADE);
System.out.println(cp.getName());
cp.setAge(101);
session.getTransaction().commit();
} catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
session.close();
}
}
/**
* 乐观锁
*/
public void lock_optimism_01() throws Exception {
Configuration cfg = new Configuration().configure();
SessionFactory sfactory = cfg.buildSessionFactory();
Session session = sfactory.openSession();
try {
session.beginTransaction();
ContactPerson cp = (ContactPerson)session.get(ContactPerson.class, 100);
System.out.println(cp.getName());
cp.setName("BB");
session.getTransaction().commit();
} catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
session.close();
}
}
public void lock_optimism_02() throws Exception {
Configuration cfg = new Configuration().configure();
SessionFactory sfactory = cfg.buildSessionFactory();
Session session = sfactory.openSession();
try {
session.beginTransaction();
ContactPerson cp = (ContactPerson)session.get(ContactPerson.class, 100);
System.out.println(cp.getName());
cp.setAge(88);
session.getTransaction().commit();
} catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
session.close();
}
}
}