用Hibernate框架演示樂觀鎖

  mysql鎖機制分爲表級鎖和行級鎖,此外根據分頁查詢,還可以擴展出頁級鎖。

   在行級鎖中有共享鎖與排他鎖。

  共享鎖(Share Locks),簡稱S鎖,又稱爲讀鎖,它是多個事務對於同一數據可以共享的一把鎖,多個事務都能訪問到數據,但是隻能讀不能修改。普通查詢沒有任何鎖機制。

加共享鎖可以使用select ... lock in share mode語句 

  排他鎖(Exclusive Locks),簡稱X鎖,又稱爲寫鎖,該鎖能與其他鎖並存,如果一個事務獲取了一個數據行的排他鎖,其他事務就不能再獲取該行的其他鎖,包括共享鎖和排他鎖,但是獲取排他鎖的事務是可以對數據就行讀取和修改的。

加排他鎖可以使用select ...for update語句

   無論採用共享鎖,還是排他鎖,都要開啓事務。

  共享鎖和排他鎖均隸屬於悲觀鎖,一個線程在使用的時候,其他線程只能排隊等待,因而速度極慢。
  在實際使用中,樂觀鎖一般是我們用得比較多的鎖,這是因爲樂觀鎖的效率較高。例如,版本管理工具SVN和git就採用了樂觀鎖機制,通常是對版本version 進行管理。

採用樂觀鎖機制管理版本是這樣判斷的:
我們拿到原始版本後,立即在自己手頭的版本是自增1,將來提交的時候,如果手頭這個版本比原始版本大1,就是可以正常提交的,否則就有問題。

   接下來,我通過Hibernate框架演示樂觀鎖操作。

  創建一個工程案例,將hibernate依賴的jar包導入工程:

  在工程的src目錄下創建hibernate.cfg.xml配置文件:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!--hibernate配置-->
<hibernate-configuration>
    <session-factory>
        <property name="connection.url">jdbc:mysql://localhost:3306/b0307</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.username">root</property>
        <property name="connection.password">2018</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.current_session_context_class">thread</property>
        <mapping resource="com/itszt/domain/Emp.hbm.xml"></mapping>
    </session-factory>
</hibernate-configuration>

   表emp的結構如下:

  測試用的實體類文件Emp.java:

package com.itszt.domain;

/**
 * 實體類
 */
public class Emp {
    private Integer uid,version;
    private String name;

    public Emp() {
    }

    @Override
    public String toString() {
        return "Emp{" +
                "uid=" + uid +
                ", version=" + version +
                ", name='" + name + '\'' +
                '}';
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Emp(Integer uid, Integer version, String name) {
        this.uid = uid;
        this.version = version;
        this.name = name;
    }
}

   類Emp與表emp之間的映射配置文件Emp.hbm.xml爲:

<?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 package="com.itszt.domain">
    <class name="Emp" table="emp">
        <id name="uid" column="uid">
            <generator class="native"></generator>
        </id>
        <version name="version" column="version"></version><!--此處加version是配置樂觀鎖的關鍵,與Emp中的屬性version對應-->
        <property name="name" column="name"></property>      
    </class>
</hibernate-mapping>

   hibernate工具類文件UtilHibernate.java,該工具類運行時加載hibernate配置文件,產生操作數據庫的session連接對象:

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

/**
 * hibernate工具類
 */
public class UtilHibernate {
    private static Configuration configuration;
    private static SessionFactory sessionFactory;

    //session是要重複調用的
    //配置文件是要初始加載一次的
    static {
        try {
            configuration = new Configuration().configure();
            sessionFactory = configuration.buildSessionFactory();
            //JVM的關閉鉤子,在JVM退出時close我們的sessionFactory
            Runtime.getRuntime().addShutdownHook(new Thread() {
                @Override
                public void run() {
                    super.run();
                    System.out.println("Execute Hook.....");
                    if (sessionFactory != null) {
                        sessionFactory.close();
                    }
                }
            });
        } catch (HibernateException e) {
            throw new ExceptionInInitializerError("hibernate加載失敗!!");
        }
    }
    //開啓一個新的session
    public static Session openNew() {
        return sessionFactory.openSession();
    }
    //獲取當前的session對象
    public static Session getCurrent() {
        return sessionFactory.getCurrentSession();
    }
    //釋放session資源
    public static void release(Session session) {
        if (session != null) {
            if (session.isOpen()) {
                session.close();
            }
        }
    }
}

   測試類文件TestLock.java內容:

import com.itszt.domain.Emp;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

/**
 * 測試樂觀鎖
 */
public class TestLock {
    public static void main(String[] args) {
        Session session = UtilHibernate.getCurrent();

        Transaction transaction = session.beginTransaction();

        //1.查詢到一個已有數據
        Query query = session.createQuery("FROM Emp WHERE uid=?");
        query.setParameter(0, 6);
        Emp emp = (Emp) query.uniqueResult();
        System.out.println("emp1 = " + emp);

        //2.對已有的數據進行修改,version會自增1
        emp.setName(emp.getName() + "_A");
        System.out.println("emp2 = " + emp);
        transaction.commit();
        UtilHibernate.release(session);
        System.exit(0);
    }
}

   當我們執行該測試類文件時,會發現每執行一次,表emp中的version字段就會自增1,從而實現了樂觀鎖機制。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章