hibernate学习之第十六篇

session context和事务边界
用current_session_context_class属性来定义context(用sessionFactory.getCurrentSession()来获得session),其值为:
1,thread:ThreadLocal来管理Session实现多个操作共享一个Session,避免反复获取Session,并控制事务边界,此时session不能调用close。当commit或rollback的时候session会自动关闭。

Open session in view: 在生成(渲染)页面时保持session打开。

2,jta:有JTA事务管理器来管理事务
connection.release_mode:after_statement

悲观锁和乐观锁
悲观锁有数据库来实现:乐观锁hibernate用version和timestamp来实现。

悲观锁定,是在读取记录时将其锁定,对记录更新修改后,解锁释放,其他人才可以进行相关操作。这回导致一些问题,一个人读取了数据后会加锁,但是却并没有继续的操作,导致其他人也想读取相同的记录时就会受限,只能等前一个人操作完后才能进行。

而乐观锁,采取的是版本号和时间戳的方式。比如,两个人都可以读取数据记录,在相应的完成修改提交更新时,先提交的没有问题(当时数据的版本号并没有修改过,提交后会导致版本号增加),后提交的会遇到版本号问题。因为先提交的会导致版本号更新,新的版本号和后提交方的版本号不一致了,会导致后者的回滚,这会提示后者,数据已被更新,要么放弃更改,要么再次读取,然后更新。

示例代码:
User 类源代码如下:与以前的代码的区别是增加了一个版本属性
Public class User{
    private int id;
    private Name userName;
    private Date birthday;
    private int version;
}
映射文件的配置也要做相应的修改:
<hibernate-mapping package="hibernatetest">
  <class name="User" table="user">
    <id name="id">
      <generator class="native"/>
    </id>
    <version name="version"/> //该元素必须位于id标签后
    <component name="userName">
    <property column="first_name" name="firstName"/>
      <property column="last_name" name="lastName"/>
    </component>
    <property name="birthday"/>
  </class>
</hibernate-mapping>
测试代码:

public class Main {

    public static void main(String[] args) {
        User user = new User();
        user.setBirthday(new Date());

        saveUser(user);
        updateUser(user.getId());

 }

    public static void saveUser(User user) {
        Session session = HibernateUtil.getSession();
        Transaction tx = session.beginTransaction();

        Name name = new Name();
        name.setFirstName("firstName");
        name.setLastName("lastName");
        user.setUserName(name);
        session.save(user);
        tx.commit();
        session.close();
    }

    public static void updateUser(int id) {
        Session s1 = HibernateUtil.getSession();
        Transaction tx1 = s1.beginTransaction();
        User user = (User)s1.get(User.class, id);
        user.getUserName().setFirstName("new1 first name");//修该数据
       
        Session s2 = HibernateUtil.getSession();
        Transaction tx2 = s2.beginTransaction();
        User user2 = (User)s2.get(User.class, id);
        user2.getUserName().setFirstName("new2 first name");//修该数据

        tx1.commit();
        tx2.commit();
        s1.close();
        s2.close();
    }
}
 

打印输出的sql语句为:
Hibernate: insert into user (version, first_name, last_name, birthday) values (?, ?, ?, ?)
Hibernate: select user0_.id as id0_0_, user0_.version as version0_0_, user0_.first_name as first3_0_0_, user0_.last_name as last4_0_0_, user0_.birthday as birthday0_0_ from user user0_ where user0_.id=?
Hibernate: select user0_.id as id0_0_, user0_.version as version0_0_, user0_.first_name as first3_0_0_, user0_.last_name as last4_0_0_, user0_.birthday as birthday0_0_ from user user0_ where user0_.id=?
Hibernate: update user set version=?, first_name=?, last_name=?, birthday=? where id=? and version=?
Hibernate: update user set version=?, first_name=?, last_name=?, birthday=? where id=? and version=?

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [hibernatetest.User#1]
Exception in thread "main" org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [hibernatetest.User#1]


在updateUser方法中,提交了两个事务,产生了不一致性。后提交的事务被抛异常回滚。
数据库中表的结构为:
+----+---------+-----------------+-----------+---------------------+
| id | version | first_name      | last_name | birthday            |
+----+---------+-----------------+-----------+---------------------+
|  1 |       1 | new1 first name | lastName  | 2009-02-08 14:32:43 |
+----+---------+-----------------+-----------+---------------------+
第一次保存数据时,版本号为0.更新后版本号升级为1.
Hibernate: update user set version=?, first_name=?, last_name=?, birthday=? where id=? and version=?
这条更新语句说明了hibernate内部是参照了id和version的。version不一致则会出现问题。


除了使用version标签,还可以使用timestamp标签。时间戳标签的类型只能是时间了,通过比较时间的先后来决定这次事务是否执行。但是在比较极端的情况下,可能在并发很高时,时间的差异很小到难以区分时,时间戳会失去作用。所以最稳妥的还是用版本来控制。version标签的type属性可以指定版本属性类型,除了为整型,还可以指定为时间戳,这样就和使用timestamp标签类似了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值