mybatis uses for update to lock data at row level

The usage scenario of for update has been introduced in Spring Date JPA for update. For more information, see the blog: https://blog.csdn.net/zc_ad/article/details/83578487 , we will introduce the usage scenario in mybatis here. Mybatis plus is used. Some basic tutorials have been done before. Address: https://blog.csdn.net/zc_ad/article/details/83301911

As with the use of row level locks in Spring Date JPA, transactions need to be added, and for update needs to be added when querying. Direct code:

Database table:

create table t_pub_student(
id int PRIMARY key auto_increment,
code VARCHAR(50) COMMENT 'Student CODE',
name VARCHAR(50) COMMENT 'Student name'
)

create table t_course_detail(
id int PRIMARY key auto_increment,
name VARCHAR(50) COMMENT 'Course title',
teacher_name VARCHAR(50) COMMENT 'Teacher's name',
elective_total int COMMENT 'Optional total',
elective_num int COMMENT 'Selected quantity'
)

create table t_course_detail(
id int PRIMARY key auto_increment,
student_code varchar(50) COMMENT 'Student code',
course_id int COMMENT 'curriculum ID'
)

Code directory structure:

Define entity:

package course.entity;

import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;

import javax.persistence.*;

/**
 * Created by Xichuan on 2018-10-31.
 */
@TableName("t_pub_student")
public class Student {

    @TableId(value = "id",type = IdType.AUTO)
    private Integer id;

    @Column(name = "code")
    private String code;

    @Column(name = "name")
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

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

import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;

import javax.persistence.*;

/**
 * Created by Xichuan on 2018-10-31.
 */
@TableName("t_pub_course")
public class Course {

    @TableId(value = "id",type = IdType.AUTO)
    private Integer id;

    @Column(name = "name")
    private String name;

    @Column(name = "teacher_name")
    private String teacherName;

    @Column(name = "elective_total")
    private Integer electiveTotal;

    @Column(name = "elective_num")
    private Integer electiveNum;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getTeacherName() {
        return teacherName;
    }

    public void setTeacherName(String teacherName) {
        this.teacherName = teacherName;
    }

    public Integer getElectiveTotal() {
        return electiveTotal;
    }

    public void setElectiveTotal(Integer electiveTotal) {
        this.electiveTotal = electiveTotal;
    }

    public Integer getElectiveNum() {
        return electiveNum;
    }

    public void setElectiveNum(Integer electiveNum) {
        this.electiveNum = electiveNum;
    }
}
package course.entity;

import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;

import javax.persistence.*;

/**
 * Created by Xichuan on 2018-10-31.
 */
@TableName("t_course_detail")
public class CourseDetail {

    @TableId(value = "id",type = IdType.AUTO)
    private Integer id;

    @Column(name = "course_id")
    private Integer courseId;

    @Column(name = "student_code")
    private String studentCode;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getCourseId() {
        return courseId;
    }

    public void setCourseId(Integer courseId) {
        this.courseId = courseId;
    }

    public String getStudentCode() {
        return studentCode;
    }

    public void setStudentCode(String studentCode) {
        this.studentCode = studentCode;
    }
}

DAO layer code:

package course.mapper;

import com.baomidou.mybatisplus.mapper.BaseMapper;
import course.entity.Student;
import org.apache.ibatis.annotations.Mapper;

/**
 * Created by XiChuan on 2018-10-31.
 */
@Mapper
public interface StudentMapper extends BaseMapper<Student> {
}
package course.mapper;

import com.baomidou.mybatisplus.mapper.BaseMapper;
import course.entity.CourseDetail;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface CourseDetailMapper extends BaseMapper<CourseDetail> {
}
package course.mapper;

import com.baomidou.mybatisplus.mapper.BaseMapper;
import course.entity.Course;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

/**
 * Created by Xichuan on 2018-10-31.
 */
public interface CourseMapper extends BaseMapper<Course> {

    /**Lock the data in this row, and the transaction will not be unlocked until the whole method commits the transaction*/
    @Select(value = "select t from t_pub_course t where t.id = #{courseId} ")
    Course queryAllById(@Param("courseId") Integer courseId);

    /**Add 1 to the electiveNum in the course table*/
    @Update("update t_pub_course t set t.elective_num = t.elective_num + 1 where t.id = #{courseId}")
    void addElectiveNumByCourseId(@Param("courseId") Integer courseId);
}

Define interface:

package course.controller;

import course.service.CourseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created by XiChuan on 2018-10-31.
 */
@RestController
public class CourseController {

    @Autowired
    CourseService courseService;

    @PostMapping("/course/choose")
    public Object chooseCourse(@RequestParam("student_code")String studentCode,
                               @RequestParam("course_id")Integer courseId){
        return  courseService.chooseCourse(studentCode,courseId);
    }
}

service layer code:

public interface CourseService {
    Object chooseCourse(String studentCode,Integer courseId);
}
package course.service.impl;

import course.entity.Course;
import course.entity.CourseDetail;
import course.mapper.CourseDetailMapper;
import course.mapper.CourseMapper;
import course.mapper.StudentMapper;
import course.service.CourseService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Objects;

/**
 * Created by XiChuan on 2018-10-31.
 */
@Service
public class CourseServiceImpl implements CourseService {
    private Logger logger = LoggerFactory.getLogger(CourseServiceImpl.class);

    @Autowired
    StudentMapper studentMapper;

    @Autowired
    CourseMapper courseMapper;

    @Autowired
    CourseDetailMapper courseDetailMapper;


    /**This transaction must be added when using for update
     * After the transaction is completed, for update will release the row level lock*/
    @Transactional(isolation = Isolation.READ_COMMITTED)
    // @Transactional(value = "testTransactionManager") / / if it is a multi data source, you need to specify a data source
    @Override
    public Object chooseCourse(String studentCode, Integer courseId) {

        /** courseRepository.queryAllById(courseId)A row level lock will be added to the selected record, and other threads will queue here to unlock after the transaction is committed*/
        Course course = courseMapper.queryAllById(courseId);

        int electiveNum = course.getElectiveNum();
        int totalNum = course.getElectiveTotal();
        logger.info("After Lock Step 1, Thread: {},courseId{}, studentId: {}, electiveNum: {}, total: {}", Thread.currentThread(),courseId,studentCode, electiveNum, totalNum);

        if (Objects.isNull(course)){
            return "Course does not exist";
        }
        if (electiveNum >= totalNum) {
            return "This course has been selected";
        }

        /**Save this student's course selection information to the course selection details*/
        CourseDetail courseDetail = new CourseDetail();
        courseDetail.setCourseId(courseId);
        courseDetail.setStudentCode(studentCode);
        courseDetailMapper.insert(courseDetail);

        /**Add 1 to the electiveNum in the course table
         * It is safer to use sql for accumulation, because the electronum in the course of using method to start query is not necessarily the value stored in the database*/
        courseMapper.addElectiveNumByCourseId(courseId);
        return "Successful course selection";
    }
}

 

Keywords: Programming Apache Spring Mybatis Database

Added by darktimesrpg on Thu, 12 Dec 2019 18:40:18 +0200