访问数据库-Spring整合MyBatis框架

JPA和Hiberbate我个人认为就不需要看了,没什么价值

1. MyBatis简介

MyBatis的官方定义是:MyBatis是支持定制化SQL,存储过程以及高级映射的优秀的持久层框架。MyBatis避免了几乎所有的JDBC代码和手动设置参数获取结果集。MyBatis可以对配置和原生Map使用简单的XML或者注解。将接口和Java的POJO映射成映射成数据库中的记录。

从官方的定义可以看出,MYBatis是一种基于SQL到POJO的模型,他需要我们提供SQL,映射关系(XML或者注解,目前已XML为主)和POJO。但是对于SQL与POJO的映射关系,它提供了自动映射与驼峰映射登,使得开发者的开发工作大大减少,由于没有屏蔽SQL,这对于追求高响应和性能的互联网系统是十分重要的,因此我们可以尽可能的通过SQL去优化性能,也可以做少量改变以适应灵活的互联网应用。与此同时,他还是支持动态SQL,以适应需求的变化。这样一个灵动的,高性能的持久层框架就成先在我们面前,这很符合当前互联网的需要。鉴于当前MyBatis的流行且渐渐成为市场的主流持久框架,本书的数据库应用就基于MyBatis进行讲述,包括后面的数据库事务和相关应用也是如此。

MyBatis的配置文件包含两大部分:一是基础配置文件,一个是映射文件,在MyBatis中也可以使用注解来实现映射,不过由于功能和可读性的限制,在实际企业中使用的比较少,因此本书不介绍是使用注解配置SQL的方式。严格来说Spring项目本身是不支持MyBatis的,那是因为Spring3在即将发布新版本时,MyBatis3还没有发布正式版本,所以Spring的项目都没有考虑MYBatis的整合。但是MYBatis社区为了整合Spring自己开发了相应的安装包,因此在Spring Boot中我们可以MYBatis社区提供的starter.例如在Maven中加入依赖的包。

        <!--引入MyBatis的依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>

从包名可以看出,mybatis-spring-boot-starter是由MyBatis社区开发的。,但是无论如何都要先了解MyBatis的配置和基本内容。

2. MyBatis的配置

MyBatis是一个基于SqlSessionFactory构建的框架啊,对于SqlSessionFactory而言,他的作用是生成SqlSession接口对象,这个接口对象是MyBatis操作的核心,而在MyBatis-Spring的结合中甚至可以“擦除”这个对象,这样做的意义是重大的,因为SqlSession是一个功能性的代码,擦除他之后,就只是剩下业务性的代码,这样就可以使得代码更具有可读性。因为SqlSessionFactory的作用是单一的恶,只是为了创建核心接口SQlSession,所以在MyBatis应用的生命周期中理应只存在一个SqlSessionFactory对象,并且往往会采取单例的模式,而构建SqlSessionFactory是通过配置类(Configuration)来完成的,因此对于mybatis-spring-boot-starter,他会给予我们在配置文件(application.propertities)进行Configuraton配置的相关内容。下面先来看看Configuration可以配置那些内容。如图所示:

在这里插入图片描述
从图中可以了解MyBatis配置的内容。

  • properties(属性):属性文件在实际应用中一般采用Spring进行配置,而不是MyBatis,所以这里不再介绍他的使用。
  • settings(设置):他的配置将改变MyBatis的底层行为,可以配置映射规则,如自动映射和驼峰映射,执行器(Executor)类型,缓存等内容,比较复杂,具体配置项可参考Mybatis官方网站。
  • typeAliases(类型别名):因为使用类全限定名会比较长,所以MyBatis会对常用的类提供默认的别名,此外还允许我们通过typeAliases配置自定义的别名。
  • typeHandlers(类型处理器):这是MYBatis的重要配置之一,在MyBatis读取和写入数据库的过程中对于不同类型的数据(对于Java是JavaTYpe,对于数据库是JdbcType)进行自定义转换,在大部分情况下我们不需要使用自定义的typeHandlers,因为在MyBatis自身就已经定义了较多的typehandler,Mybatis会自动识别javaType和jdbcType,从而实现各种类型的转换,一般来说,typeHandler的使用集中在美剧类型之上。
  • objectFactory(对象工厂):这是一个在MyBatis生成返回的POJO时回调用的工厂类。一般我们使用MyBatis提供的默认工厂类就已经足够了,而不需要任何配置,所以不讨论他。
  • plugins(配件):有时候也成为拦截器,是MYBatis最强大也是最危险的组件,它通过动态代理和责任链模式来完成,可以修改MyBatis底层的实现功能,掌握他需要较多的MyBatis知识,额可以参考相关的书籍和资料。
  • environments(数据库环境):可以配置数据库链接内容和事务。一般而言,这些交由Spring 托管,不用讨论他
  • databaseIdProvider(数据库厂商标识):允许MyBatis配置多类型数据库支持,不常用,不再讨论他的使用
  • mappers(映射器):是MyBatis最核心的组件,它提供SQL和POJO映射关系,这是MyBatis开发的核心
    一下我们举个简单的例子。为了使用MyBatis的别名,我们先改写代码:
