for upate使用场景在Spring Date JPA之 for update中已经介绍过了,想要了解,可以看博客:Spring Data JPA 之 for update_兮川的博客-CSDN博客,在这边会介绍一下在mybatis中的使用场景。使用了mybatis-plus,前面有些过基础教程,地址:springboot整合mybatis-plus,以及mybatis-plus入门使用_兮川的博客-CSDN博客

与Spring Date JPA中使用行级锁一样,都需要加上事务,并在查询的时候加上for update。直接上代码:

数据库表:

create table t_pub_student(
id int PRIMARY key auto_increment,
code VARCHAR(50) COMMENT '学生CODE',
name VARCHAR(50) COMMENT '学生名字'
)

create table t_course_detail(
id int PRIMARY key auto_increment,
name VARCHAR(50) COMMENT '课程名称',
teacher_name VARCHAR(50) COMMENT '教师名字',
elective_total int COMMENT '可选总数',
elective_num int COMMENT '已选数量'
)

create table t_course_detail(
id int PRIMARY key auto_increment,
student_code varchar(50) COMMENT '学生code',
course_id int COMMENT '课程ID'
)

代码目录结构:

定义实体:

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层代码:

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> {

    /**将此行数据进行加锁,当整个方法将事务提交后,才会解锁*/
    @Select(value = "select t from t_pub_course t where t.id = #{courseId} for update")
    Course queryAllById(@Param("courseId") Integer courseId);

    /**将course表中的electiveNum进行加1操作*/
    @Update("update t_pub_course t set t.elective_num = t.elective_num + 1 where t.id = #{courseId}")
    void addElectiveNumByCourseId(@Param("courseId") Integer courseId);
}

定义接口:

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层代码:

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;


    /**使用for update一定要加上这个事务
     * 当事务处理完后,for update才会将行级锁解除*/
    @Transactional(isolation = Isolation.READ_COMMITTED)
    // @Transactional(value = "testTransactionManager") //如果是多数据源,需要制定数据源
    @Override
    public Object chooseCourse(String studentCode, Integer courseId) {

        /** courseMapper.queryAllById(courseId)会对所选中的那条记录加行级锁,其他线程会在此排队,当事务提交后,才会进行解锁*/
        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 "课程不存在";
        }
        if (electiveNum >= totalNum) {
            return "此课程已被选完";
        }

        /**将此此学生的选课信息保存到选课详情里面*/
        CourseDetail courseDetail = new CourseDetail();
        courseDetail.setCourseId(courseId);
        courseDetail.setStudentCode(studentCode);
        courseDetailMapper.insert(courseDetail);

        /**将course表中的electiveNum进行加1操作
         * 使用sql进行累加更加安全,因为使用方法开始查询的course中的electiveNum,并不一定是数据库存储的值*/
        courseMapper.addElectiveNumByCourseId(courseId);
        return "选课成功";
    }
}

 github上有我更多的笔记:Raray-chuan (兮川) · GitHub,欢迎stars与following,如果有问题可以在issue中向我咨询

关注我的公众号,获取更多关于后端、大数据的知识

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