Springboot集成Mybatis

原创
2019/08/08 00:00
阅读数 128

Springboot集成Mybatis

Mybatis作为持久层框架,是现在主流的框架, 本文只关注Springboot和Mybatis之间的构建,不关心Mybatis框架如何时使用,如果需要详细了解Mybatis的请绕道,后续可能我也会研究这个框架

一:构建springboot-mybatis

1:pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.geek.calvin</groupId>
	<artifactId>springboot-mybatis</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springboot-mybatis</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.1.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

2:application.yml

server:
  port: 8080

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://localhost:3306/student?useUnicode=true&characterEncoding=utf8&userSSL=false&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
    hikari:
      connection-timeout: 30000
mybatis:
  mapper-locations: classpath:/mybatis/mapper/*.xml

3: ResultEntity.java

本来不想贴这个,但是怕新手丢了,不知道如何做,勿喷

**
 * 请求返回的消息body
 * @author Calvin
 * @date 2019/07/29
 */
public class ResultEntity {

    /**
     * 返回状态
     */
    private int code;

    /**
     * 消息
     */
    private String msg;

    /**
     * 消息body
     */
    private Object data;

    public int getCode() {
        return code;
    }

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

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

二:业务代码编写

1:StudentController.java

/**
 * @author Calvin
 * @date 2019/07/29
 */
@RestController
@RequestMapping("/student")
public class StudentController {

    @Autowired
    private StudentService studentService;
    /**
     * 查找所有的学生
     * @return
     */
    @RequestMapping("/list")
    public ResultEntity listStudent(){
        List<Student> students = studentService.listAll();
        ResultEntity entity = new ResultEntity();
        entity.setCode(200);
        entity.setMsg("请求成功");
        entity.setData(students);
        return entity;
    }
}

2:StudentService.java

/**
 * @author Calvin
 * @date 2019/07/29
 */
public interface StudentService {

    /**
     * 查询学生列表
     * @return
     */
    List<Student> listAll();
}

3:StudentServiceImpl.java

/**
 * @author Calvin
 * @date 2019/07/29
 */

@Service
public class StudentServiceImpl implements StudentService{

    @Autowired
    private StudentMapper studentMapper;

    @Override
    public List<Student> listAll() {
        return studentMapper.listAll();
    }
}

4: Student.java

/**
 * @author Calvin
 * @date 2019/07/29
 */
public class Student {

    /**
     * 学号
     */
    private String id;

    /**
     * 姓名
     */
    private String name;

    /**
     * 性别
     */
    private int gender;

    /**
     * 入学时间
     */
    private Date enterTime;

    /**
     * 年龄
     */
    private int age;

    public String getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public int getGender() {
        return gender;
    }

    public void setGender(int gender) {
        this.gender = gender;
    }

    public Date getEnterTime() {
        return enterTime;
    }