package cn.hctech2006.boot.bootmybatis.bean;
/**
 * 在用户类指定MyBatis别名
 */

import org.apache.ibatis.type.Alias;

import java.io.Serializable;
import java.util.Date;
//Mybatis指定别名
@Alias(value = "sysRole")
public class SysRole implements Serializable {
    private Long id;

    private String name;

    private String remark;

    private String createBy;

    private Date createTime;

    private Date lastUpdateTime;

    private String lastUpdateBy;

    private Byte delFlag;

    private static final long serialVersionUID = 1L;

// setter and getter
}

这里加入了一个注解@Alias,并且指定他的别名为sysRole。为了使这个POJO能够与数据库的数据对应,还需要提供一个映射文件,代码如下:

<?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="cn.hctech2006.boot.bootmybatis.mapper.SysRoleMapper">
  <resultMap id="BaseResultMap" type="sysRole">
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="name" jdbcType="VARCHAR" property="name" />
    <result column="remark" jdbcType="VARCHAR" property="remark" />
    <result column="create_by" jdbcType="VARCHAR" property="createBy" />
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
    <result column="last_update_time" jdbcType="TIMESTAMP" property="lastUpdateTime" />
    <result column="last_update_by" jdbcType="VARCHAR" property="lastUpdateBy" />
    <result column="del_flag" jdbcType="TINYINT" property="delFlag" />
  </resultMap>
  
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    select id, name, remark, create_by, create_time, last_update_time, last_update_by, 
    del_flag
    from sys_role
    where id = #{id,jdbcType=BIGINT}
  </select>

</mapper>

