欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Boot整合Mybatis

程序员文章站 2022-04-23 15:45:26
...

SpringBoot整合MyBatis框架

MyBatis比JPA和Hibernate更为简单易用,也更加灵活。在以系统管理为主的时代,Hibernate的模型化有助于系统的分系和建模,重点在于业务模型的分系和设计,术语表和业务模型分系的阶段。而现今已经是移动互联网的时代,互联网的特点是面对公众,相对而言业务比较简单,但是往往网站会拥有大量的用户,面对的问题主要是大数据、高并发和性能问题。因此在这个时代,互联网企业开发的难度主要集中在大数据和性能问题上,所以互联网企业更加关注的是性能和灵活性。

简介

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

可以看出MyBatis是基于一种sql到pojo的模型,它需要我们提供SQL、映射关系(XML或者注解,目前以XML为主)和pojo。但是对于sql和pojo的ing折关系,它提供了自动映射和驼峰映射等,使开发者的开发工作大大减少。

Maven依赖

        <dependency>
        	<groupId>org.mybatis.spring.boot</groupId>
        	<artifactId>mybatis-spring-boot-starter</artifactId>
        	<version>1.3.1</version>
        </dependency>

MyBatis的配置MyBatis的配置文件包括两部分

  • 基础配置文件
  • 映射文件

MyBatis是一个基于SqlSessionFactory构建的框架。对于SqlSessionFactory而言,它的作用是生成SqlSession接口对象,这个接口对象是MyBatis操作的核心。

构建SqlSessionFactory是通过配置类(Configuration)来完成的,对于mybatis-spring-boot-starter,它会给与我们在配置文件(application.properties)进行Configuration配置相关内容。

  • properties(属性):属性文件在实际应用中一般采用spring进行配置,而不是mybatis,这里不介绍。

  • settings(设置):它的配置将改变mybatis的底层行为,可以配置映射规则,如指定映射和驼峰映射,执行器(executor)类型,缓存等内容,比较复杂,具体可以参考mybatis官方网站

  • typeAliases(类型别名):因为使用类全限定名会比较长,所以mybatis会对常用的类提供默认的别名,此外还允许我们通过typeAliases配置自定义别名

  • typeHandlers(类型处理器):这是mybatis的重要配置之一,在mybatis写入和读取数据库的过程中对于不同类型的数据(对于java是JavaType,对于数据库则是JdbcType)进行自定义转换,在大部分的情况下我们不需要使用自定义的typeHandler,因为在mybatis自身就已经定义了比较多的typehandler,mybatis会自动识别javaType和jdbcType,从而实现各种类型的转换。一般来说,typeHandler主要使用集中在枚举类型上。

  • objectFactory(对象工厂):这是一个在mybatis生成返回pojo时会调用的工厂类型。一般我们使用mybatis默认提供的对象工厂类型(DafaultObjectFactory)就可以了。

  • plugins(插件):有时候也成为拦截器,是mybatis最强大也是最危险的组件,它通过动态代理和责任链模式来完成,可以修改mybatis底层的实现功能。

  • environments(数据库环境):可以配置数据库连接内容和事务,一般而言,这些交由spring托管。

  • mappers(映射器):是mybatis最核心的组件,它提供sql和pojo映射关系,这是mybatis开发的核心。

定义POJO对象

现在我们定义一个POJO

Person

package com.lay.pojo;

// MyBatis指定别名
@Alias(value = "person")
public class Person {
    private Long id = null;
    
    private String personName = null;
    
    //性别枚举,这里需要使用typeHandler进行转换
    private SexEnum sex = null;//枚举
    
    private String note = null;
    
    public Long getId() {
        return id;
    }
    
    public void setId(Long id) {
        this.id = id;
    }
    
    public String getUserName() {
        return personName;
    }
    
    public void setUserName(String personName) {
        this.personName = personName;
    }
    
    public SexEnum getSex() {
        return sex;
    }
    
    public void setSex(SexEnum sex) {
        this.sex = sex;
    }
    
    public String getNote() {
        return note;
    }
    
    public void setNote(String note) {
        this.note = note;
    }
    
}

这里通过注解@Alias(value = "person")指定了对象的别名为user,同时,person有个性别属性类型为枚举。

package com.lay.enumeration;

public enum SexEnum {
    MALE(1, "男"), FEMALE(2, "女");
    
    private int id;
    
    private String name;
    
    SexEnum(int id, String name) {
        this.id = id;
        this.name = name;
    }
    
    public static SexEnum getEnumById(int id) {
        for (SexEnum sex : SexEnum.values()) {
            if (sex.getId() == id) {
                return sex;
            }
        }
        return null;
    }
    
    public static SexEnum getEnumByName(String name) {
        for (SexEnum sex : SexEnum.values()) {
            if (sex.getName().equals(name)) {
                return sex;
            }
        }
        return null;
    }
    
    public int getId() {
        return id;
    }
    
    public void setId(int id) {
        this.id = id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
}

在mybatis里,枚举是可以通过typeHandler进行转换的。

TypeHandler转换类

BaseTypeHandler<T>抽象类实现了TypeHandler<T>接口。

这里我们对性别这个枚举类通过继承BaseTypeHandler<T>类进行转换。

SexTypeHandler

package com.lay.typehandler;

//声明JdbcType为整形
@MappedJdbcTypes(JdbcType.INTEGER)
//声明JavaType为枚举SexEnum
@MappedTypes(value = SexEnum.class)
public class SexTypeHandler extends BaseTypeHandler<SexEnum> {
    
    //通过列名读取性别
    @Override
    public SexEnum getNullableResult(ResultSet rs, String col) throws SQLException {
        int sex = rs.getInt(col);
        if (sex != 1 && sex != 2) {
            return null;
        }
        return SexEnum.getEnumById(sex);
    }
    
