高级特性—数据库锁操作


数据库是一个多用户使用的共享资源,当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况,若对并发操作不加以控制就可能会造成数据的错误读取和存储,破坏数据库的数据一致性,所以说,加锁是实现数据库并发控制的一个非常重要的技术;

数据库加锁的流程是:当事务在对某个数据对象进行操作前,先向系统发出请求对其加锁,加锁后的事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作;

因此,JDBC模块在数据库查询操作中集成了针对数据库记录锁的控制能力,称之为IDBLocker,以参数的方式使用起来同样的简单!

首先了解一下IDBLocker提供的锁的类型:

  • MySQL:

    IDBLocker.MYSQL:行级锁,只有符合条件的数据被加锁,其它进程等待资源解锁后再进行操作;

  • Oracle:

    IDBLocker.ORACLE:行级锁,只有符合条件的数据被加锁,其它进程等待资源解锁后再进行操作;

    IDBLocker.ORACLE_NOWAIT:行级锁,不进行资源等待,只要发现结果集中有些数据被加锁,立刻返回“ORA-00054错误”;

  • SQL Server:

    IDBLocker.SQLSERVER_NOLOCK:不加锁,在读取或修改数据时不加任何锁;

    IDBLocker.SQLSERVER_HOLDLOCK:保持锁,将此共享锁保持至整个事务结束,而不会在途中释放;

    IDBLocker.SQLSERVER_UPDLOCK:修改锁,能够保证多个进程能同时读取数据但只有该进程能修改数据;

    IDBLocker.SQLSERVER_TABLOCK:表锁,整个表设置共享锁直至该命令结束,保证其他进程只能读取而不能修改数据;

    IDBLocker.SQLSERVER_PAGLOCK:页锁;

    IDBLocker.SQLSERVER_TABLOCKX:排它表锁,将在整个表设置排它锁,能够防止其他进程读取或修改表中的数据;

  • 其它数据库:

    可以通过IDBLocker接口自行实现;

下面通过示例代码展示如何使用锁:

示例代码一:通过EntitySQL对象传递锁参数;

session.find(EntitySQL.create(User.class)
        .field(Fields.create(User.FIELDS.ID, User.FIELDS.USER_NAME).excluded(true))
        .forUpdate(IDBLocker.MYSQL));

示例代码二:通过Select查询对象传递锁参数;

Select _select = Select.create(User.class, "u")
        .field("u", "username").field("ue", "money")
        .where(Where.create(
                Cond.create().eq(User.FIELDS.ID).param("bc19f5645aa9438089c5e9954e5f1ac5")))
        .forUpdate(IDBLocker.MYSQL);

session.find(SQL.create(_select), IResultSetHandler.ARRAY);

示例代码三:基于数据实体对象传递锁参数

//
User _user = new User();
_user.setId("bc19f5645aa9438089c5e9954e5f1ac5");
//
_user.load(IDBLocker.MYSQL);

//
User _user = new User();
_user.setUsername("suninformation");
_user.setPwd(DigestUtils.md5Hex("123456"));
//
IResultSet<User> _users = _user.find(IDBLocker.MYSQL);

注意

请谨慎使用数据库锁机制,尽量避免产生锁表,以免发生死锁情况!