一、为什么要使用锁?
要想弄清楚锁机制存在的原因,首先要了解事务的概念。
事务是对数据库一系列相关的操作,它必须具备ACID特征:
A(原子性):要么全部成功,要么全部撤销。
C(一致性):要保持数据库的一致性。
I(隔离性):不同事务操作相同数据时,要有各自的数据空间。
D(持久性):一旦事务成功结束,它对数据库所做的更新必须永久保持。
我们常用的关系型数据库RDBMS实现了事务的这些特性。其中,原子性、
一致性和持久性都是采用日志来保证的。而隔离性就是由今天我们关注的
锁机制来实现的,这就是为什么我们需要锁机制。
如果没有锁,对隔离性不加控制,可能会造成哪些后果呢?
1.更新丢失:事务1提交的数据被事务2覆盖。
2.脏读:事务2查询到了事务1未提交的数据。
3.虚读:事务2查询到了事务1提交的新建数据。
4.不可重复读:事务2查询到了事务1提交的更新数据。
下面来看Hibernate的例子,两个线程分别开启两个事务操作tb_account表中
的同一行数据col_id=1。
package com.cdai.orm.hibernate.annotation;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "tb_account")
public class Account implements Serializable {
private static final long serialVersionUID = 5018821760412231859L;
@Id
@Column(name = "col_id")
private long id;
@Column(name = "col_balance")
private long balance;
public Account() {
}
public Account(long id, long balance) {
this.id = id;
this.balance = balance;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getBalance() {
return balance;
}
public void setBalance(long balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Account [id=" + id + ", balance=" + balance + "]";
}
}
package com.cdai.orm.hibernate.transaction;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;
import com.cdai.orm.hibernate.annotation.Account;
public class DirtyRead {
public static void main(String[] args) {
final SessionFactory sessionFactory = new AnnotationConfiguration().
addFile("hibernate/hibernate.cfg.xml").
configure().
addPackage("com.cdai.orm.hibernate.annotation").
addAnnotatedClass(Account.class).
buildSessionFactory();
Thread t1 = new Thread() {
@Override
public void run() {
Session session1 = sessionFactory.openSession();
Transaction tx1 = null;
try {
tx1 = session1.beginTransaction();
System.out.println("T1 - Begin trasaction");
Thread.sleep(500);
Account account = (Account)
session1.get(Account.class, new Long(1));
System.out.println("T1 - balance=" + account.getBalance());