mybatis 使用for update,對資料進行行級鎖定
阿新 • • 發佈:2018-12-18
與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} ")
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) {
/** courseRepository.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 "選課成功";
}
}