for upate使用场景在Spring Date JPA之 for update中已经介绍过了,想要了解,可以看博客:https://blog.csdn.net/zc_ad/article/details/83578487,在这边会介绍一下在mybatis中的使用场景。使用了mybatis-plus,前面有些过基础教程,地址:https://blog.csdn.net/zc_ad/article/details/83301911
与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 {
}
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 {
}
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 {
/**将此行数据进行加锁,当整个方法将事务提交后,才会解锁*/
@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 "选课成功";
}
}