    public void setEnterTime(Date enterTime) {
        this.enterTime = enterTime;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

5:StudentMapper.java

**
 * @author Calvin
 * @date 2019/07/29
 */
@Mapper
public interface StudentMapper {

    /**
     *
     * @return
     */
    List<Student> listAll();

}

6:StudentMapper.xml

<mapper namespace="com.geek.calvin.mapper.StudentMapper">

    <resultMap id="student" type="com.geek.calvin.entity.Student">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="gender" property="gender"/>
        <result column="age" property="age"/>
        <result column="enter_time" property="enterTime"/>
    </resultMap>

    <select id="listAll" resultMap="student">
        select id, name, gender, age, enter_time from student where delete_flag = 0
    </select>

</mapper>

三:数据库

-- create table DDL
DROP TABLE IS EXISTS 'student';
CREATE TABLE `student` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `gender` int(1) DEFAULT NULL,
  `enter_time` datetime DEFAULT NULL,
  `age` int(2) DEFAULT NULL,
  `delete_flag` int(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

-- record 
INSERT INTO STUDENT VALUES(1, "Calvin", "1", str_to_date('2009-09-01', '%Y-%m-%d'), 26, 0);

四:运行结果

chrome中打开localhost:8080/student/list

运行结果

五:相关配置


1:打印sql日志

在application.yml中,配置 loggin.level.#{mapper接口所在包,如果有多个,逗号隔开,或者使用通配符} 末尾给值 debug

logging:
  level:
    com.geek.calvin.mapper: debug

如果是propertie文件就更好办了

loggin.level.com.geek.calvin.mapper=debug

2:PageHelper插件

<!-- pagehelper分页插件 -->
<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper-spring-boot-starter</artifactId>
	<version>1.2.9</version>
</dependency>

改造StudentController.listStudent

/**
 * 查找所有的学生
 * @return 根据分页信息查询学生列表
 */
@RequestMapping("/list")
public ResultEntity listStudent(@RequestParam(value = "pageNum", defaultValue = "1", required = false) int pageNum,
                                @RequestParam(value = "pageSize", defaultValue = "10",required = false) int pageSize){
	PageInfo<Student> students = studentService.listAll(pageNum, pageSize);
	ResultEntity entity = new ResultEntity();
	entity.setCode(200);
	entity.setMsg("请求成功");
	entity.setData(students);
	return entity;
    }

改造StudentService和impl

@Override
public PageInfo<Student> listAll(int pageNum, int pageSize) {
	PageHelper.startPage(pageNum, pageSize);
	List<Student> students = studentMapper.listAll();
	PageInfo<Student> pageInfo = new PageInfo<Student>(students);
	return pageInfo;
}

结果:

分页处理器结果

控制台sql

[nio-8080-exec-7] c.g.c.m.StudentMapper.listAll_COUNT      : ==>  Preparing: SELECT count(0) FROM student WHERE delete_flag = 0 
[nio-8080-exec-7] c.g.c.m.StudentMapper.listAll_COUNT      : ==> Parameters: 
[nio-8080-exec-7] c.g.c.m.StudentMapper.listAll_COUNT      : <==      Total: 1
[nio-8080-exec-7] c.g.calvin.mapper.StudentMapper.listAll  : ==>  Preparing: select id, name, gender, age, enter_time from student where delete_flag = 0 LIMIT ? 
[nio-8080-exec-7] c.g.calvin.mapper.StudentMapper.listAll  : ==> Parameters: 10(Integer)
[nio-8080-exec-7] c.g.calvin.mapper.StudentMapper.listAll  : <==      Total: 1

3:mybatis-plus
https://mp.baomidou.com/guide/

pom.xml中增加依赖

<!-- mybatis-plus-->
<dependency>
	<groupId>com.baomidou</groupId>
	<artifactId>mybatis-plus-boot-starter</artifactId>
	<version>3.1.2</version>
</dependency>

application.yml中

# 删除原来mybatis的配置,修改为mybatis-plus的配置
# mybatis:
#  mapper-locations: classpath:/mybatis/mapper/*.xml
mybatis-plus:
   mapper-locations: classpath:/mybatis/mapper/*.xml

新增 MybatisPlusConfig.java作为mybatis-plus配置类

/**
 * mybatis-plus配置类
 * @author Calvin
 * @date 2019/07/29
 */

@Configuration
@MapperScan("com.geek.calvin.mapper")
public class MybatisPlusConfig {


    /**
     * 分页插件,摘自官网
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // paginationInterceptor.setLimit(你的最大单页限制数量,默认 500 条,小于 0 如 -1 不受限制);
        return paginationInterceptor;
    }

}

新增CourseController.java课程的接口类

/**
 * 课程的接口
 * @author Calvin
 * @date 2019/07/29
 */
@RestController
@RequestMapping("/course")
public class CourseController {

    @Autowired
    private CourseService courseService;


    @RequestMapping("/list")
    public ResultEntity listCourse(@RequestParam(value = "page",defaultValue = "1", required = false) int page,
                                   @RequestParam(value = "pageSize", defaultValue = "10",required = false) int pageSize){
        IPage<Course> courseIPage = courseService.listAll(page, pageSize);
        ResultEntity entity = new ResultEntity();
        entity.setData(courseIPage);
        entity.setMsg("success");
        entity.setCode(200);
        return entity;
    }


    @RequestMapping("/add")
    public ResultEntity create(Course course){
        ResultEntity resultEntity = new ResultEntity();
        resultEntity.setCode(200);
        resultEntity.setMsg("success");
        resultEntity.setData(courseService.create(course));
        return resultEntity;
    }



}

CourseService.java & CourseServiceImpl.java

**
 * 课程的service, 逻辑服务
 * @author Calvin
 * @date 2019/07/29
 * @see com.geek.calvin.controller.CourseController
 */
public interface CourseService {


    /**
     * 根据分页查询
     * @param page
     * @param pageSize
     * @return
     */
    IPage<Course>  listAll(int page, int pageSize);


    /**
     * 新建一门课程
     * @param course 要添加的课程form
     * @return
     */
    Course create(Course course);
}
**
 * @author Calvin
 * @date 2019/07/29
 */
@Service
public class CourseServiceImpl implements CourseService {


    @Autowired
    private CourseMapper courseMapper;


    @Override
    public Course create(Course course) {
        courseMapper.insert(course);
        return course;
    }

    @Override
    public IPage<Course> listAll(int currentPage, int pageSize) {
        IPage page = new Page();
        page.setCurrent(currentPage);
        page.setSize(pageSize);
        return courseMapper.selectPage(page, null);
    }

定义一个空的没有方法的CourseMapper

/**
 * @author Calvin
 * @date 2019/07/29
 */
@Mapper
public interface CourseMapper extends BaseMapper<Course>{
	/**
	 * 此处定义这个是为了方便自己方法,如果不打算写sql,直接用mybatis-plus中的Wrapper进行查询的
	 * 可以直接让自己的service继承BaseMapper,少一个类,虽然我这里没有写
	 */
}

课程实体类 Course.java

/**
 * 课程实体类
 * @author Calvin
 * @date 2019/07/29
 */
@TableName("course")
public class Course {


    /**
     * uuid
     */
    @TableId(type = IdType.UUID)
    private String id;

    /**
     * 课程名
     */
    @TableField
    private String courseName;

    /**
     * 年级
     */
    @TableField
    private int grade;

    /**
     * 节数
     */
    @TableField
    private int lessonsCount;

    /**
     * 分类 1:主修 2:选修
     */
    @TableField
    private int classify;


    public String getId() {
        return id;
    }

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

    public String getCourseName() {
        return courseName;
    }

    public void setCourseName(String courseName) {
        this.courseName = courseName;
    }

    public int getGrade() {
        return grade;
    }

    public void setGrade(int grade) {
        this.grade = grade;
    }

    public int getLessonsCount() {
        return lessonsCount;
    }

    public void setLessonsCount(int lessonsCount) {
        this.lessonsCount = lessonsCount;
    }

    public int getClassify() {
        return classify;
    }

    public void setClassify(int classify) {
        this.classify = classify;
    }
}

创建表的sql

create table course(
	id varchar(40) PRIMARY key , 
	course_name varchar(40) comment '课程名称' ,
	grade int(2) comment '年级',
	lessons_count int(2) comment '课程节数' ,
	classify int(2) comment '分类, 1:入门 2:进阶'
);

这里不使用代码生成器,是觉得代码生成器生成的文件比较繁重,有需要的同学自行研究

add接口 测试调用

//http://localhost:8080/course/add?courseName=设计模式之禅&classify=1&lessonsCount=30&grade=2
{
  "code": 200,
  "msg": "success",
  "data": {
    "id": "b2ffe8fd6c7bae770e3e87da2cd65f73",
    "courseName": "设计模式之禅",
    "grade": 2,
    "lessonsCount": 30,
    "classify": 1
  }
}

list接口调用测试

//http://localhost:8080/course/list
{
  "code": 200,
  "msg": "success",
  "data": {
    "records": [
      {
        "id": "67ab9cea401ab6d74d0cfb1e289d20ce",
        "courseName": "计算机科学与技术",
        "grade": 1,
        "lessonsCount": 20,
        "classify": 1
      },
      {
        "id": "992f20aed79322e5bb33bd9c21fd77e4",
        "courseName": "架构之美",
        "grade": 1,
        "lessonsCount": 20,
        "classify": 1
      },
      {
        "id": "a397b80c6d1c7c9d25b6919066c3e97e",
        "courseName": "Docker技术实战",
        "grade": 2,
        "lessonsCount": 30,
        "classify": 1
      },
      {
        "id": "ae7fe9d9068fbbaa0eb7dbe4150d009a",
        "courseName": "java从入门到放弃",
        "grade": 1,
        "lessonsCount": 20,
        "classify": 1
      },
      {
        "id": "b2ffe8fd6c7bae770e3e87da2cd65f73",
        "courseName": "设计模式之禅",
        "grade": 2,
        "lessonsCount": 30,
        "classify": 1
      },
      {
        "id": "e0bce26b0a94f27a0c0b154f8cc53e20",
        "courseName": "深入浅出微服务",
        "grade": 2,
        "lessonsCount": 30,
        "classify": 2
      }
    ],
    "total": 6,
    "size": 10,
    "current": 1,
    "orders": [
      
    ],
    "searchCount": true,
    "pages": 1
  }
}

此时依然可以使用student/list接口

或许在分页这块,sql中mybatis-plus做的更简单,是对于表进行了 select count(1) from table_name where conditions 但是如果对分页要求比较高的,还是使用page-helper插件; 老版本的page-helper插件是把你要查询的语句,用一个select count包起来查询一遍的, 新版本这里没有做测试,不知是不是优化了

// http://localhost:8080/student/list
{
  "code": 200,
  "msg": "请求成功",
  "data": {
    "total": 1,
    "list": [
      {
        "id": "1",
        "name": "Calvin",
        "gender": 1,
        "enterTime": "2009-09-01T00:07:15.000+0000",
        "age": 26
      }
    ],
    "pageNum": 1,
    "pageSize": 10,
    "size": 1,
    "startRow": 1,
    "endRow": 1,
    "pages": 1,
    "prePage": 0,
    "nextPage": 0,
    "isFirstPage": true,
    "isLastPage": true,
    "hasPreviousPage": false,
    "hasNextPage": false,
    "navigatePages": 8,
    "navigatepageNums": [
      1
    ],
    "navigateFirstPage": 1,
    "navigateLastPage": 1
  }
}

更多关于Mybatis-plus的详情,关注https://mp.baomidou.com 官网网站,并且官网对mybatis-plus和springboot、springmvc的集成都有很多解释


4:控制事务

原子性(Atomicity) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 比如发生删除动作,主表信息被删除,详情表一定也要删除,如果主表或者详情表删除发生失败,那么主表和详情表的两条数据应该都没有被删除。
一致性(Consistency) 事务前后数据的完整性必须保持一致。 甲同学给乙同学转账,甲同学账户扣款200元,乙同学账户应当增加200元,否则一致性出现问题。
隔离性(Isolation) 事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。管理员A给一个账户增加1000个积分的同时管理员B给这个账户增加500个积分,那么最后这个账户应当被增加1500积分。
持久性(Durability) 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。 即使机器坏了,或者断电,宕机,当数据库恢复或者重新开机后,被修改成功记录的一定是修改后的数据,没有修改成功的,也一定要保证是之前没有被修改的数据。

在现在主流的数据库中,持久性基本可以被保证,所以编码过程中只需要关注原子性、一致性、隔离性,原子性,此处不做这种深入的研究,只演示mybatis框架中如何进行事务控制

TransactionController.java

/**
 * 事务控制演示
 * @author Calvin
 * @date 2019/07/29
 */
@RestController
@RequestMapping("/transaction")
public class TransactionController {

    @Autowired
    private TransActionService transactionService;

    /**
     * 演示自动控制事务
     * @return
     */
    @GetMapping("/auto")
    public ResultEntity autoTransaction(){
        transactionService.showTransaction();
        ResultEntity resultEntity = new ResultEntity();
        resultEntity.setCode(200);
        resultEntity.setMsg("success");
        return resultEntity;
    }

}

TransactionServiceImpl.java

/**
 * @author Calvin
 * @date 2019/07/29
 */
@Service
public class TransactionServiceImpl implements TransActionService {


    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private CourseMapper courseMapper;


	/**
	 * 简单展示事务控制,此处可以设置事务隔离级别,事务的回滚触发等等
	 * @see org.springframework.transaction.annotation.Transactional
	 * @see com.geek.calvin.controller.TransactionController
	 */
    @Override
	@Transactional(rollbackFor=Exception.class)
	public void showTransaction() {

        //随便拿一个学生,设置年龄
        List<Student> students = studentMapper.listAll();
        Student student = students.get(0);
        student.setAge(100);
        studentMapper.updateById(student);
        //制造异常,除数不能为0
        System.out.println(1/0);

        Course course = new Course();
        course.setCourseName("Effective Java Ⅱ");
        course.setClassify(2);
        course.setGrade(3);
        course.setLessonsCount(20);
        courseMapper.insert(course);

    }
}

小结

1:springboot集成mybatis
2:springboot+mybatis周边配置及常用插件

存在问题

1:没有深入探究mybatis事务
2:多数据源配置及分库分表插件未探究
3:没有架如mybatis缓存等设置

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部