70

SpringBoot | 第九章:Mybatis-plus的集成和使用

 5 years ago
source link: http://www.importnew.com/29501.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

本章节开始介绍数据访问方面的相关知识点。对于后端开发者而言,和数据库打交道是每天都在进行的,所以一个好用的ORM框架是很有必要的。目前,绝大部分公司都选择MyBatis框架作为底层数据库持久化框架。

多说几句

看着现在Mybatis框架的大行其道,让我不禁想起,大学时期,当时还是hibernate的时代,现在基本已经忘记了。而当时,Mybatis的前身iBatis还在书中的某个章节出现过。当时大学老师的意思是:目前国内基本没有使用iBatis的公司,所以这一章节略过,略,过。。。现在对这个还记忆犹新,看着现在Mybatis大行其道,不禁令人唏嘘呀。

Mybatis-Plus

Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

官方网站: http://mp.baomidou.com

简单来说, Mybatis-PlusMybatis 的增强工具包,其简化了 CRUD 操作,提供了 代码生成器 ,强大的 条件构造器 (这是我最喜欢的一个),同时内置了多个实用插件:标配的 分页 插件、 性能分析 插件、 全局拦截 插件等。使得开发过程中,基本的范式代码都一句话解决了,省去了很多重复的操作(程序猿存在的意义呢,说好的让我们搬砖呢!)。

SpringBoot集成

这里选用的mybatis-plus版本为:2.1.9,

mybatisplus-spring-boot-starter版本为:1.0.5。

对应Mybatis版本为:3.4.5

