mysql锁机制分为表级锁和行级锁,此外根据分页查询,还可以扩展出页级锁。
顾名思义,表级锁可以理解为锁住整个表,可以同时读,但是不能同时写,也就是说,直接锁定整张表,在你锁定期间,其它进程无法对该表进行写操作。如果你是写锁,则其它进程则读也不允许。如:
select * from emp for update;
行级锁是仅对指定的记录进行加锁,这样其它进程还是可以对同一个表中的其它记录进行操作。如:
select * from emp where id=1 for update ;
页级锁是一次锁定相邻的一组记录,比表级锁速度快,但冲突多,比行级冲突少,但速度慢。如:
select * from emp limit 0,5 for update ;
在行级锁中有共享锁与排他锁。
共享锁(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,从而实现了乐观锁机制。