• Hibernate悲观锁(pessimistic lock)实例详解

    悲观锁(pessimistic lock)是指在每次操作数据时,总是悲观地认为会有其他事务操作同一数据,因此,在整个数据处理过程中,会把数据处于锁定状态。

    悲观锁具有排他性,一般由数据库实现。在锁定时间内,其他事务不能对数据进行存取等操作,这可能导致长时间的等待问题。

    在 Hibernate 中,用户可以显示地设定锁模式,通常在应用中会设定如下两种锁模式。

    1)LockMode.UPGRADE

    该模式不管缓存中是否存在对象,总是通过 select 语句到数据库中加载该对象,如果映射文件中设置了版本元素,则执行版本检查,比较缓存中的对象是否与数据库中对象的版本一致,如果数据库系统支持悲观锁(如 MySQL),则执行 select...for update 语句,如果不支持(如 Sybase),则执行普通 select 语句。

    2)LockMode.UPGRADE_NOWAIT

    该模式与 LockMode.UPGRADE 具有同样的功能,是 Oracle 数据库特有的锁模式,会执行 select...for update nowait 语句。

    nowait 表示如果执行 select 语句的事务不成立则获得悲观锁,它不会等待其他事务释放锁,而是立刻抛出锁定异常。

    下面通过丢失更新的案例演示悲观锁的使用。

    1. 创建项目并引入JAR包

    在 MyEclipse 中创建一个名称为 hibernateDemo05 的 Web 项目,将 Hibernate 所必需的 JAR 包添加到 lib 目录中,并发布到类路径下。

    2. 创建实体类

    在 src 目录下创建一个名为 com.mengma.domain 的包,在该包下创建一个 Person 类,定义 id、name 和 age 三个属性,并实现各属性的 getter 和 setter 方法以及 toString() 方法,如下所示。

    package com.mengma.domain;
    
    public class Person {
        private Integer id;
        private String name; // 姓名
        private Integer age; // 年龄
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
    }

    3. 创建映射文件

    在 com.mengma.domain 包中创建一个名为 Person.hbm.xml 的映射文件,将实体类 Person 映射到数据表中,如下所示。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
    <hibernate-mapping>
        <class name="com.mengma.domain.Person" table="person">
            <id name="id" column="id">
                <generator class="native" />
            </id>
            <property name="name" column="name" type="string" />
            <property name="age" column="age" type="integer" />
        </class>
    </hibernate-mapping>

    4. 创建配置文件

    在 src 目录下创建一个名为 hibernate.cfg.xml 的配置文件,该文件中包含数据库的连接信息,以及关联的映射文件信息等,如下所示。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-configuration PUBLIC
              "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
              "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
    <hibernate-configuration>
    <session-factory>
      <!-- 指定方言 -->
      <property name="dialect">
       org.hibernate.dialect.MySQL5Dialect
      </property>
      <!-- 链接数据库url -->
      <property name="connection.url">
                  <![CDATA[jdbc:mysql://localhost:3306/hibernate?useUnicode=true&characterEncoding=utf-8]]>
      </property>
      <!-- 连接数据库的用户名 -->
      <property name="connection.username">
       root
      </property>
      <!-- 数据库的密码 -->
      <property name="connection.password">
       1128
      </property>
      <!-- 数据库驱动 -->
      <property name="connection.driver_class">
       com.mysql.jdbc.Driver
      </property>
      <!-- 显示sql语句 -->
      <property name="show_sql">
       true
      </property>
      <!-- 格式化sql语句 -->
      <property name="format_sql">true</property>
      <!-- 映射文件配置 -->
      <mapping resource="com/mengma/domain/Person.hbm.xml" />
    </session-factory>
    </hibernate-configuration>

    5. 创建工具类

    在 src 目录下创建一个名为 com.mengma.utils 的包,并在该包下创建一个名为 HibernateUtils 的工具类,该工具类的实现代码如下所示。

    package com.mengma.utils;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    
    public class HibernateUtils {
        // 声明一个私有的静态final类型的Configuration对象
        private static final Configuration config;
        // 声明一个私有的静态的final类型的SessionFactory对象
        private static final SessionFactory factory;
        // 通过静态代码块构建SessionFactory
        static {
            config = new Configuration().configure();
            factory = config.buildSessionFactory();
        }
    
        // 提供一个公有的静态方法供外部获取,并返回一个session对象
        public static Session getSession() {
            return factory.openSession();
        }
    }

    6. 创建测试类

    在 src 目录下创建一个名为 com.mengma.test 的包,在该包下创建一个名为 PersonTest 的类。在类中编写一个test1()方法,用于添加一条记录,并检查项目是否可以正常运行。然后再分别编写一个 test2() 方法和 test3() 方法,其中 test2() 方法用于修改 name 属性,test3() 方法用于修改 age 属性。其具体实现代码如下所示。

    package com.mengma.test;
    
    import org.hibernate.Session;
    import org.junit.Test;
    
    import com.mengma.domain.Person;
    import com.mengma.utils.HibernateUtils;
    
    public class PersonTest {
        // 添加数据
        @Test
        public void test1() {
            Session session = HibernateUtils.getSession();
            session.beginTransaction();
            Person person = new Person();
            person.setName("zhangsan");
            person.setAge(20);
            session.save(person);
            session.getTransaction().commit();
            session.close();
        }
    
        // 修改name屈性
        @Test
        public void test2() {
            Session session = HibernateUtils.getSession();
            session.beginTransaction();
            Person person = (Person) session.get(Person.class, 1);
            person.setName("lisi");
            session.save(person);
            session.getTransaction().commit();
            session.close();
        }
    
        // 修改age属性
        @Test
        public void test3() {
            Session session = HibernateUtils.getSession();
            session.beginTransaction();
            Person person = (Person) session.get(Person.class, 1);
            person.setAge(25);
            session.save(person);
            session.getTransaction().commit();
        }
    }

    7. 运行程序并查看结果

    使用 JUnit 测试运行 test1() 方法,运行成功后,控制台的显示结果如图 1 所示。

    输出结果
    图 1  输出结果

更多...

加载中...