0. 这里以user表为例子,数据库为mysql

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint(20) DEFAULT NULL COMMENT '唯一标示',
  `code` varchar(20) DEFAULT NULL COMMENT '编码',
  `name` varchar(64) DEFAULT NULL COMMENT '名称',
  `status` char(1) DEFAULT '1' COMMENT '状态 1启用 0 停用',
  `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

1. pom依赖:

<!--mybatis plus -->
<dependency>
	<groupId>com.baomidou</groupId>
	<artifactId>mybatisplus-spring-boot-starter</artifactId>
	<version>1.0.5</version>
</dependency>
<dependency>
	<groupId>com.baomidou</groupId>
	<artifactId>mybatis-plus</artifactId>
	<version>2.1.9</version>
</dependency>

2. 配置文件(当然也可以直接使用@Bean的方式进行或者通过 application 配置文件进行,详见官网)

spring-mybatis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--创建jdbc数据源 这里直接使用阿里的druid数据库连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="driverClassName" value="${mysql.driver}"/>
        <property name="url" value="${mysql.url}"/>
        <property name="username" value="${mysql.username}"/>
        <property name="password" value="${mysql.password}"/>
        <!-- 初始化连接大小 -->
        <property name="initialSize" value="0"/>
        <!-- 连接池最大使用连接数量 -->
        <property name="maxActive" value="20"/>
        <!-- 连接池最大空闲 -->
        <property name="maxIdle" value="20"/>
        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="0"/>
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="60000"/>

        <property name="validationQuery" value="${validationQuery}"/>
        <property name="testOnBorrow" value="false"/>
        <property name="testOnReturn" value="false"/>
        <property name="testWhileIdle" value="true"/>

        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="25200000"/>

        <!-- 打开removeAbandoned功能 -->
        <property name="removeAbandoned" value="true"/>
        <!-- 1800秒,也就是30分钟 -->
        <property name="removeAbandonedTimeout" value="1800"/>
        <!-- 关闭abanded连接时输出错误日志 -->
        <property name="logAbandoned" value="true"/>

        <!-- 监控数据库 -->
        <property name="filters" value="mergeStat"/>
    </bean>

    <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 可通过注解控制事务 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!--mybatis-->
    <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
		<!-- 自动扫描mapper.xml文件,支持通配符 -->
        <property name="mapperLocations" value="classpath:mapper/**/*.xml"/>
		<!-- 配置文件,比如参数配置(是否启动驼峰等)、插件配置等 -->
        <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
		<!-- 启用别名,这样就无需写全路径类名了,具体可自行查阅资料 -->
        <property name="typeAliasesPackage" value="cn.lqdev.learning.springboot.chapter9.biz.entity"/>
        <!-- MP 全局配置注入 -->
        <property name="globalConfig" ref="globalConfig"/>
    </bean>
    <bean id="globalConfig" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
        <!--
            AUTO->`0`("数据库ID自增")QW
             INPUT->`1`(用户输入ID")
            ID_WORKER->`2`("全局唯一ID")
            UUID->`3`("全局唯一ID")
        -->
        <property name="idType" value="3" />
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<!-- 自动扫描包路径,接口自动注册为一个bean类 -->
        <property name="basePackage" value="cn.lqdev.learning.springboot.chapter9.biz.dao"/>
    </bean>

</beans>

3. 编写启动类,应用启动时自动加载配置xml文件

/**
 *  mybatisPlus 配置类,使其加载配置文件
 * @author oKong
 *
 */
@Configuration
@ImportResource(locations = {"classpath:/mybatis/spring-mybatis.xml"})
//@MapperScan("cn.lqdev.learning.springboot.chapter9.biz.dao")
//@EnableTransactionManagement
public class MybatisPlusConfig {

}

至此, mybatis-plus 就配置完成了,接下来,利用 代码生成器 一次性创建所需的 daomapper 、通用 CRUD service类。

4. 编写代码生成器类

由于生成器依赖velocity模版引擎,故需要加入依赖:

<dependency>
	<groupId>org.apache.velocity</groupId>
	<artifactId>velocity-engine-core</artifactId>
	<version>2.0</version>
	<scope>test</scope>
</dependency>

MysqlGenerator,此类较长,相关配置可根据实际情况信息修改替换。

public class MysqlGenerator {

	private static final String PACKAGE_NAME = "cn.lqdev.learning.springboot.chapter9";
	private static final String MODULE_NAME = "biz";
	private static final String OUT_PATH = "D:\\develop\\code";
	private static final String AUTHOR = "oKong";

	private static final String DRIVER = "com.mysql.jdbc.Driver";
	private static final String URL = "jdbc:mysql://127.0.0.1:3306/learning?useUnicode=true&characterEncoding=UTF-8";
	private static final String USER_NAME = "root";
	private static final String PASSWORD = "123456";

	/**
	 * <p>
	 * MySQL 生成演示
	 * </p>
	 */
	public static void main(String[] args) {
		// 自定义需要填充的字段
		List<TableFill> tableFillList = new ArrayList<TableFill>();

		// 代码生成器
		AutoGenerator mpg = new AutoGenerator().setGlobalConfig(
				// 全局配置
				new GlobalConfig().setOutputDir(OUT_PATH)// 输出目录
						.setFileOverride(true)// 是否覆盖文件
						.setActiveRecord(true)// 开启 activeRecord 模式
						.setEnableCache(false)// XML 二级缓存
						.setBaseResultMap(false)// XML ResultMap
						.setBaseColumnList(true)// XML columList
						.setAuthor(AUTHOR)
						// 自定义文件命名,注意 %s 会自动填充表实体属性!
						.setXmlName("%sMapper").setMapperName("%sDao")
		// .setServiceName("MP%sService")
		// .setServiceImplName("%sServiceDiy")
		// .setControllerName("%sAction")
		).setDataSource(
				// 数据源配置
				new DataSourceConfig().setDbType(DbType.MYSQL)// 数据库类型
						.setTypeConvert(new MySqlTypeConvert() {
							// 自定义数据库表字段类型转换【可选】
							@Override
							public DbColumnType processTypeConvert(String fieldType) {
								System.out.println("转换类型:" + fieldType);
								// if ( fieldType.toLowerCase().contains( "tinyint" ) ) {
								// return DbColumnType.BOOLEAN;
								// }
								return super.processTypeConvert(fieldType);
							}
						}).setDriverName(DRIVER).setUsername(USER_NAME).setPassword(PASSWORD).setUrl(URL))
				.setStrategy(
						// 策略配置
						new StrategyConfig()
								// .setCapitalMode(true)// 全局大写命名
								.setDbColumnUnderline(true)// 全局下划线命名
								// .setTablePrefix(new String[]{"unionpay_"})// 此处可以修改为您的表前缀
								.setNaming(NamingStrategy.underline_to_camel)// 表名生成策略
								// .setInclude(new String[] {"citycode_org"}) // 需要生成的表
								// .setExclude(new String[]{"test"}) // 排除生成的表
								// 自定义实体,公共字段
								// .setSuperEntityColumns(new String[]{"test_id"})
								.setTableFillList(tableFillList)
								// 自定义实体父类
								// .setSuperEntityClass("com.baomidou.demo.common.base.BsBaseEntity")
								// // 自定义 mapper 父类
								// .setSuperMapperClass("com.baomidou.demo.common.base.BsBaseMapper")
								// // 自定义 service 父类
								// .setSuperServiceClass("com.baomidou.demo.common.base.BsBaseService")
								// // 自定义 service 实现类父类
								// .setSuperServiceImplClass("com.baomidou.demo.common.base.BsBaseServiceImpl")
								// 自定义 controller 父类
								// .setSuperControllerClass("com.baomidou.demo.TestController")
								// 【实体】是否生成字段常量(默认 false)
								// public static final String ID = "test_id";
								.setEntityColumnConstant(true)
								// 【实体】是否为构建者模型(默认 false)
								// public User setName(String name) {this.name = name; return this;}
								.setEntityBuilderModel(true)
								// 【实体】是否为lombok模型(默认 false)<a href="https://projectlombok.org/">document</a>
								.setEntityLombokModel(true)
				// Boolean类型字段是否移除is前缀处理
				// .setEntityBooleanColumnRemoveIsPrefix(true)
				// .setRestControllerStyle(true)
				// .setControllerMappingHyphenStyle(true)
				).setPackageInfo(
						// 包配置
						new PackageConfig().setModuleName(MODULE_NAME).setParent(PACKAGE_NAME)// 自定义包路径
								.setController("controller")// 这里是控制器包名,默认 web
								.setXml("mapper").setMapper("dao")

				).setCfg(
						// 注入自定义配置,可以在 VM 中使用 cfg.abc 设置的值
						new InjectionConfig() {
							@Override
							public void initMap() {
								Map<String, Object> map = new HashMap<String, Object>();
								map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
								this.setMap(map);
							}
						}.setFileOutConfigList(
								Collections.<FileOutConfig>singletonList(new FileOutConfig("/templates/mapper.xml.vm") {
									// 自定义输出文件目录
									@Override
									public String outputFile(TableInfo tableInfo) {
										return OUT_PATH + "/xml/" + tableInfo.getEntityName() + "Mapper.xml";
									}
								})))
				.setTemplate(
						// 关闭默认 xml 生成,调整生成 至 根目录
						new TemplateConfig().setXml(null)
		// 自定义模板配置,模板可以参考源码 /mybatis-plus/src/main/resources/template 使用 copy
		// 至您项目 src/main/resources/template 目录下,模板名称也可自定义如下配置:
		// .setController("...");
		// .setEntity("...");
		// .setMapper("...");
		// .setXml("...");
		// .setService("...");
		// .setServiceImpl("...");
		);

		// 执行生成
		mpg.execute();
	}

}

运行后即可,省了多少事!

Z7BRJr3.png!web

简单实例

简单演示下增删改查及分页的使用。

使用分页时, mybatis-config.xml 需要加入分页插件: PerformanceInterceptor

<plugins>
  <!-- 分页插件配置 -->
  <plugin interceptor="com.baomidou.mybatisplus.plugins.PaginationInterceptor"></plugin>
</plugins>

编写控制层

/**
 * 用户控制层 简单演示增删改查及分页
 * @author oKong
 *
 */
@RestController
@RequestMapping("/user")
public class UserController {

	@Autowired
	IUserService userService;

	@PostMapping("add")
    //正常业务时, 需要在user类里面进行事务控制,控制层一般不进行业务控制的。
	//@Transactional(rollbackFor = Exception.class)
	public Map<String,String> addUser(@Valid @RequestBody UserReq userReq){

		User user = new User();
		user.setCode(userReq.getCode());
		user.setName(userReq.getName());
		//由于设置了主键策略 id可不用赋值 会自动生成
		//user.setId(0L);
		userService.insert(user);
		Map<String,String> result = new HashMap<String,String>();
		result.put("respCode", "01");
		result.put("respMsg", "新增成功");
		//事务测试
		//System.out.println(1/0);
		return result;
	}

	@PostMapping("update")
	public Map<String,String> updateUser(@Valid @RequestBody UserReq userReq){

		if(userReq.getId() == null || "".equals(userReq.getId())) {
			throw new CommonException("0000", "更新时ID不能为空");
		}
		User user = new User();
		user.setCode(userReq.getCode());
		user.setName(userReq.getName());
		user.setId(Long.parseLong(userReq.getId()));		
		userService.updateById(user);
		Map<String,String> result = new HashMap<String,String>();
		result.put("respCode", "01");
		result.put("respMsg", "更新成功");
		return result;
	}

	@GetMapping("/get/{id}")
	public Map<String,Object> getUser(@PathVariable("id") String id){
		//查询
		User user = userService.selectById(id);
		if(user == null) {
			throw new CommonException("0001", "用户ID:" + id + ",未找到");
		}
		UserResp resp = UserResp.builder()
				.id(user.getId().toString())
				.code(user.getCode())
				.name(user.getName())
				.status(user.getStatus())
				.build();
		Map<String,Object> result = new HashMap<String,Object>();
		result.put("respCode", "01");
		result.put("respMsg", "成功");
		result.put("data", resp);
		return result;
	}

	@GetMapping("/page")
	public Map<String,Object> pageUser(int current, int size){
		//分页
		Page<User> page = new Page<>(current, size);
		Map<String,Object> result = new HashMap<String,Object>();
		result.put("respCode", "01");
		result.put("respMsg", "成功");
		result.put("data", userService.selectPage(page));
		return result;
	}		
}

启动应用后,使用 postman 依次访问对应的url地址即可。

IVNvyyI.png!web

数据库:

32yQnab.png!web

分页

rmIf22F.png!web

由于配置了分析插件,控制台会输出执行的sql语句

qeAfMrF.png!web

其他的就不一一贴图了。

关于事务

正常情况下,只需要在服务层中加入 @Transactional 即可,事务相关的此章节不进行阐述,之后有机会会专门拿一个章节来说明下。

示例中为了方便,直接在控制层中加入了 @Transactional 进行事务测试,正式开发过程中,强烈建议在服务层进行业务控制,控制层一般上是进行逻辑判断的!

实体对象字段为枚举类

可能在实际开发中,大家会碰到,为了方便,一些类型、状态字段会编写成枚举类型,比如启用状态:DISABLE(“0”),ENABLE(“1”)。此时可通过配置 typeHandlers 进行自定义类型的处理,这里简单以 EnumOrdinalTypeHandler(存储enum类里的序号值) 进行示例,当然也可根据需要进行自定义处理器的编写,比如编写一个通用的枚举转换器等,其他相关知识点,大家可自行谷歌。

StatusEnums

public enum StatusEnum {

	DISABLE,
	ENABLE;

}

将user对象修改成枚举类型

/**
 * 状态1 启用 0 停用
 */
private StatusEnum status;

配置文件 mybatis-config.xml 加入处理类

<typeHandlers>
   <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
     javaType="cn.lqdev.learning.springboot.chapter9.biz.entity.StatusEnum"/>
</typeHandlers>

之后就会自动进行转换了。大家可下载示例,进行实际操作下。

jy2IZzZ.png!web

总结

本章节主要是对 Mybatis-plus 的集成和简单使用进行了说明,详细的用法,可到官网查看,官网有详细的使用指南,这里就不班门弄斧了。至此,对于一般的开发需求基本上都可以满足了。接下来的章节会重点讲解其他配套工具的使用,敬请期待!

最后

目前互联网上很多大佬都有 springboot 系列教程,如有雷同,请多多包涵了。本文是作者在电脑前一字一句敲的,每一步都是实践的。若文中有所错误之处,还望提出,谢谢。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK