2.1 MyBatis框架简介

JDBC是Java程序实现数据访问的基础,它提供了一套数据库操作API。JDBC连接数据库比较繁琐,并且需要编写大量Java程序代码操作数据库。

  • 频繁地创建、释放数据库连接会造成系统资源浪费,从而影响系统性能。
  • 代码中的SQL语句写在Java代码中(硬编码),而SQL变化可能性比较大,会造成代码频繁修改,不易于维护。

1、什么是Mybatis框架
MyBatis 是支持定制化 SQL、存储过程以及高级映射(ORM)的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生SQL使用简单的 XML 配置或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
2、MyBatis的优缺点
(1)优点

  • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要2个jar文件+配置几个sql映射文件,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
  • 灵活:MyBatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
  • 解除sql与程序代码的耦合:将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
  • 提供映射标签,支持对象与数据库的orm字段关系映射,支持对象关系的维护。
  • 支持编写动态sql。

(2)缺点

  • 编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。
  • SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。

    2.2 Spring Boot整合MyBatis环境搭建

    在开发中,Spring Boot在简化项目开发和实现自动化配置的基础上,对数据库的访问操作提供了非常好的整合支持。

    2.2.1 引入依赖启动器

    1. <dependency>
    2. <groupId>org.mybatis.spring.boot</groupId>
    3. <artifactId>mybatis-spring-boot-starter</artifactId>
    4. <version>2.2.2</version>
    5. </dependency>
    6. <dependency>
    7. <groupId>mysql</groupId>
    8. <artifactId>mysql-connector-java</artifactId>
    9. <scope>runtime</scope>
    10. </dependency>
    11. <dependency>
    12. <groupId>com.alibaba</groupId>
    13. <artifactId>druid-spring-boot-starter</artifactId>
    14. <version>1.1.10</version>
    15. </dependency>
    16. <dependency>
    17. <groupId>org.projectlombok</groupId>
    18. <artifactId>lombok</artifactId>
    19. <optional>true</optional>
    20. </dependency>

    2.2.2核心配置

    1. spring:
    2. datasource:
    3. type: com.alibaba.druid.pool.DruidDataSource
    4. username: root
    5. password: 123456
    6. url: jdbc:mysql://localhost:3306/hcy?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    7. mvc:
    8. pathmatch:
    9. matching-strategy: ant_path_matcher
    10. server:
    11. port: 8080
    12. compression:
    13. enabled: true
    14. mybatis:
    15. configuration:
    16. log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    17. #标注待解析的mapper的xml文件位置
    18. mapper-locations: classpath:mapper/*.xml
    19. #配置映射类所在的包名
    20. type-aliases-package: com.example.demo.entity
    映射文件:MyBatis消除了JDBC代码,用XML标签来配置SQL,即SQL语句配置在XML文件中,也就是映射文件。MyBatis需要加载这些映射文件,所以指定映射文件的保存位置 ,否则会报异常(方法绑定声明无效)。
    本项目映射文件保存在src/resources/mapper目录下。
    image.png

    2.2.3 创建Mapper接口并扫描Mapper接口

    1.MyBatis中的mapper接口相当于以前的dao。但是区别在于,mapper仅仅是接口,我们不需要提供实现类
    1. public interface EmployeeMapper {
    2. List<Employee> selectAll();
    3. }
  1. MyBatis消除了JDBC代码,SQL语句写在映射文件中,即Dao层接口不用写实现类,可以
  • 在接口类上添加了@Mapper,在编译之后会生成相应的接口实现类。这样每一个Dao层接口都要加上@Mapper注解。
  • 在SpringBoot启动类中加 @MapperScan(“dao包名”) 注解,这样会比较方便,不需要对每个Mapper都添加@Mapper注解。

建议使用第2种方法:

  1. @SpringBootApplication
  2. @MapperScan(basePackages = "com.example.demo.mapper")
  3. public class DemoApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(DemoApplication.class, args);
  6. }
  7. }

2.2.4 MyBatis映射文件

MyBatis真正强大之处在于可以配置SQL映射语句。相比于JDBC,MyBatis专注于SQL语句。

  • 映射文件的命名规则
    • 表所对应的实体类的类名+Mapper.xml
    • 例如:表t_user,映射的实体类为User,所对应的映射文件为UserMapper.xml
    • 因此一个映射文件对应一个实体类,对应一张表的操作
    • MyBatis映射文件用于编写SQL,访问以及操作表中的数据
    • MyBatis映射文件存放的位置是src/main/resources/mappers目录下
  • MyBatis中可以面向接口操作数据,要保证两个一致
    • mapper接口的全类名和映射文件的命名空间(namespace)保持一致
    • mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致

映射文件结构:
第二章 SpringBoot Mybatis整合 - 图2

2.2.5 CRUD

  1. 查询

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    3. <mapper namespace="com.example.demo.mapper.EmployeeMapper" >
    4. <select id="selectAll" resultType="com.example.demo.entity.Employee" >
    5. select
    6. name,
    7. gender,
    8. birthday,
    9. idCard
    10. from employee
    11. </select>
    12. </mapper>
  2. 插入

    1. <insert id="insertEmp" parameterType="com.example.demo.entity.Employee">
    2. insert into employee (name,gender,birthday,idCard)
    3. values (#{name},#{gender},#{birthday},#{idCard})
    4. </insert>

    ```java /**

  • 添加 */ @Test void add() { Employee e = new Employee(); e.setName(“111”); e.setGender(“男”); e.setBirthday(new Date()); e.setIdCard(“1111111”); employeeMapper.insertEmp(e); } ```
  1. 更新

    1. <update id="updateEmpByPrimaryKey" parameterType="com.example.demo.entity.Employee">
    2. update employee
    3. set name = #{name}
    4. where id = #{id}
    5. </update>
    1. /**
    2. * 更新
    3. */
    4. @Test
    5. void update() {
    6. Employee e = new Employee();
    7. e.setId(1942);
    8. e.setName("11111");
    9. employeeMapper.updateEmpByPrimaryKey(e);
    10. }
  2. 删除

    1. <delete id="delEmp" parameterType="java.lang.Integer">
    2. delete from employee where id = #{id}
    3. </delete>

    ```java /**

  • 删除 */ @Test void delete() { employeeMapper.delEmp(1942); }
    1. <a name="ZxeCy"></a>
    2. ## 2.2.6 高级(动态查询,动态插入,批量插入,动态更新,批量删除,模糊查询)
    3. **1.动态查询**
    4. ```xml
    5. <select id="getTotal" resultType="java.lang.Long">
    6. select count(*) from employee e
    7. <where>
    8. <if test="emp!=null">
    9. <if test="emp.name !=null and emp.name!=''">
    10. and e.name like concat('%',#{emp.name},'%')
    11. </if>
    12. <if test="emp.departmentId !=null">
    13. and e.departmentId =#{emp.departmentId}
    14. </if>
    15. </if>
    16. <if test="beginDateScope !=null">
    17. and e.beginDate between #{beginDateScope[0]} and #{beginDateScope[1]}
    18. </if>
    19. </where>
    20. </select>
    1. Long getTotal(@Param("emp") Employee employee, @Param("beginDateScope") String[] beginDateScope);
    1. /**
    2. * 动态查询
    3. */
    4. @Test
    5. void selectby() {
    6. Employee e = new Employee();
    7. e.setName("存亮");
    8. e.setDepartmentId(92);
    9. String[] s = {"2018-01-01","2018-02-01"};
    10. System.out.println(employeeMapper.getTotal(e,s));
    11. }
  1. 动态插入

    1. <insert id="insertCardWithId" parameterType="com.example.demo.entity.Employee">
    2. insert into employee
    3. <trim prefix="(" suffix=")" suffixOverrides=",">
    4. name,
    5. gender,
    6. <if test="null != birthday and ''!= birthday">birthday,</if>
    7. idCard
    8. </trim>
    9. VALUES
    10. <trim prefix="(" suffix=")" suffixOverrides=",">
    11. #{name},
    12. #{gender},
    13. <if test="null != birthday and ''!=birthday">
    14. #{birthday},
    15. </if>
    16. #{idCard}
    17. </trim>
    18. </insert>
    1. int insertCardWithId(Employee e);

    ```xml /**

    • 动态插入 */ @Test void insertCardWithId() { Employee e = new Employee(); e.setName(“111”); e.setGender(“男”); // e.setBirthday(new Date()); e.setIdCard(“1111111”);

      employeeMapper.insertCardWithId(e); }

  1. 3. **批量插入**
  2. ```java
  3. <insert id="insertBatch" parameterType="java.util.List">
  4. INSERT INTO employee
  5. (name, gender,birthday,idCard)
  6. VALUES
  7. <foreach collection ="empList" item="emp" separator =",">
  8. (#{emp.name}, #{emp.gender}, #{emp.birthday},#{emp.idCard})
  9. </foreach >
  10. </insert>
  1. int insertBatch(@Param("empList") List<Employee> empList);
  1. /**
  2. * 批量插入
  3. */
  4. @Test
  5. void insertBatchTest() {
  6. Employee e = new Employee();
  7. e.setName("111");
  8. e.setGender("男");
  9. e.setBirthday(new Date());
  10. e.setIdCard("1111111");
  11. Employee e1 = new Employee();
  12. e1.setName("111");
  13. e1.setGender("男");
  14. e1.setBirthday(new Date());
  15. e1.setIdCard("1111111");
  16. List<Employee> emplist = new ArrayList<>();
  17. emplist.add(e);
  18. emplist.add(e1);
  19. employeeMapper.insertBatch(emplist);
  20. }

4.动态更新

  1. <update id="updateByPrimaryKeySelective" parameterType="com.example.demo.entity.Employee">
  2. update employee
  3. <set>
  4. <if test="name != null">
  5. name = #{name,jdbcType=VARCHAR},
  6. </if>
  7. <if test="gender != null">
  8. gender = #{gender,jdbcType=CHAR},
  9. </if>
  10. <if test="birthday != null">
  11. birthday = #{birthday,jdbcType=DATE},
  12. </if>
  13. <if test="idCard != null">
  14. idCard = #{idCard,jdbcType=CHAR}
  15. </if>
  16. </set>
  17. where id = #{id,jdbcType=INTEGER}
  18. </update>
  1. int updateByPrimaryKeySelective(Employee e);
  1. /**
  2. * 动态插入
  3. */
  4. @Test
  5. void updateByPrimaryKeySelective() {
  6. Employee e = new Employee();
  7. e.setId(1946);
  8. e.setName("111");
  9. e.setGender("女");
  10. e.setBirthday(new Date());
  11. e.setIdCard("1111111");
  12. employeeMapper.updateByPrimaryKeySelective(e);
  13. }
  1. 批量删除

    1. <delete id="deleteByIds">
    2. DELETE FROM employee WHERE id IN (
    3. <foreach collection="array" item="id" separator=",">
    4. #{id}
    5. </foreach>
    6. )
    7. </delete>
    1. int deleteByIds(int[] ids);
    1. /**
    2. * 批量删除
    3. */
    4. @Test
    5. void deleteByIds() {
    6. int[] ids = {1946,1947};
    7. employeeMapper.deleteByIds(ids);
    8. }

    2.2.7 关联查询
    查询部门名称
    image.png

    1. <select id="selectDeptName" resultType="com.example.demo.entity.Employee">
    2. select
    3. e.name,
    4. e.gender,
    5. e.birthday,
    6. e.idCard,
    7. d.name as departName
    8. from employee e left join department d on e.departmentId = d.id limit 10
    9. </select>
    1. List<Employee> selectDeptName();
    1. /**
    2. * 关联查询
    3. */
    4. @Test
    5. void selectDeptName() {
    6. List<Employee> emplist = employeeMapper.selectDeptName();
    7. for(Employee e : emplist){
    8. System.out.println(e.getDepartName());
    9. }
    10. }

    2.2.7 分页查询

  2. PageHelper

pom.xml

  1. <dependency>
  2. <groupId>com.github.pagehelper</groupId>
  3. <artifactId>pagehelper-spring-boot-starter</artifactId>
  4. <version>1.4.1</version>
  5. </dependency>
  1. 测试 ```java /**
  • 分页查询 */ @Test void selectPage() { // 开启分页插件,放在查询语句上面 帮助生成分页语句(核心程序) int pageNum = 1; int pageSize = 20; PageHelper.startPage(pageNum, pageSize); //底层实现原理采用改写语句 将下面的方法中的sql语句获取到然后做个拼接 limit List empList = employeeMapper.selectAll(); // 封装分页之后的数据 返回给客户端展示 PageInfo做了一些封装 作为一个类 PageInfo pageInfo = new PageInfo(empList); //所有分页属性都可以冲pageInfo拿到; pageInfo.getList().forEach(System.out::println); } ```