springboot下mybatis的应用
作为一个前端开发者和伪后台开发者前后端的技术都需要了解一些。早些时候做Java开发的时候主要用到的是SSH框架,对于SSM了解不是很多。之前的博客中写了一些关于springboot的总结,深深地被springboot的简化配置吸引,最近出差有时间继续完善一下springboot系列。之前提到了springboot的helloworld的创建、三层架构的搭建、spring security的应用。上面三个掌握了之后就可以搭建一个简单springboot开发框架,用来练手是很不错的。现在我们进行逐个细化,这里的细化不是说教技术点,而是根据开发需求来,用到什么说什么。先从ORM框架Mybatis说起吧。
1. spring-boot环境下创建一个mybatis项目
很简单,打开dos,使用spring init命令创建一个springboot项目。如下:
$ spring init -g=com.briup.apps -a=app03 -d=mybatis,mysql app03 $ cd app03 $ mvn install
上面意思表示maven项目的组ID为com.briup.apps ,artifactID为app03,依赖mybatis和mysql驱动,项目名称为app03。项目创建完成后进入到app03目录下,为app03安装所需要的依赖
这样,就创建了一个app03的项目,然后通过eclipse打开该项目即可。
这时候如果直接运行项目,将会报错,提示数据源配置出错。这个问题在搭建三层架构的章节中提到过,解决方案是在application.properties文件中添加如下配置
#数据源相关参数的配置 spring.datasource.driverClassName=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://127.0.0.1:3306/poll2.0 spring.datasource.username=root spring.datasource.password=root #mybatis映射地址的位置 mybatis.mapper-locations= classpath:/mapper/**/*.xml
2. 搭建项目结构
2.1 数据建模
在常见的数据模型中,一对多关系和多对多关系是最为常用的关系,所以我这里测试采用的案例是大学《数据库系统概论》 教程中的案例,班级,学生,课程之间的关系,具体如下图:
根据如上ER图,建表
-- ---------------------------- -- Table structure for tbl_clazz -- ---------------------------- DROP TABLE IF EXISTS `tbl_clazz`; CREATE TABLE `tbl_clazz` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for tbl_course -- ---------------------------- DROP TABLE IF EXISTS `tbl_course`; CREATE TABLE `tbl_course` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `credit` int(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for tbl_sc -- ---------------------------- DROP TABLE IF EXISTS `tbl_sc`; CREATE TABLE `tbl_sc` ( `id` int(11) NOT NULL AUTO_INCREMENT, `student_id` int(11) DEFAULT NULL, `course_id` int(11) DEFAULT NULL, `score` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `student_id` (`student_id`), KEY `course_id` (`course_id`), CONSTRAINT `tbl_sc_ibfk_1` FOREIGN KEY (`student_id`) REFERENCES `tbl_student` (`id`) ON DELETE SET NULL, CONSTRAINT `tbl_sc_ibfk_2` FOREIGN KEY (`course_id`) REFERENCES `tbl_course` (`id`) ON DELETE SET NULL ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for tbl_student -- ---------------------------- DROP TABLE IF EXISTS `tbl_student`; CREATE TABLE `tbl_student` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `gender` varchar(255) DEFAULT NULL, `clazz_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `clazz_id` (`clazz_id`), CONSTRAINT `tbl_student_ibfk_1` FOREIGN KEY (`clazz_id`) REFERENCES `tbl_clazz` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
2.2 ORM映射
搭建项目结构,并且根据表结构创建类
因为是对mybatis的学习,所以这里我重点写了bean和dao,也就是对于数据访问层的剖析测试。
//班级类 public class Clazz { private Long id; private String name; //对应的setters,getters } //课程类 public class Course { private Long id; private String name; private Integer credit; //对应的setters,getters } //学生类 public class Student { private Long id; private String name; private String gender; private Long clazz_id; //对应的setters,getters } //学生选课类 public class StudentCourse { private Long id; private Long student_id; private Long course_id; //对应的setters,getters }
2.3 dao层代码编写 、mybatis的应用
在mybatis中,我们只需要提供映射接口和映射文件即可,而事务的处理和SqlSession的处理我们交给springBoot即可,这种方式可以使得我们很快的构建数据访问层的代码。
2.3.1 简单单表的增删改查操作
package com.briup.apps.app03.dao; import java.util.List; import com.briup.apps.app03.bean.Student; public interface StudentMapper { List<Student> findAll(); Student findById(long id); void save(Student student); void update(Student student); void deleteById(long id); }
查看mybatis官方网站可以知道,实现增删改查需要在xml中提供对应的select,insert,update,delete元素,id名称与映射接口中定义的方法名称相同,resultType属性表示返回值类型,parameterType表示参数类型。还有更多其他属性的解释可以参照官方文档
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.briup.apps.app03.dao.StudentMapper"> <!-- 查询所有学生 --> <select id="findAll" resultType="com.briup.apps.app03.bean.Student"> select * from tbl_student </select> <!-- 通过ID查询学生信息 --> <select id="findById" resultType="com.briup.apps.app03.bean.Student"> select * from tbl_student where id = #{id} </select> <!-- 保存学生信息 --> <insert id="save" parameterType="com.briup.apps.app03.bean.Student"> insert into tbl_student values(null,#{name},#{gender},#{clazz_id}) </insert> <!-- 修改学生信息 --> <update id="update" parameterType="com.briup.apps.app03.bean.Student"> update tbl_student set name = #{name}, gender = #{gender} ,clazz_id = #{clazz_id} where id = #{id} </update> <!-- 通过id删除学生信息 --> <delete id="deleteById" parameterType="long"> delete from tbl_student where id = #{id} </delete> </mapper>
2.3.2 简单多对一的查询
很多时候单表的增删改查并不能满足项目需求,比如,在查询学生信息的时候我们不仅仅需要拿到clazz_id这个外键,我们还需要知道该学生所在的班级名称,这样就需要用到多表查询。
一个学生对应一个班级,为了能够映射查询结构,这里我们需要先重新定义一个POJO类,来表示这种多对一关系。
如下:
package com.briup.apps.app03.bean.vm; import com.briup.apps.app03.bean.Clazz; import com.briup.apps.app03.bean.Student; public class StudentVM { //学生信息 private Student student; //学生对应的班级信息 private Clazz clazz; //setters getters }
package com.briup.apps.app03.dao.extend; import java.util.List; import com.briup.apps.app03.bean.vm.StudentVM; public interface StudentVMMapper { List<StudentVM> findAll(); }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.briup.apps.app03.dao.extend.StudentVMMapper"> <!-- 查询所有学生 --> <select id="findAll" resultMap="studentVMResultType"> select s.id as s_id, s.name as s_name, s.gender, c.id as c_id, c.name as c_name from tbl_student as s ,tbl_clazz as c where s.clazz_id = c.id </select> <resultMap type="com.briup.apps.app03.bean.vm.StudentVM" id="studentVMResultType"> <association property="student" javaType="com.briup.apps.app03.bean.Student"> <id column="s_id" property="id"/> <result column="s_name" property="name"/> <result column="gender" property="gender"/> </association> <association property="clazz" javaType="com.briup.apps.app03.bean.Clazz"> <id column="c_id" property="id"/> <result column="c_name" property="name"/> </association> </resultMap> </mapper>
这种情况下映射文件还有另外一种写法:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.briup.apps.app03.dao.extend.StudentVMMapper"> <!-- 查询所有学生 --> <select id="findAll" resultMap="studentVMResultType"> select * from tbl_student </select> <resultMap type="com.briup.apps.app03.bean.vm.StudentVM" id="studentVMResultType"> <association property="student" column="id" javaType="com.briup.apps.app03.bean.Student" select="com.briup.apps.app03.dao.StudentMapper.findById" ></association> <association property="clazz" column="clazz_id" javaType="com.briup.apps.app03.bean.Clazz" select="com.briup.apps.app03.dao.ClazzMapper.findById"></association> </resultMap> </mapper>
2.3.3 简单一对多的查询
在开发过程中我们还经常遇到一对多的查询,比如,查询题目的时候需要查询该题目下的所有选项的信息,查询班级的时候级联查询出该班级下所有的学生信息。
pojo类
package com.briup.apps.app03.bean.vm; import java.util.List; import com.briup.apps.app03.bean.Student; public class ClazzVM { private Long id; private String name; private List<Student> students; //setters getters }
映射接口
package com.briup.apps.app03.dao.extend; import com.briup.apps.app03.bean.vm.ClazzVM; public interface ClazzVMMapper { ClazzVM findById(long id); }
映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.briup.apps.app03.dao.extend.ClazzVMMapper"> <!-- 查询所有班级以及该班级下所有学生 --> <select id="findById" resultMap="clazzVMResultType"> select * from tbl_clazz where id = #{id} </select> <resultMap type="com.briup.apps.app03.bean.vm.ClazzVM" id="clazzVMResultType"> <id column="id" property="id"/> <result column="name" property="name"/> <collection property="students" column="id" javaType="ArrayList" ofType="com.briup.apps.app03.bean.Student" select="com.briup.apps.app03.dao.StudentMapper.findByClazzId"></collection> </resultMap> </mapper>
3. 测试
这里的测试我使用的是springboot的单元测试功能,如下
package com.briup.apps.app03.test; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.briup.apps.app03.bean.Student; import com.briup.apps.app03.bean.vm.StudentVM; import com.briup.apps.app03.dao.StudentMapper; import com.briup.apps.app03.dao.extend.StudentVMMapper; import net.minidev.json.JSONArray; @RunWith(SpringRunner.class) @SpringBootTest public class StudentMapperTest { @Autowired private StudentMapper studentMapper; @Autowired private StudentVMMapper studentVMMapper; //@Test public void save(){ Student student = new Student(); student.setName("张三"); student.setGender("男"); studentMapper.save(student); System.out.println("--保存成功"); } //@Test public void update(){ Student student = new Student(); student.setId(8L); student.setName("张三三"); student.setGender("男"); studentMapper.update(student); System.out.println("--修改成功"); } //@Test public void delete(){ studentMapper.deleteById(8L); System.out.println("--删除成功"); } @Test public void test1(){ List<Student> list = studentMapper.findAll(); String json = JSONArray.toJSONString(list); System.out.println("student:"+json); } @Test public void test2(){ List<StudentVM> list = studentVMMapper.findAll(); String json = JSONArray.toJSONString(list); System.out.println("studentVM:"+json); } }
4. 代码结构
为了使得代码拓展性强一些,我将单表操作的类和关联操作的类分开存放。为了实现敏捷开发,我们可以使用mybatis generator来实现单表操作的pojo类,映射文件,映射接口的自动产生。