【问题标题】:How to acquired a lock at database row level using in Spring Boot and Hibernate?如何在 Spring Boot 和 Hibernate 中获取数据库行级别的锁?
【发布时间】:2022-01-12 10:01:52
【问题描述】:

我正在从数据库中读取一条记录并更新它,我有两种方法。 但是在这两个操作之间,我想在那个特定的行上获得一个锁,这样不同的线程就不会处理同一行。

这是我的存储库代码:

        package com.anco.crud.repository;

import com.anco.crud.model.Employee;
import org.hibernate.Session;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import javax.persistence.EntityManager;

@Repository
public class EmployeeRepositoryImpl implements EmployeeRepository{


    @Autowired
    private EntityManager entityManager;

@Override
@Transactional
public Employee findEmpToProcessAndUpdateStatus() {
    Session currentSession = entityManager.unwrap(Session.class);

    String hql = "from Employee where status is null";
    Query query = currentSession.createQuery(hql);
    query.setMaxResults(1);
    Employee employee = (Employee) query.uniqueResult();
    System.out.println("FETCHED : " + employee.getFirstName());
    employee.setStatus("INPROGRESS");
    updateEmployee(employee);
    return employee;
}

private boolean updateEmployee(Employee employee) {
    try{
        Thread.sleep(3000);
        Session currentSession = entityManager.unwrap(Session.class);
        currentSession.update(employee);
        System.out.println("UPDATED : " + employee.getFirstName());
    }catch (Exception e){
        e.printStackTrace();
    }
    return true;
}
}

控制器代码:

    @GetMapping("/employee/{mail}")
    public boolean getAndUpdate() {
        for(int i=0;i<5;i++){
            Runnable rn = () -> process();
            this.executor.execute(rn);
        }
        return true;
    }

    private void process() {
    try {
        Employee emp = employeeRepository.findEmpToProcessAndUpdateStatus();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

如果这不是正确的方法,那么更好的方法是什么?

【问题讨论】:

  • 使用spring事务管理,注入entitymanager。将逻辑放入标记为@Transactional 的服务方法中,以便所有内容都在单个事务中执行。不要使用syncrhonized,因为那根本行不通。
  • 嗨@M.Deinum,感谢您的回复。我已经更新了代码 sn-p ...所有线程都选择了具有相同邮件 ID 的员工
  • 我说服务不是控制器,注释私有方法和内部方法调用都不起作用。您仍然需要在您的选择方法中选择更新,否则它将不起作用。
  • @M.Deinum,我再次更新了代码......仍然没有达到预期的效果
  • 再一次你没有使用锁。另外,为什么要强制转换为Session?只需使用EntityManager。在查询中使用 setLockType(或者是 setLockMode 并使用您想要的。另外,您不需要调用更新,因为它是一个托管对象,它将自动持久化。

标签: java spring-boot hibernate jpa concurrency


【解决方案1】:

您可以设置特定的锁定模式,以便向 DBMS 发出“选择更新”:

EntityManager em;
...
Employee found = em.find(Employee.class, empId, LockModeType.PESSIMISTIC_WRITE);

【讨论】:

    【解决方案2】:

    首先要充分利用 Spring,您目前正在解决它。您应该有一个可以完成工作的单一事务方法。接下来只需使用 JPA 而不是直接访问 Hibernate Session,这只会增加混乱/复杂性。最后在查询上使用setLockMode 来确定锁。

    您的存储库方法(或服务)应如下所示

    @Repository
    public class EmployeeRepositoryImpl implements EmployeeRepository{
    
    
      @PersistenceContext
      private EntityManager em;
    
      @Override
      @Transactional
      public Employee findEmpToProcessAndUpdateStatus() {
    
        String hql = "SELECT e FROM Employee e where e.status is null";
        TypedQuery<Employee> query = em.createQuery(hql, Employee.class);
        query.setMaxResults(1);
        query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
        Employee employee = query.getSingleResult();
        System.out.println("FETCHED : " + employee.getFirstName());
        employee.setStatus("INPROGRESS");
        this.updateEmployee(employee);
        return employee;
      }
    
      private boolean updateEmployee(Employee employee) {
        try{
            Thread.sleep(3000);
            em.merge(employee);
            System.out.println("UPDATED : " + employee.getFirstName());
        }catch (Exception e){
            e.printStackTrace();
        }
        return true;
      }
    }
    

    但是,这可能会导致所有这些锁在您的数据库上出现死锁。所以要小心使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-22
      • 1970-01-01
      • 2019-07-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多