    //通过下标读取性别
    @Override
    public SexEnum getNullableResult(ResultSet rs, int idx) throws SQLException {
        int sex = rs.getInt(idx);
        if (sex != 1 && sex != 2) {
            return null;
        }
        return SexEnum.getEnumById(sex);
    }
    
    //通过存储存储过程读取性别
    @Override
    public SexEnum getNullableResult(CallableStatement rs, int idx) throws SQLException {
        int sex = rs.getInt(idx);
        if (sex != 1 && sex != 2) {
            return null;
        }
        return SexEnum.getEnumById(sex);
    }
    
    //设置非空性别参数
    @Override
    public void setNonNullParameter(PreparedStatement ps, int idx, SexEnum sex, JdbcType jdbcType) throws SQLException {
        ps.setInt(idx, sex.getId());
    }
    
}

映射文件(xml)

为了使POJO能够与数据库的数据对应,还需要一个映射文件。

personMapper.xml

<?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.lay.dao.MyBatisPersonDao">
	<select id="getPerson" parameterType="long" resultType="person">
		select id,person_name as personName,sex,note from t_person where id=#{id}
	</select>
</mapper>

这里personMapper.xml的目录结构是resources/mapper/personMapper.xml

  • namespcae:指定一个接口(DAO)
  • select:代表是一个查询语,id属性指这条sql,映射dao接口中的方法,parameterType代表参数类型,resultType代表返回类型,这里用到了pojo对象的别名。
  • 这里我们注意到 数据库person_name用了别名对应pojo属性personName保持一致性。在默认的情况下,我们可以不写,mybatis会默认按照驼峰命名规则映射。或者我们也可以自己配置。

定义mybatis操作接口(DAO)

MyBatisPersonDao

package com.lay.dao;

@Mapper
public interface MyBatisPersonDao {
    public Person getPerson(Long id);
}

注意这仅仅是一个接口,不需要任何实现类。通过配置扫描mybatis会自动装配进IoC容器实现依赖注入。

配置文件application.properties

application.properties

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/springboot_database
spring.datasource.username=root
spring.datasource.password=123456
# spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#指定数据连接池的类型
spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
#最大等待连接中的数量,设置0为没有限
spring.datasource.dbcp2.max-idle=10
#最大连接活动数
spring.datasource.dbcp2.max-total=50
#最大等待毫秒数,单位ms,超过时间会出错误信息
spring.datasource.dbcp2.max-wait-millis=10000
#数据库连接池初始化连接数
spring.datasource.dbcp2.initial-size=5

# mybatis映射文件
mybatis.mapper-locations=classpath:mapper/*.xml
# mybatis扫描别名包,和注解@Alias联用
mybatis.type-aliases-package: com.lay.pojo
# 配置typehandler类型转换的扫描包
mybatis.type-handlers-package=com.lay.typehandler
#驼峰命名转换
mybatis.configuration.mapUnderscoreToCamelCase=true
#打印日志
logging.level.com.lay.dao:trace


扫描装配@MapperScan

SpringbootDatabaseApplication

package com.lay;

@SpringBootApplication
@MapperScan(basePackages = "com.lay.dao", annotationClass = Mapper.class)
public class SpringbootDatabaseApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(SpringbootDatabaseApplication.class, args);
    }
}

通过@MapperScan指定扫描包basePackages = "com.lay.dao",会自动装配到IoC容器中。annotationClass = Mapper.class限定被@Mapper标注的类。

测试

首先定义一个服务接口

MyBatisPersonService

package com.lay.service;

public interface MyBatisPersonService {
    public Person getPerson(Long id);
}

实现类MyBatisPersonServiceImpl

package com.lay.service.impl;

@Service
public class MyBatisPersonServiceImpl implements MyBatisPersonService {
    
    @Autowired
    MyBatisPersonDao myBatisPersonDao;
    
    @Override
    public Person getPerson(Long id) {
        return myBatisPersonDao.getPerson(id);
    }
    
}

访问控制器MybatisController

package com.lay.controller;
@Controller
@RequestMapping(value = "mybatis")
public class MybatisController {
    @Autowired
    MyBatisPersonService myBatisPersonService;
    
    @RequestMapping(value = "getPerson")
    @ResponseBody
    public Person getPerson(@RequestParam("id") Long id) {
        return myBatisPersonService.getPerson(id);
    }
    
}

然后通过浏览器地址栏输入http://localhost:8080/mybatis/getPerson?id=1进行测试

其他装配方式

  • MapperFactoryBean手动
@Configuration
public class MybatisConfig(){
	//这里的SqlSessionFactory是spring boot自动为我们生成的。
    @Autowired
    SqlSessionFactory sqlSessionFactory=null;
    
    @Bean
    public MapperFactoryBean<MyBatisPersonDao> initMyBatisUserDao(){
        MapperFactoryBean<MyBatisPersonDao> bean=new MapperFactoryBean<>();
        bean.setMapperInterface(MyBatisPersonDao.class);
        bean.setSqlSessionFactory(sqlSessionFactory);
        return bean;
    }
}
  • 使用MapperScannerConfigurer扫描装配Mybatis接口
@Bean
public MapperScannerConfigurer mapperScannerConfig(){
    //定义扫描器实例
    MapperScannerConfigurer mapperScannerConfigurer=new MapperScannerConfigurer();
    //加载SqlSessionFactory,spring boot会自动生产SqlSessionFactory实例
    mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
    //定义扫描的包
    mapperScannerConfigurer.setBasePackage("com.lay.dao");
    //限定被标注@Mapper的接口才被扫描
    mapperScannerConfigurer.setAnnotationClass(Mapper.class);
    return mapperScannerConfigurer;
}