这里先看到元素的namespace属性,他指定一个接口,后文会提供这个接口:接着定义元素,它代表一个查询语句,而id属性代指这条SQL,parameterType属性配置为Long,表示是一个长整型参数,resultMap指定返回映射类型,这里定义一个ResultMap也就是结果映射。他的映射类型使用sysRole,这是一个别名,因为上面我们已经有了指代,所以才可以这样使用,也可以使用全限定名(cn.hctech2006.boot.bootmybatis.bean.SysRole;在接着是SQL语句,这里ResultMap里面的列名和POJO名是保持一致的。请注意,数据库的字段名为create_by,那么POJO的属性名为createBy。在默认情况下,MyBatis会启动自动映射,将SQL中的列映射到POJO上,有时你也可以启用驼峰映射,这样就可以不用别名了。
---因为配置了代码生成器,以上看看就行-----
为了启用这个映射,我们还需要一个接口,注意,仅仅是一个接口,并不需要任何实现类,他就是元素namespace属性定义的cn.hctech2006.boot.bootmybatis.mapper.SysRoleMapper,代码如下:

package cn.hctech2006.boot.bootmybatis.mapper;

import cn.hctech2006.boot.bootmybatis.bean.SysRole;
import java.util.List;
@Repository
public interface SysRoleMapper {

    SysRole selectByPrimaryKey(Long id);

}

注意,这里加了一个注解@Repository.这个注解在将来讨论扫描加载MYBatis接口Bean时是十分有用的,而他的方法selectByPrimaryKey和映射文件中定义的查询SQL的id是保持一致的,参数也是如此,这样就可以定义一个查询方法了。好了,有了上面的内容,我们开始配置MyBatis。这里需要对映射文件,POJO别名和typeHandler进行配置,这样就可以在配置文件application.yml中加入一下代码:

mybatis:
  #MyBatis映射文件通配
  mapper-locations: classpath:/mappers/*Mapper.xml
  #配置别名扫描包,和注解@Alias连用
  type-aliases-package: cn.hctech2006.boot.boot-mybatis.bean
  #配置typeHandler扫描包
  #type-handlers-package:
#日志配置
logging:
  level: debug

这里配置了我们的映射文件,别名文件和typeHandler,这样就可以让MyBatis扫描他们了。日志配置为DEBUG级别,是为了更好的观察测试结果。其他的东西不需要配置,因为mybatis-spring-boot-starter对mybatis启动做了默认的配置,只需要修改我们需要的东西就可以。这是Spring Boot的特性,让你在最少的配置下完成你想要的功能,这样对于MyBatis而言,就结束了,下面我们讨论如何在Spring Boot整合他。

3. Spring Boot整合MyBatis

在大部分情况下应该擦除SqlSession接口的使用而直接获取Mapper接口,这样更加集中于业务的开发,而不是MyBatis功能性的开发。但是上面我们看到Mapper是一个接口,是不可以使用new为其生成对象实例的。为了方便我们使用,MyBatis社区在于Sprting整合的包中提供了两个类,他们是MapperFactoryBean和MapperScannerConfiguration。他们是有区别的,MapperFactoryBean是针对一个接口配置,而MapperSacnnerConfigurer则是扫描装配,也就是提供扫描装配MyBatis的接口到Spring IOC的容器中。实际上,MyBatis还提供了注解@MapperScan,也就是能够将MyBatis所需的接口扫描装配到IOC容器。相对于MapperFactoryBean以及MapperScannerConfidurer这样需要代码开发的方式,@MapperScan显得更加方便,所以在大部分的情况下,建议读者使用他。下面分别对MapperFactoryBean,MapperScannerConfigurer和@MapperScan的使用给予说明。
先用MapperFactoryBean配置SysRoleMapper接口。我们在Spring Boot的启动配置文件中加入代码如下:

package cn.hctech2006.boot.bootmybatis;

import cn.hctech2006.boot.bootmybatis.mapper.SysRoleMapper;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class BootMybatisApplication {
    @Autowired
    SqlSessionFactory sqlSessionFactory = null;
    //定义一个MyBatis的Mapper接口
    @Bean
    public MapperFactoryBean<SysRoleMapper> initSysRoleMapperMapper(){
        MapperFactoryBean<SysRoleMapper> bean = new MapperFactoryBean<>();
        bean.setMapperInterface(SysRoleMapper.class);
        bean.setSqlSessionFactory(sqlSessionFactory);
        return bean;
    }

    public static void main(String[] args) {
        SpringApplication.run(BootMybatisApplication.class, args);
    }

}

这里的SqlSessionFactory是Spring Boot自动为我们生成的,可以奉行拿来主义,然后直接使用MapperFactoryBean来定义Mapper接口。下面开发服务层来装配他。代码清单如下,他给出了服务类的接口和实现类:

/**
 * 服务实现类
 */
 @Service
public class SysRoleServiceImpl implements SysRoleService {
    @Autowired
    private SysRoleMapper sysRoleMapper=null;

    @Override
    public int save(SysRole record) {
        return sysRoleMapper.insert(record);
    }

}

因为我们在启动文件中装配了对应的接口,所以可以使用@Autowired进行依赖注入,把这个接口注入应用中。接着就是insert方法,这个代码也挺简单。接下来开发控制器,已完成这个接口的测试,代码如下所示

/**
 * 控制器测试MyBatis接口
 */
@RestController
@RequestMapping("/mybatis")
public class RoleController {
    @Autowired
    private SysRoleService sysRoleService;
    @RequestMapping("/getRole")
    public SysRole getRole(Long id){
        return sysRoleService.findById(id);
    }

}

这样通过注入服务器接口就可以测试控制器的方法。启动Spring Boot的应用,在浏览器地址栏输入http://localhost:8242/mybatis/getRole?id=8,就可以看到如图所示的效果

{"id":8,"name":"user2","remark":"shiyanb","createBy":"ldy","createTime":"2020-03-07T01:09:52.000+0000","lastUpdateTime":"2020-03-08T16:25:54.000+0000","lastUpdateBy":"ldy","delFlag":0}

显然这里我们已经整合了MyBatis,并且成功的打印出了JSON数据集。上面使用MapperFactoryBean装配的仅仅是一个DAO接口,然后将其装配到Spring IOC容器。现实中接口会非常多,如果一个个的定义会比较麻烦,这个时候就可以使用MapperScannerConfigurer类来定义扫描了,它可以配置包和注解(或者接口)类型进行装配,首先把上面关于MapperFactoryBean的代码注释掉,然后在SpringBoot的启动配置文件中加入如下代码。

package cn.hctech2006.boot.bootmybatis;

@SpringBootApplication
public class BootMybatisApplication {

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        //定义扫描器实例
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        //加载SqlSessionFactory。Spring Boot会自动生产,sqlSerssionFactory实例
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
        //定义到扫描的包
        mapperScannerConfigurer.setBasePackage("cn.hctech2006.boot.bootmybatis.mapper");
        //限定使用注解@REpository的接口才可以被扫描
        mapperScannerConfigurer.setAnnotationClass(Repository.class);
        //通过继承某个接口限制扫描一般使用不多
        //mapperScannerConfigurer.setMarkerInterface(......);
        return mapperScannerConfigurer;
    }

    public static void main(String[] args) {
        SpringApplication.run(BootMybatisApplication.class, args);
    }

}

上述代码中使用了MapperScannerConfigurer类定义了扫描的包,这样程序就回去扫描对应的包了,然后还可以使用注解限制,限制被标注为@Respository,这就是为什么mapper中的接口使用这个注解标注的原因,这样也可以防止在扫描中被错误装配。当然也可以使用接口继承的关系限定,不过在现实中使用的不多,所以不在进行探讨。
结果如下,可以正常使用:

{"id":8,"name":"user2","remark":"shiyanb","createBy":"ldy","createTime":"2020-03-07T01:09:52.000+0000","lastUpdateTime":"2020-03-08T16:25:54.000+0000","lastUpdateBy":"ldy","delFlag":0}

但是上述还是需要编写代码,而实际上还有更加简单的方式,那就是注解@MapperScan,例如我们可以删除上述关于MapperFactoryBean和MapperScannerConfigurer的相关代码,单独使用@MapperScan,代码如下:

package cn.hctech2006.boot.bootmybatis;

@SpringBootApplication
//定义MyBatis扫描路径
@MapperScan(
        //指定扫描包
        basePackages = "cn.hctech2006.boot.bootmybatis.mapper",
        //指定SqlSessionFactory
        sqlSessionFactoryRef = "sqlSessionFactory",
        //指定sqlSessionTemplte,将会忽略sqlSessionFactory的配置
        sqlSessionTemplateRef = "sqlSessionTemplate"
        //markerInterface=Class.class限制扫描接口不常用
        //markerInterface =
        //注解限制
        annotationClass = Repository.class
)
public class BootMybatisApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootMybatisApplication.class, args);
    }

}

@MapperScan允许我们通过扫描加载MyBatis的Mapper,如果你的Spring Boot项目中不存在多个SqlSessionFactory(或者SqlSessionTemplate),那么你完全可以不用配置sqlSessionFactoryRef(或者sqlSessionTemplateRef),上述代码关于他们的配置可有可无,但是如果存在多个时,就需要我们指定了,而且有一点是需要注意的:sqlSessionTemplateRef的优先权是大于SqlSessionFactoryRef的,也就是两者都配置之后,系统优先选择sqlSessionTemplateRef,而把sqlSessionFactoryRef作废。与我们代码开发一样,制定了扫描包和注解限制,当然也可以选择接口限定,只是并不常用。这里选择使用注解@Repository作为限定,这是一个Spring对持久层注解,而事实上MyBatis也提供一个对Mapper的注解@Mapper,在工作中我们二选其一就可以。

4. MyBatis的其他配置

在上述我们其实已经完成了一个简单的SSM的例子,但是这里还需要进一步探讨MyBatis的常用配置,毕竟MyBatis的内容远远不止上述的那么少。下面也是我们常用的配置项,代码如下:

#MyBatis常用的配置
mybatis:
  #MyBatis映射文件通配
  mapper-locations: classpath:/mappers/*Mapper.xml
  #配置别名扫描包,和注解@Alias连用
  type-aliases-package: cn.hctech2006.boot.boot-mybatis.bean
  #配置typeHandler扫描包
  #type-handlers-package:
  #MyBatis插件(拦截器)
#  configuration:
#    interceptors:
  #级联延迟加载属性
#  configuration:
#    aggressive-lazy-loading: false
  #执行器
#  executor-type: batch

上述是在Spring Boot中比较常见的MyBatis的配置选项。如果你遇到比较复杂的配置,可以直接通过mybatis.config-location去指定MyBatis本身的配置文件。去完成你需要的复杂配置当你的项目不是太复杂时,使用Spring Boot提供给你的配置就可以了。下面我们再来讲解Spring Boot集成MyBatis插件的例子。
例如,现在存在一个MYBatis插件MyPlugin,其内容如代码所示

package cn.hctech2006.boot.bootmybatis.plugin;


import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;

import java.sql.Connection;
import java.util.Properties;

/**定义MYBatis插件
 *
 */
