概述

定义

MyBatis官网 https://mybatis.org/mybatis-3/ 最新版本为3.5.9

MyBatis是一个的ORM框架,支持自定义SQL、存储过程和高级映射。MyBatis对JDBC做了稳定封装使我们不再需要直接操作繁琐JDBC的代码进行参数的手动设置和结果检索。在MyBatis可以使用XML、注解和Java pojo映射到数据库记录。

由于我们Java技术栈程序员对MyBatis都已非常熟悉,本篇我们则主要是从源码的角度去收获不一样MyBatis理解之旅。众所周知JDBC操作核心步骤包括创建连接、创建Statement和执行SQL语句、ResultSet接收结果;MyBatis的ORM核心思想是用于实现面向对象编程语言里不同类型系统的数据之间的转换,我们接下来探索三个问题。

  • 如何获取数据库源?
  • 如何执行Sql语句?
  • 结果集如何处理?

源码探索

JDBC

我们先简单回顾下JDBC操作MySQL数据库的例子

package org.apache.ibatis.itxs;import java.sql.*;public class JDBCMain {public static void main(String[] args) {Connection connection = null;Statement st = null;ResultSet rs = null;try {//第一种注册驱动DriverManager.registerDriver(new com.mysql.jdbc.Driver());//第二种方式也可加载驱动程序//Class.forName("com.mysql.jdbc.Driver");//2.建立连接,参数一:协议+访问数据库,参数二:用户名,参数三:密码connection = DriverManager.getConnection("192.168.50.68:3306/testdb?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull", "test", "");//3.创建statement,跟数据库打交道一定需要这个对象st = connection.createStatement();//4.执行查询String sql = "select * from blog";rs = st.executeQuery(sql);//5.遍历查询每一条记录while(rs.next()) {int id = rs.getInt("id");String title = rs.getString("title");String content = rs.getString("content");System.out.println("id = " + id + "; title = " + title + "; content = " + content);}connection.close();st.close();rs.close();} catch (SQLException e) {e.printStackTrace();}}
}

搭建源码调试环境

mybatis源码https://github.com/mybatis/mybatis-3

由于我们直接在mybatis源码演示demo,所以需要先将mybatis源码下的pom中mysql-connector-java依赖的test注释掉,

数据库准备Blog表并初始数据,新建resources并标记为资源目录,常见mybatis-config.xml和db.properties文件,创建实体类Blog、还有对应Mapper接口、Mapper xml文件。创建一个main类调试。

Blog表语句

CREATE TABLE `blog` (`id` BIGINT NOT NULL,`title` VARCHAR(100) DEFAULT NULL,`content` VARCHAR(300) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
insert into blog(id,title,content) values(1,'Java','面向对象');

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><properties resource="config/db.properties"></properties><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment></environments><mappers><mapper resource="org/apache/ibatis/example/BlogMapper.xml"/></mappers>
</configuration>

db.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://192.168.50.68:3306/testdb?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull
username=test
password=

Blog.java

package org.apache.ibatis.example;public class Blog {private long id;private String title;private String content;public long getId() {return id;}public void setId(long id) {this.id = id;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}@Overridepublic String toString() {return "Blog{" +"id=" + id +", title='" + title + '\'' +", content='" + content + '\'' +'}';}
}

BlogMapper.java

package org.apache.ibatis.example;public interface BlogMapper {Blog selectBlog111(Long id);
}

BlogMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.apache.ibatis.example.BlogMapper"><select id="selectBlog" resultType="org.apache.ibatis.example.Blog">select * from Blog where id = #{id}</select>
</mapper>

Main.java

package org.apache.ibatis.itxs;import org.apache.ibatis.example.Blog;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;
import java.io.InputStream;public class Main {public static void main(String[] args) {String resource = "config/mybatis-config.xml";InputStream inputStream = null;try {inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);try (SqlSession session = sqlSessionFactory.openSession()) {Blog blog = session.selectOne("org.apache.ibatis.example.BlogMapper.selectBlog", 1);System.out.printf(blog.toString());}} catch (IOException e) {e.printStackTrace();}}
}

如何获取数据源

加载数据源配置信息

我们这里采用基于XML配置文件方式来演示,这样方式来学习通过mybatis源码笔者也认为最能得到,是Main类的main方法逐步调试进入mybatis的源码,从下面的堆栈调用链可以看出操作mybatis是从SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream)这一行开始,在这里先读取mybatis配置文件数据加载到全局配置Configuration里

首先parseConfiguration(XNode root)这个方法主要是初始化处理mybatis配置文件,我们发现配置文件中的在源码中是放在最前面,这个也解释如果我们把这行放在environments节点后面XML格式校验提示错误。org.apache.ibatis.builder.xml.XMLConfigBuilder中parseConfiguration方法代码如下:

  private void parseConfiguration(XNode root) {try {// issue #117 read properties firstpropertiesElement(root.evalNode("properties"));Properties settings = settingsAsProperties(root.evalNode("settings"));loadCustomVfs(settings);loadCustomLogImpl(settings);typeAliasesElement(root.evalNode("typeAliases"));pluginElement(root.evalNode("plugins"));objectFactoryElement(root.evalNode("objectFactory"));objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));reflectorFactoryElement(root.evalNode("reflectorFactory"));settingsElement(settings);// read it after objectFactory and objectWrapperFactory issue #631environmentsElement(root.evalNode("environments"));databaseIdProviderElement(root.evalNode("databaseIdProvider"));typeHandlerElement(root.evalNode("typeHandlers"));mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}}

在org.apache.ibatis.builder.xml.XMLConfigBuilder中environmentsElement(XNode context)这个方法处理environments节点,通过configuration.setEnvironment(environmentBuilder.build())将数据源的配置存储数据源工厂中,便于后续操作sql时候可以从配置类中获取信息进而创建连接。

Configuration可以说是mybatis最重要的一个全局类,mybatis大部分重要对象都存在Configuration里,Environment在Configuration里面,而JDBC的数据源是在Environment类中。mybatis采用工厂模式创建数据源和管理连接池(池化技术),不过在实际项目开发中,我们都会使用阿里的druid或者hikari等三方的连接池。

如何操作JDBC

首先在mybatis源码的pom有包含mysql-connector-java的依赖,上面我们也已把test注释掉,在程序加载时候org.apache.ibatis.datasource.unpooled.UnpooledDataSource类通过静态代码块方式将mysql驱动写入registeredDrivers的ConcurrentHashMap里。

在UnpooledDataSource中doGetConnection方法调用获取连接,在这里我们见到熟悉的JDBC操作代码(DriverManager.getConnection),并返回一个JDBC的连接供后续操作使用。该方法主要做下面三个工作:

  • initializeDriver,初始化驱动,如果在前面的静态代码块中没有扫描到需要的数据库驱动,则需要在这里加载需要的驱动。
  • 根据配置信息获取一个新的连接。
  • 配置创建的连接对象。
  private Connection doGetConnection(Properties properties) throws SQLException {initializeDriver();Connection connection = DriverManager.getConnection(url, properties);configureConnection(connection);return connection;}private synchronized void initializeDriver() throws SQLException {if (!registeredDrivers.containsKey(driver)) {Class<?> driverType;try {if (driverClassLoader != null) {driverType = Class.forName(driver, true, driverClassLoader);} else {driverType = Resources.classForName(driver);}// DriverManager requires the driver to be loaded via the system ClassLoader.// http://www.kfu.com/~nsayer/Java/dyn-jdbc.htmlDriver driverInstance = (Driver) driverType.getDeclaredConstructor().newInstance();DriverManager.registerDriver(new DriverProxy(driverInstance));registeredDrivers.put(driver, driverInstance);} catch (Exception e) {throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);}}}private void configureConnection(Connection conn) throws SQLException {if (defaultNetworkTimeout != null) {conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), defaultNetworkTimeout);}if (autoCommit != null && autoCommit != conn.getAutoCommit()) {conn.setAutoCommit(autoCommit);}if (defaultTransactionIsolationLevel != null) {conn.setTransactionIsolation(defaultTransactionIsolationLevel);}}

到此我们知道MyBatis是如何一步步走到获取数据源和JDBC创建连接的过程,到这里第一个问题也已梳理清楚。

如何执行Sql语句

mapper支持类型?

我们接着从上面org.apache.ibatis.builder.xml.XMLConfigBuilder中parseConfiguration(XNode root)方法中的mapperElement(XNode parent)这里可以看到源码对于mapper的四种类型配置支持的优先级和逻辑,可以很清晰看出package优先级最高,且resource、url 、class只能存在一个,否则直接抛出异常。我们这里的例子使用的是resource方式

Mybatis配置文件mapper类型配置使用官网示例如下

mapper.xml内容在哪里?

mapper.xml存储很多操作的信息比如最核心的sql语句,看下堆栈调用链从XMLConfigBuilder->XMLMapperBuilder->XMLStatementBuilder,mybatis有很多这种Builder,每个Builder处理各自的职责。

addMappedStatement:298, MapperBuilderAssistant (org.apache.ibatis.builder)
parseStatementNode:113, XMLStatementBuilder (org.apache.ibatis.builder.xml)
buildStatementFromContext:138, XMLMapperBuilder (org.apache.ibatis.builder.xml)
buildStatementFromContext:131, XMLMapperBuilder (org.apache.ibatis.builder.xml)
configurationElement:121, XMLMapperBuilder (org.apache.ibatis.builder.xml)
parse:95, XMLMapperBuilder (org.apache.ibatis.builder.xml)
mapperElement:379, XMLConfigBuilder (org.apache.ibatis.builder.xml)
parseConfiguration:120, XMLConfigBuilder (org.apache.ibatis.builder.xml)
parse:99, XMLConfigBuilder (org.apache.ibatis.builder.xml)
build:78, SqlSessionFactoryBuilder (org.apache.ibatis.session)
build:64, SqlSessionFactoryBuilder (org.apache.ibatis.session)
main:33, Main (org.apache.ibatis.itxs)

我们这里XMLMapperBuilder则是加载解析mapper节点文件并将数据写入Configuration里面集合成员变量。

XMLStatementBuilder的parseStatementNode则是解析我们mapper.xml文件的内容,将mapper.xml的所有元素拆分并组装成MappedStatement对象,然后通过configuration.addMappedStatement(statement)的put存储到Configuration全局配置中mappedStatements的StrictMap,StrictMap是一个内部类继承HashMap,所以本质也是一个HashMap。

  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection").conflictMessageProducer((savedValue, targetValue) ->". please check " + savedValue.getResource() + " and " + targetValue.getResource());protected static class StrictMap<V> extends HashMap<String, V> {}

执行器类型有哪些?

通过堆栈调用链,mybatis在openSession中先创建执行器,

newExecutor:691, Configuration (org.apache.ibatis.session)
openSessionFromDataSource:96, DefaultSqlSessionFactory (org.apache.ibatis.session.defaults)
openSession:47, DefaultSqlSessionFactory (org.apache.ibatis.session.defaults)
main:34, Main (org.apache.ibatis.itxs) //sqlSessionFactory.openSession()

执行器有SimpleExecutor(简单执行器)、BatchExecutor(批量执行器)、ReuseExecutor(重用执行器),然后对上面三种执行器判断是否需要缓存增强功能,在调试中可以看到cacheEnabled为true,也即是mybatis默认是开启缓存功能的,本篇就不展开研究mybatis的一二级缓存功能。

public enum ExecutorType {SIMPLE, REUSE, BATCH
}

Mybatis中的插件允许你针对核心组件接口Executor 、StatementHandler、ParameterHandler、ResultSetHandler中任何一个方法进行拦截调用。而每个Interceptor(拦截的接口)其实是通过JDK的动态代理技术生成的代理类,每当执行这4种接口中的方法时,就会进入拦截方法(具体就是InvocationHandler的invoke()方法)。插件这里采用的是责任链设计模式,很多对象由每一个对象对其下家的引用而连接起来形成一条链,请求在这个链上传递,直到链上的某一个对象决定处理此请求。这里设计到InterceptorChain(拦截链类)、Interceptor(拦截器接口)、Plugin(插件类,实现了InvocationHandler),具体有兴趣可以自己再深入研究。

在哪执行SQL语句

从下面当我们执行session.selectOne查询数据时

query:64, PreparedStatementHandler (org.apache.ibatis.executor.statement)
query:79, RoutingStatementHandler (org.apache.ibatis.executor.statement)
doQuery:63, SimpleExecutor (org.apache.ibatis.executor)
queryFromDatabase:325, BaseExecutor (org.apache.ibatis.executor)
query:156, BaseExecutor (org.apache.ibatis.executor)
query:109, CachingExecutor (org.apache.ibatis.executor)
query:89, CachingExecutor (org.apache.ibatis.executor)
selectList:151, DefaultSqlSession (org.apache.ibatis.session.defaults)
selectList:145, DefaultSqlSession (org.apache.ibatis.session.defaults)
selectList:140, DefaultSqlSession (org.apache.ibatis.session.defaults)
selectOne:76, DefaultSqlSession (org.apache.ibatis.session.defaults)
main:35, Main (org.apache.ibatis.itxs)  //session.selectOne("org.apache.ibatis.example.BlogMapper.selectBlog", 1);

首先从DefaultSqlSession获取MappedStatement 这里存储了我们在上一步存储的sql语句当然还有其他很多很多的信息,执行器去执行sql语句

MappedStatement ms = configuration.getMappedStatement(statement);
BoundSql boundSql = ms.getBoundSql(parameterObject);
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

//在org.apache.ibatis.executor.SimpleExecutor#doQuery完成sql语句和参数组装,这里包括sql语句解析、参数替换等内容,我们本篇不展开

stmt = prepareStatement(handler, ms.getStatementLog());

最后在org.apache.ibatis.executor.statement.PreparedStatementHandler#query,这里就非常熟悉了,这个是JDBC的PreparedStatement,到这里我们也一步步找到MyBatis使用JDBC执行查询Sql语句最终地方。

如何处理结果集?

我们接着上小节最后地方org.apache.ibatis.executor.resultset.ResultSetHandler#handleResultSets,这个就是mybatis处理结果集核心函数,mybatis提供一个非常重要结果集包装类ResultSetWrapper,这个就是MyBatis实现ORM的重要地方

ResultSetWrapper使用了columnNames、classNames、jdbcTypes三个ArrayList分别存放数据库的列名、Java类型、Mybatis jdbc对应数据库类型。

至此我们前面对MyBatis源码的三个基础问题已经全部弄清楚了。

MyBatis-Plus

概述

MyBatis-Plus官网 https://www.baomidou.com/

MyBatis-Plus(简称 MP)是MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。是MyBatis最好的搭档,就像魂斗罗中的1P、2P,基友搭配,效率翻倍。

简单示例

我们在企业数据库应用开发为提升效率更多会使用MyBatis-Plus,MyBatis-Plus宗旨就是为了简化开发而生,只做增强,简单配置就可以使用单表的CRUD,还支持丰富功能如代码生成、自动分页、逻辑删除、自动填充。MyBatis-Plus提供非常详细功能使用示例,需全面学习伙伴可自行前往官网,我们这里以一个简单Mybatis-Plus整合Spring Boot的实例抛出一个引子。

创建用户信息表:

CREATE TABLE user_info
(id                   INT NOT NULL AUTO_INCREMENT COMMENT '主键唯一ID',user_name            VARCHAR(20) COMMENT '名称',age                  SMALLINT COMMENT '年龄',create_time          DATETIME COMMENT '创建时间',update_time          DATETIME COMMENT '更新时间',VERSION              INT COMMENT '版本号',deleted              INT COMMENT '删除标识(0:正常,1:删除)',PRIMARY KEY (id)
)ENGINE=INNODB DEFAULT CHARSET=utf8;

pom文件内容

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>cn.itxs</groupId><artifactId>mybatis-demo</artifactId><version>1.0-SNAPSHOT</version><repositories><!--阿里云仓库--><repository><id>aliyun</id><url>http://maven.aliyun.com/nexus/content/groups/public/</url></repository><!--快照版本使用,正式版本无需添加此仓库--><repository><id>snapshots</id><url>https://oss.sonatype.org/content/repositories/snapshots/</url></repository></repositories><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.2</version></parent><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><java.version>1.8</java.version><druid.version>1.2.8</druid.version><mysql.version>8.0.27</mysql.version><lombok.version>1.18.22</lombok.version><mybatis-plus.version>3.5.0</mybatis-plus.version><mybatis-plus-generator.version>3.5.1</mybatis-plus-generator.version><freemarker.version>2.3.31</freemarker.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version><scope>provided</scope><optional>true</optional></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>${druid.version}</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatis-plus.version}</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>${mybatis-plus-generator.version}</version></dependency><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>${freemarker.version}</version></dependency></dependencies></project>

application.yml内容

spring:application:name: mybatis-plus-demodatasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://192.168.50.95:3308/test?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8username: rootpassword: 123456type: com.alibaba.druid.pool.DruidDataSourcedruid:max-active: 1000min-idle: 5initial-size: 10
mybatis-plus:global-config:db-config:id-type: autologic-delete-field: deletedlogic-delete-value: 1logic-not-delete-value: 0configuration:map-underscore-to-camel-case: oncall-setters-on-nulls: onlog-impl: org.apache.ibatis.logging.stdout.StdOutImpl

MyBatisPlusConfig.java

package cn.itxs.config;import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;@MapperScan("cn.itxs.mapper")
@EnableTransactionManagement
@Configuration
public class MyBatisPlusConfig {/*** 配置新版乐观锁插件,新版分页插件*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();//乐观锁插件mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());//分页插件mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return mybatisPlusInterceptor;}
}

MyMetaObjectHandler.java

package cn.itxs.handle;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;import java.util.Date;/*** 用于对数据库表中实现记录的创建时间和修改时间自动填充实现类*/
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {//插入记录时填充策略@Overridepublic void insertFill(MetaObject metaObject) {log.info("start insert fill.....");this.setFieldValByName("createTime",new Date(),metaObject);this.setFieldValByName("updateTime",new Date(),metaObject);this.setFieldValByName("modifyTime",new Date(),metaObject);}//更新记录时填充策略@Overridepublic void updateFill(MetaObject metaObject) {log.info("start update fill.....");this.setFieldValByName("updateTime",new Date(),metaObject);this.setFieldValByName("modifyTime",new Date(),metaObject);}
}

UserInfo.java

package cn.itxs.pojo;import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class UserInfo {@TableId(value = "id", type = IdType.AUTO)private Integer id;private String userName;private Short age;@TableField(fill = FieldFill.INSERT)private Date createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private Date updateTime;@Versionprivate Integer version;@TableLogicprivate Integer deleted;
}

UserInfoMapper.java

package cn.itxs.mapper;import cn.itxs.pojo.UserInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;public interface UserInfoMapper extends BaseMapper<UserInfo> {}

加下来的service和serviceImpl文件是为了使用提供批量插入功能,上面UserInfoMapper已经提供很多常用增删改查的功能了。

UserInfoService.java

package cn.itxs.service;import cn.itxs.pojo.UserInfo;
import com.baomidou.mybatisplus.extension.service.IService;public interface UserInfoService extends IService<UserInfo> {}

UserInfoServiceImpl.java

package cn.itxs.service.impl;import cn.itxs.mapper.UserInfoMapper;
import cn.itxs.pojo.UserInfo;
import cn.itxs.service.UserInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {}

MainAppllication.java

package cn.itxs;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class MainAppllication {public static void main(String[] args) {SpringApplication.run(MainAppllication.class, args);}
}

最后我们创建一个测试类,常见的添加、默认值填充、乐观锁修改、逻辑删除、批量添加、条件查询、条件修改、条件删除等功能

MyBatisPlusTest.java

package cn.itxs;import cn.itxs.mapper.UserInfoMapper;
import cn.itxs.pojo.UserInfo;
import cn.itxs.service.UserInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;@SpringBootTest
@Slf4j
@SuppressWarnings("all")
public class MyBatisPlusTest {@AutowiredUserInfoMapper userInfoMapper;@AutowiredUserInfoService userInfoService;@Testvoid addUserInfo(){int result = userInfoMapper.insert(UserInfo.builder().userName("张三").age((short)22).version(1).deleted(0).build());log.info("result={}",result);}@Testvoid updateUserInfo(){UserInfo userInfo = new UserInfo();userInfo.setId(3);userInfo.setUserName("张三三");userInfo.setAge((short)26);int result = userInfoMapper.updateById(userInfo);log.info("result={}",result);}@Testvoid updateUserInfoByVersion(){UserInfo userInfo = userInfoMapper.selectById(3);userInfo.setUserName("张三丰");userInfo.setAge((short)23);int result = userInfoMapper.updateById(userInfo);log.info("result={}",result);}@Testvoid insertUserInfoBatch() {Random random =new Random();List<UserInfo> userInfos = new ArrayList<>(1000);for (int i=1;i<1000;i++){UserInfo userInfo = new UserInfo();userInfo.setUserName("李四"+i);userInfo.setAge((short)(random.nextInt(100)+1));userInfo.setVersion(1);userInfo.setDeleted(0);userInfos.add(userInfo);}boolean result = userInfoService.saveBatch(userInfos, 100);log.info("result={}",result);}@Testpublic void SelectAll() {List<UserInfo> userInfos = userInfoMapper.selectList(null);userInfos.forEach(System.out::println);}@Testvoid selectUserInfoByQuery() {QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();queryWrapper.lambda().gt(UserInfo::getId,300);queryWrapper.lambda().eq(UserInfo::getAge,50);List<UserInfo> userInfos = userInfoMapper.selectList(queryWrapper);userInfos.forEach(System.out::println);}@Testvoid updateUserInfoByQuery() {UpdateWrapper<UserInfo> updateWrapper = new UpdateWrapper<>();updateWrapper.lambda().set(UserInfo::getAge,30).gt(UserInfo::getId ,800);int result = userInfoMapper.update(null,updateWrapper);log.info("result={}",result);}@Testvoid updateUserInfoByQuerySql() {UpdateWrapper<UserInfo> updateWrapper = new UpdateWrapper<>();updateWrapper.set("user_name","age30username");updateWrapper.apply("age = 30");int result = userInfoMapper.update(null,updateWrapper);log.info("result={}",result);}@Testvoid deleteUserInfoByQuery() {UpdateWrapper<UserInfo> updateWrapper = new UpdateWrapper<>();updateWrapper.lambda().gt(UserInfo::getId ,900);int result = userInfoMapper.delete(updateWrapper);log.info("result={}",result);}}

示例运行如下

代码生成器

上一小节我们已经引入依赖mybatis-plus-generator和freemarker模板引擎,详细参数可以查阅官网

快速生成

package cn.itxs.generator;import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Collections;public class CodeGererator {public static void main(String[] args) {FastAutoGenerator.create("jdbc:mysql://192.168.50.95:3308/test?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8", "root", "123456").globalConfig(builder -> {builder.author("itxs") // 设置作者.enableSwagger() // 开启 swagger 模式.fileOverride() // 覆盖已生成文件.outputDir("D://code_generator"); // 指定输出目录,一般设置我们工程项目下}).packageConfig(builder -> {builder.parent("cn.itxs.generator") // 设置父包名.moduleName("system") // 设置父包模块名.entity("po").service("service").serviceImpl("service.impl").mapper("mapper").controller("controller").pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://code_generator")); // 设置mapperXml生成路径,指定输出目录,一般设置我们工程项目下}).strategyConfig(builder -> {builder.addInclude("user_info") // 设置需要生成的表名.addTablePrefix("t_", "c_"); // 设置过滤表前缀}).templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板.execute();}
}

交互式生成

package cn.itxs.generator;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.fill.Column;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;public class InteractiveCodeGererator {public static void main(String[] args) {FastAutoGenerator.create("jdbc:mysql://192.168.50.95:3308/test?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8", "root", "123456")// 全局配置.globalConfig((scanner, builder) -> builder.author(scanner.apply("请输入作者名称?")).fileOverride())// 包配置.packageConfig((scanner, builder) -> builder.parent(scanner.apply("请输入包名?")))// 策略配置.strategyConfig((scanner, builder) -> builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all"))).controllerBuilder().enableRestStyle().enableHyphenStyle().entityBuilder().enableLombok().addTableFills(new Column("create_time", FieldFill.INSERT)).build())/*模板引擎配置,默认 Velocity 可选模板引擎 Beetl 或 Freemarker.templateEngine(new BeetlTemplateEngine()).templateEngine(new FreemarkerTemplateEngine())*/.execute();}// 处理 all 情况protected static List<String> getTables(String tables) {return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));}
}

苞米豆生态圈

概述

MyBatis-Plus由苞米豆开发的,而苞米豆生态圈还有很多组件。我们本篇再简单了解MybatisX和Mybatis-Mate。

  • MybatisX (opens new window) - 一款全免费且强大的 IDEA 插件,支持跳转,自动补全生成 SQL,代码生成。
  • Mybatis-Mate (opens new window) - 为 MyBatis-Plus 企业级模块,支持分库分表、数据审计、字段加密、数据绑定、数据权限、表结构自动生成 SQL 维护等高级特性。
  • Dynamic-Datasource (opens new window) - 基于 SpringBoot 的多数据源组件,功能强悍,支持 Seata 分布式事务。
  • Shuan (opens new window) - 基于 Pac4J-JWT 的 WEB 安全组件, 快速集成。
  • Kisso (opens new window) - 基于 Cookie 的单点登录组件。
  • Lock4j (opens new window) - 基于 SpringBoot 同时支持 RedisTemplate、Redission、Zookeeper 的分布式锁组件。
  • Kaptcha (opens new window) - 基于 SpringBoot 和 Google Kaptcha 的简单验证码组件,简单验证码就选它。
  • Aizuda 爱组搭 (opens new window) - 低代码开发平台组件库。

MybatisX

MybatisX是一款全免费且强大的 IDEA 插件,支持跳转,自动补全生成 SQL,代码生成。在插件市场下载MybatisX,安装即可使用

快速体现跳转功能,可以从mapper接口中的接口方法直接跳转到对应xml文件的代码部分

代码快速补全功能

Mybatis-Mate

Mybatis-Mate为 MyBatis-Plus 企业级模块,支持分库分表、数据审计、字段加密、数据绑定、数据权限、表结构自动生成 SQL 维护等高级特性等。

https://gitee.com/baomidou/mybatis-mate-examples 这里提供企业级很多使用场景示例,我们下载源码工程后测试数据脱敏的例子,我将数据库修改为mysql,端口为8088

访问测试页面UserController的几个测试页面,针对用户表用户名、手机、邮箱的脱敏的结果如下,有兴趣可以多研究其他高级功能

**本人博客网站 **IT小神 www.itxiaoshen.com

来自MyBatis不一样收获结果的探索之旅-v3.5.9相关推荐

  1. 来自MyBatis不一样收获结果的探索之旅

    概述 定义 MyBatis官网** mybatis – MyBatis 3 | Introduction 最新版本为3.5.9** MyBatis是一个的ORM框架,支持自定义SQL.存储过程和高级映 ...

  2. 美团外卖商家端视频探索之旅

    美团外卖商家端视频探索之旅 背景 美团外卖至今已迅猛发展了六年,随着外卖业务量级与日俱增,单一的文字和图片已无法满足商家的需求,商家迫切需要更丰富的商品描述手段吸引用户,增加流量,进而提高下单转化率和 ...

  3. 游戏AI探索之旅:从AlphaGo到MOBA游戏

    背景:7月28日,腾讯云在北京举办云+社区沙龙,邀请来自腾讯与四川云检科技的五位AI技术专家,分享他们在专业领域的AI开发经验,帮助开发者在具体行业场景中实践AI技术.本文根据王亮在[7.28日腾讯云 ...

  4. 浏览器向服务器发送的消息称为,网络是怎样连接之浏览器的探索之旅读书笔记(一)...

    照例是写在前面的话,今天在这里想和自己说一些话,希望未来的自己能够记住,就像我在简书的签名里写下的,"希望自己能记得当下写下那段文字的初心!",学习其实是一件非常严肃的事情,它容不 ...

  5. C语言探索之旅 | 第二部分第一课:模块化编程

    C语言之父 Dennis Ritchie -- 简书作者 谢恩铭 转载请注明出处 第二部分第一课:模块化编程 上一课是C语言探索之旅 | 第一部分第十课:练习题+习作,至此,我们[C语言探索之旅]的第 ...

  6. 幻影机器人庄园讲解员_探索之旅——莘城海粟幼儿园秋游活动

    原标题:探索之旅--莘城海粟幼儿园秋游活动 美丽的金秋时节, 是大自然赋予我们取之不尽的源泉. 秋天的绚丽与多姿, 已成为孩子探索与发现的最美时节, 于是,孩子们最盼望的秋游开始啦! 莘城海粟幼儿园 ...

  7. Linux 探索之旅 | 第一部分第四课:磁盘分区 + 完成 Ubuntu 安装

    -- 作者 谢恩铭 转载请注明出处 内容简介 第一部分第四课:磁盘分区+完成Ubuntu安装 第一部分第五课预告:Unity桌面,人生若只如初见 磁盘分区+完成Ubuntu安装 上一课Linux探索之 ...

  8. 快快来,和我们一起开启稳定性的探索之旅

    我们穿过山和大海,也见过人山人海. 我们见过各类故障,也排过千雷万险. 这一次,不如我们一起,开启稳定性的探索之旅. 让无法解决的问题少一点点,让世界的确定性多一点点.? 无论是前端业务的开发者,还是 ...

  9. Linux 探索之旅 | 开宗明义 + 第一部分第一课:什么是 Linux?

    -- 作者 谢恩铭 转载请注明出处 内容简介 开宗明义 第一部分第一课:什么是Linux? 第一部分第二课预告:下载Linux,免费的噢! 开宗明义 我们总听到别人说:Linux挺复杂的,是给那些追求 ...

最新文章

  1. NHibernate之旅(11):探索多对多关系及其关联查询
  2. wireshark 选择网络接口
  3. Jacobian矩阵和Hessian矩阵的理解
  4. SAP系统中凭证涉及日期
  5. JMS学习(六)--提高非持久订阅者的可靠性 以及 订阅恢复策略
  6. [转载]oracle的表导入导出,表空间,用户名
  7. 前端学习(3276):js中this的使用
  8. 什么是mybatis,mybatis有什么特点
  9. Leetcode算法题(C语言)6--只出现一次的数字
  10. Go语言---面向对象编程
  11. NASA无人机障碍赛:专业选手Vs.人工智能,赌一赌谁赢?
  12. Task10.Bert
  13. Python中re.findall的贪心和非贪心算法
  14. Android开发:ZXing条码扫描-竖屏解决方案
  15. 《Qt on Android核心编程》介绍
  16. 《惢客创业日记》2021.06.28-30(周一)防骗的终极解决方案
  17. html+input+js双击,JS双击变input框批量修改内容
  18. APT仓库目录和repository目录结构
  19. 家庭“好用”优化师:每一件好物都是在积攒生活闪光
  20. linux内核配置cpu相关,Linux内核配置

热门文章

  1. 英语十大词性之一 - 介词
  2. 阿里云服务器 云监控 API 调用示例
  3. 出现ERROR 1698 (28000): Access denied for user ‘root‘@‘localhost‘ 的解决方法
  4. 牛客—编程初学者入门训练—Kiki和酸奶(C语言实现)
  5. 2563: 阿狸和桃子的游戏 贪心
  6. 内存卡损坏怎么修复?分享实际经验
  7. 《UE4蓝图完全学习》笔记
  8. 常见编程语言对REPL支持情况小结[转]
  9. 【高项备考】多种类型计算题学习
  10. 产品 • B端和C端产品经理有什么区别?