//定义拦截签名
@Intercepts({
        @Signature(type = StatementHandler.class,
                method = "prepare",
        args = {Connection.class, Integer.class})
})
public class MyPlugin implements Interceptor {

    Properties properties = null;
    //拦截方法逻辑
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        return invocation.proceed();
    }
    //生成MyBatis拦截器代理对象
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    //设置插件属性
    @Override
    public void setProperties(Properties properties) {
        this.properties=properties;
    }
}

这样一个MyBatis插件就被创建出来了,但是我们没有把它放到MyBatis的配置中,这个时候我们完全可以通过application.properties文件增加下面的配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD config 3.0//EN"
   "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <plugins>
        <plugin interceptor="cn.hctech2006.boot.bootmybatis.plugin.MyPlugin">
            <property name="key1" value="value1"/>
            <property name="key2" value="value2"/>
            <property name="key3" value="value3"/>
        </plugin>
    </plugins>
</configuration>

这个文件只是配置了MyBatis部分的组件,开发者可以按照自己所需的部分进行自定义即可,因为MyBatis的其他组件Spring Boot已经默认生产了。
当然,如果不希望使用配置文件也可以使用编码的方式进行处理。如果项目依赖了mybatis-spring-boot-starter后,Spring Boot就会自动的在IoC容器中shanghai名为sqlSessionFactory和sqlSessionTemplate的两个Bean。有时候配置比较少,也可以使用他们来配置MyBatis的相关内容,这样也是比较方便的,但是需要开发者对MyBatsi的底层内容有着足够的了解。例如,我们现在删掉关于MyBatis文件mybatis-config.xml的配置和内容,仅仅使用代码处理,这是我们修改一下Spring Boot的启动文件,如代码清单所示:

package cn.hctech2006.boot.bootmybatis;

import cn.hctech2006.boot.bootmybatis.mapper.SysRoleMapper;
import cn.hctech2006.boot.bootmybatis.plugin.MyPlugin;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.omg.CORBA.MARSHAL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Repository;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Properties;

@SpringBootApplication
//定义MyBatis扫描路径
@MapperScan(
        //指定扫描包
        basePackages = "cn.hctech2006.boot.bootmybatis.mapper",
        //指定SqlSessionFactory
        sqlSessionFactoryRef = "sqlSessionFactory",
        //指定sqlSessionTemplte,将会忽略sqlSessionFactory的配置
        sqlSessionTemplateRef = "sqlSessionTemplate",
        //markerInterface=Class.class限制扫描接口不常用
        //markerInterface =
        //注解限制
        annotationClass = Repository.class
)
public class BootMybatisApplication {
    @Autowired
    SqlSessionFactory sqlSessionFactory = null;
    //启动Spring Bean生命周期执行方法,加入插件
    @PostConstruct
    public void initMyBatis(){
        //插件实例
        Interceptor plugin = new MyPlugin();
        //设置插件属性
        Properties properties = new Properties();
        properties.setProperty("key1","value1");
        properties.setProperty("key2","value2");
        properties.setProperty("key3","value3");
        plugin.setProperties(properties);
        //在SqlSessionFactory中添加插件
        sqlSessionFactory.getConfiguration().addInterceptor(plugin);
    }

    public static void main(String[] args) {
        SpringApplication.run(BootMybatisApplication.class, args);
    }

}

在上述代码中,SqlSessionFactory对象由Spring Boot自动配置得到,这步不需要我们处理,所以直接把它注入进来。接着采用注解@PostConstruct自定义了初始化后的initMyBatis方法。在该方法中就可以配置插件了,在增加插件之前,调用了插件的setProperties方法相关的属性,然后把插件放到MyBatis的机制中。当然,这些需要的对MyBatis的底层有所了解才行。在通常的情况下,如果你遇到了复杂的场景,使用原生的MYBatis配置文件会好一些。

发布了180 篇原创文章 · 获赞 114 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43404791/article/details/105489269