<file_sep>package com.mybatis.sqlsession;

import java.util.List;

import com.mybatis.config.Configuration;
import com.mybatis.config.MappedStatement;
import com.mybatis.executor.CachingExecutor;
import com.mybatis.executor.Executor;
import com.mybatis.executor.SimpleExecutor;

public class DefaultSqlSession implements SqlSession {

	private Configuration configuration;

	public DefaultSqlSession(Configuration configuration) {
		this.configuration = configuration;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T selectOne(String statementId, Object param) {
		List<Object> list = this.selectList(statementId, param);
		if (list == null || list.size() == 0) {
			return null;
		} else if (list.size() == 1) {
			return (T) list.get(0);
		} else {
			throw new RuntimeException("只能返回一个对象");
		}
	}

	@Override
	public <T> List<T> selectList(String statementId, Object param) {
		// 根据statementId获取MappedStatement对象
		MappedStatement mappedStatement = configuration.getMappedStatementById(statementId);
		// 执行Statement的操作（执行方式有多种：一种是带有二级缓存的执行方式、一种是基本执行方式[只带有一级缓存，基本执行方式又分成几种：基本执行器、批处理执行器等]）
		// 此处可以考虑放到MappedStatement对象中，该对象中可以根据是否配置了二级缓存来确定创建的是哪个Executor
		Executor executor = new CachingExecutor(new SimpleExecutor());
		return executor.query(mappedStatement, configuration, param);
	}

}
<file_sep>package com.mybatis.config;

import com.mybatis.sqlsource.SqlSource;

/**
 * 封装select等CRUD标签的信息
 * 
 * @author liu
 *
 */
public class MappedStatement {
	private String statementId;
	private Class<?> parameterTypeClass;
	private Class<?> resultTypeClass;
	private String statementType;
	private SqlSource sqlSource;

	public MappedStatement(String statementId, Class<?> parameterTypeClass, Class<?> resultTypeClass,
			String statementType, SqlSource sqlSource) {
		this.statementId = statementId;
		this.parameterTypeClass = parameterTypeClass;
		this.resultTypeClass = resultTypeClass;
		this.statementType = statementType;
		this.sqlSource = sqlSource;
	}

	public String getStatementId() {
		return statementId;
	}

	public void setStatementId(String statementId) {
		this.statementId = statementId;
	}

	public Class<?> getParameterTypeClass() {
		return parameterTypeClass;
	}

	public void setParameterTypeClass(Class<?> parameterTypeClass) {
		this.parameterTypeClass = parameterTypeClass;
	}

	public Class<?> getResultTypeClass() {
		return resultTypeClass;
	}

	public void setResultTypeClass(Class<?> resultTypeClass) {
		this.resultTypeClass = resultTypeClass;
	}

	public String getStatementType() {
		return statementType;
	}

	public void setStatementType(String statementType) {
		this.statementType = statementType;
	}

	public SqlSource getSqlSource() {
		return sqlSource;
	}

	public void setSqlSource(SqlSource sqlSource) {
		this.sqlSource = sqlSource;
	}

}
<file_sep>package mybatis.demo.phase02.mapper;


import mybatis.demo.phase02.po.User;

public interface UserMapper {
	User findUserById(int id);
}
<file_sep>package com.mybatis.sqlsession;

public interface SqlSessionFactory {

	SqlSession openSqlSession();
}
<file_sep>package com.mybatis.sqlnode;

import com.mybatis.sqlsource.DynamicContext;

/**
 * 提供对sql脚本的解析
 * 
 * @author liu
 *
 */
public interface SqlNode {

	void apply(DynamicContext context);
}
<file_sep>package com.mp.entity;

import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;

@Data
@TableName("permission")
@ApiModel("权限类")
public class Permission implements Serializable {

    @ApiModelProperty(name = "id", value = "ID 主键")
    @TableId(type = IdType.AUTO)
    private String id;

    @ApiModelProperty(name = "permissionCode", value = "权限编号")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String permissionCode;

    @ApiModelProperty(name = "permissionName", value = "权限名")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String permissionName;

    @ApiModelProperty(name = "path", value = "映射路径")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String path;

}<file_sep>package com.mybatis.sqlsource;

import com.mybatis.utils.GenericTokenParser;
import com.mybatis.utils.ParameterMappingTokenHandler;

/**
 * 将DynamicSqlSource和RawSqlSource解析成StaticSqlSource
 * StaticSqlSource存储的就是只有?的sql语句以及相应的sql信息
 * 
 * @author liu
 *
 */
public class SqlSourceParser {

	public SqlSource parse(String sqlText) {
		ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler();
		GenericTokenParser tokenParser = new GenericTokenParser("#{", "}", tokenHandler);
		// tokenParser.parse(sqlText)参数是未处理的，返回值是已处理的（没有${}和#{}）
		String sql = tokenParser.parse(sqlText);
		return new StaticSqlSource(sql, tokenHandler.getParameterMappings());
	}

}
<file_sep>package com.mybatis.sqlsource;
/**
 * 解析#{}获取到的参数信息，主要包含参数名称（也就是#{}中的名称和参数类型）
 * @author liu
 *
 */
public class ParameterMapping {

	private String name;
	
	private Class<?> type;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Class<?> getType() {
		return type;
	}

	public void setType(Class<?> type) {
		this.type = type;
	}

	public ParameterMapping(String name) {
		super();
		this.name = name;
	}
	
	
}
<file_sep>package mybatis.demo.phase01.test;

import java.io.InputStream;

import mybatis.demo.phase01.dao.UserDao;
import mybatis.demo.phase01.dao.UserDaoImpl;
import mybatis.demo.phase01.entity.UserEntity;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;


/**
 * 测试入门案例
 * 
 * @author think
 *
 */
public class Test1 {

	private SqlSessionFactory sqlSessionFactory;

	@Before
	public void init() throws Exception{
		// 加载全局配置文件（同时把映射文件也加载了）
		String resource = "phase01/SqlMapConfig.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		// sqlsessionFactory需要通过sqlsessionFactoryBuilder读取全局配置文件信息之后
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}

	@Test
	public void testFindUserById() {
		UserDao dao = new UserDaoImpl(sqlSessionFactory);
		UserEntity user = dao.findUserById(1);
		System.out.println(user);
	}

}
<file_sep>package com.mp;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mp.entity.Role;
import com.mp.entity.User;
import com.mp.mapper.RoleMapper;
import com.mp.mapper.UserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;
import java.util.Set;

/**
 * 保持和main 目录一致
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class MPTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelect() {

        // 此处 null 指的是不用根据参数去查询
        // 可以调用 CRUD 相关的多种方式

        // 1. 查询所有的数据
        List<User> userList = userMapper.selectList(null);
        userList.forEach(user -> System.out.println(user.getName()));

        // 2. 根据 id 删除
        userMapper.deleteById(1);

        // 3. 添加数据
        User user = new User();
        user.setName("老王");
        user.setEmail("<EMAIL>");
        user.setAge(18);
        userMapper.insert(user);

        // 4. 更新数据
        user.setName("老王王");
        user.setEmail("<EMAIL>");
        userMapper.updateById(user);
    }


    @Autowired
    private RoleMapper roleMapper;

    @Test
    public void testUserRole() {

        User user = userMapper.findUserByName("cuihua");
        Set<Role> roles = userMapper.getUserRoles("cuihua");

        for (Role role : roles) {
            role.setPermissions(roleMapper.
                    getRolePermissions(role.getRoleCode()));
        }

        user.setRoles(roles);
        System.out.println(user);
    }

    @Test
    public void testSelect2(){
        IPage<User> page = selectUserPage();
        List<User> userList = page.getRecords();
        userList.forEach(user -> System.out.println("用户：" + user));
    }

    // 查询年龄在 20-30 名字为 cuihua 的用户
    public  IPage<User>  selectUserPage() {
        // 不进行 count sql 优化，解决 MP 无法自动优化 SQL 问题，这时候你需要自己查询 count 部分
        // page.setOptimizeCountSql(false);
        // 当 total 为非 0 时(默认为 0),分页插件不会进行 count 查询
        // 要点!! 分页返回的对象与传入的对象是同一个
        Page page = new Page<User>(1, 20);
        QueryWrapper<User> queryWrapper = new QueryWrapper<User>().between("age", 18, 20).eq("name", "cuihua");
        return userMapper.selectPage(page, queryWrapper);
    }

}<file_sep>package mybatis.demo.phase02.test;

import java.io.InputStream;

import mybatis.demo.phase02.mapper.UserMapper;
import mybatis.demo.phase02.po.User;
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 org.junit.Before;
import org.junit.Test;




/**
 * 测试基础应用案例
 * @date: 2021/6/4
 * @auther: liu
 */
public class Test2 {
	private SqlSessionFactory sqlSessionFactory;

	@Before
	public void init() throws Exception {
		// 加载全局配置文件（同时把映射文件也加载了）
		String resource = "phase02/SqlMapConfig.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		// sqlsessionFactory需要通过sqlsessionFactoryBuilder读取全局配置文件信息之后
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}

	@Test
	public void testFindUserById() {
		//创建UserMapper对象
		SqlSession sqlSession = sqlSessionFactory.openSession();
		//反射获取 mapper JDK 代理实现sqlSession.select
		UserMapper mapper = sqlSession.getMapper(UserMapper.class);
		//调用UserMapper对象的API
		User user = mapper.findUserById(1);
		System.out.println(user);
	}

}
<file_sep>package mybatis.demo.phase01.dao;

import mybatis.demo.phase01.entity.UserEntity;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;


/**
 * 后面会通过反射和代理 实现 sqlSession查询
 * @date: 2021/6/4
 * @auther: liu
 */
public class UserDaoImpl implements UserDao {

	private SqlSessionFactory sqlSessionFactory;

	// 注入sqlSessionFactory
	public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
		this.sqlSessionFactory = sqlSessionFactory;
	}

	@Override
	public UserEntity findUserById(int id) {
		// sqlsessionFactory工厂类去创建sqlsession会话
		SqlSession sqlSession = sqlSessionFactory.openSession();
		// sqlsession接口，开发人员使用它对数据库进行增删改查操作   test 来自xml namespace
		UserEntity user = sqlSession.selectOne("test.findUserById", id);
		return user;
	}

}
<file_sep>package com.mp.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mp.entity.Role;
import com.mp.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.Set;

/**
 * @date: 2021/6/15
 * @auther: liu
 */
public interface UserMapper extends BaseMapper<User> {

    @Select("select * from user where name = #{name}")
    public User findUserByName(String username);

    // 获取用户所拥有的角色
    @Select("select * from  role where rolecode in(select rolecode from userrole where username = #{userName})")
    public Set<Role> getUserRoles(String username);

}<file_sep>package mybatis.demo.phase04.test;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import mybatis.demo.phase04.mapper.UserMapper;
import mybatis.demo.phase04.po.User;
import mybatis.demo.phase04.po.UserExample;
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 org.junit.Before;
import org.junit.Test;

import java.io.InputStream;
import java.util.List;


/**
 * 测试逆向工程代码和PageHelper分页插件案例
 * @date: 2021/6/4
 * @auther: liu
 */
public class Test4 {
	private SqlSessionFactory sqlSessionFactory;

	@Before
	public void init() throws Exception {
		// 加载全局配置文件（同时把映射文件也加载了）
		String resource = "phase04/SqlMapConfig.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		// sqlsessionFactory需要通过sqlsessionFactoryBuilder读取全局配置文件信息之后
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}

	@Test
	public void test() {
		// 创建UserMapper对象
		SqlSession sqlSession = sqlSessionFactory.openSession();
		UserMapper mapper = sqlSession.getMapper(UserMapper.class);
		
		UserExample example = new UserExample();
		List<User> list = mapper.selectByExample(example);
		System.out.println(list);
	}
	@Test
	public void test2() {
		// 创建UserMapper对象
		SqlSession sqlSession = sqlSessionFactory.openSession();
		UserMapper mapper = sqlSession.getMapper(UserMapper.class);

		//拦截器 注入实现分页，把sql 拼接到语句上
		//编写分页代码
		PageHelper.startPage(1, 2);
		
		UserExample example = new UserExample();
		// 此处返回的list实现类不再是ArrayList，而是PageHelper提供的Page对象
		List<User> list = mapper.selectByExample(example);
		System.out.println(list);
		//转为 page 对象
		PageInfo<User> pageInfo = new PageInfo<User>(list);
		System.out.println(pageInfo);
	}

}
<file_sep>package com.mybatis.sqlnode.handler;

import java.util.List;

import org.dom4j.Element;

import com.mybatis.sqlnode.SqlNode;

/**
 * 针对不同子标签进行处理，处理之后，封装到对应的SqlNode对象中
 * 
 * 比如if标签被处理之后，会封装到IfSqlNode对象中
 * 
 * @author liu
 *
 */
public interface NodeHandler {

	/**
	 * 处理非文本节点
	 * @param nodeToHandle 待处理子节点
	 * @param contents 处理之后的节点集合
	 */
	void handleNode(Element nodeToHandle, List<SqlNode> contents);
}
<file_sep>package com.mybatis.config;

import java.util.List;

import org.dom4j.Element;

public class XMLMapperParser {
	private Configuration configuration;

	public XMLMapperParser(Configuration configuration) {
		this.configuration = configuration;
	}

	/**
	 * 
	 * @param rootElement
	 *            <mapper namespace="test">
	 */
	@SuppressWarnings("unchecked")
	public void parse(Element rootElement) {
		String namespace = rootElement.attributeValue("namespace");
		// mapper标签下会包含一些sql片段标签、resultMap标签等，这些标签直接解析处理，而statement相关的标签单独处理
		//此处可以使用XPath语法来进行通配
		List<Element> elements = rootElement.elements("select");
		for (Element selectElement : elements) {
			// select update delete insert 都对应一个statement
			XMLStatementParser scriptParser =  new XMLStatementParser(configuration);
			scriptParser.parseStatement(selectElement,namespace);
		}
		
	}

}
<file_sep>package com.mp;

/**
 * @Date: 2021/6/15
 * @author: liu
 */
/**
 * 如果mapper 不在主方法同级包下，需要MapperScan
 */

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.mp.mapper")
public class AppRun {

    public static void main(String[] args) {
        SpringApplication.run(AppRun.class, args);
    }
}<file_sep>db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/yuque?characterEncoding=utf-8
db.username=root
db.password=<PASSWORD><file_sep>package com.mybatis.config;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

public class Configuration {

	private DataSource dataSource;

	private Map<String, MappedStatement> mappedStatements = new HashMap<String, MappedStatement>();

	public DataSource getDataSource() {
		return dataSource;
	}

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	public void setMappedStatement(String statementId, MappedStatement mappedStatement) {
		mappedStatements.put(statementId, mappedStatement);
	}

	public MappedStatement getMappedStatementById(String statementId) {
		return mappedStatements.get(statementId);
	}
}
<file_sep>package com.mybatis.executor;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.mybatis.config.Configuration;
import com.mybatis.config.MappedStatement;

/**
 * 基本执行器，主要处理一级缓存
 * 
 * @author liu
 *
 */
public abstract class BaseExecutor implements Executor {

	private Map<String, List<Object>> oneLevelCache = new HashMap<String, List<Object>>();

	@SuppressWarnings("unchecked")
	@Override
	public <T> List<T> query(MappedStatement mappedStatement, Configuration configuration, Object param) {
		// 获取带有值的sql语句
		String sql = mappedStatement.getSqlSource().getBoundSql(param).getSql();
		// 从一级缓存去根据sql语句获取查询结果
		List<Object> result = oneLevelCache.get(sql);
		if (result != null) {
			return (List<T>) result;
		}
		// 如果没有结果，则调用相应的处理器去处理
		result = queryFromDataBase(mappedStatement, configuration, param);
		oneLevelCache.put(sql, result);
		return (List<T>) result;
	}

	public abstract List<Object> queryFromDataBase(MappedStatement mappedStatement, Configuration configuration,
			Object param);

}
<file_sep>package com.mp.entity;

import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;

@Data
@TableName("rolePermission")
@ApiModel("角色权限关系类")
public class RolePermission implements Serializable {

    @ApiModelProperty(name = "id", value = "ID 主键")
    @TableId(type = IdType.AUTO)
    private String id;

    @ApiModelProperty(name = "roleCode", value = "角色编号")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer roleCode;

    @ApiModelProperty(name = "permission", value = "权限编号")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String permissionCode;

}<file_sep>package com.mybatis.executor;

import java.util.List;

import com.mybatis.config.Configuration;
import com.mybatis.config.MappedStatement;

/**
 * 处理二级缓存
 * 
 * @author liu
 *
 */
public class CachingExecutor implements Executor {

	// 基本执行器
	private Executor delegate;
	
	public CachingExecutor(Executor delegate) {
		super();
		this.delegate = delegate;
	}

	@Override
	public <T> List<T> query(MappedStatement mappedStatement, Configuration configuration, Object param) {
		// 从二级缓存中根据sql语句获取处理结果（二级缓存怎么存？？？？？）
		// 如果有，则直接返回，如果没有则继续委托给基本执行器去吃力
		return delegate.query(mappedStatement, configuration, param);
	}

}
<file_sep>/*
 Navicat Premium Data Transfer

 Source Server         : 本地
 Source Server Type    : MySQL
 Source Server Version : 50730
 Source Host           : localhost:3306
 Source Schema         : mybatis-plus

 Target Server Type    : MySQL
 Target Server Version : 50730
 File Encoding         : 65001

 Date: 15/06/2021 10:28:45
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission`  (
  `id` bigint(20) NOT NULL COMMENT '主键ID',
  `permissioncode` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '权限编号',
  `permissionname` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '权限名字',
  `path` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '映射路径',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES (1, '111', '随便乱来', NULL);
INSERT INTO `permission` VALUES (2, '222', '偶尔乱来', NULL);
INSERT INTO `permission` VALUES (3, '333', '小小乱来', NULL);

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `id` bigint(20) NOT NULL COMMENT '主键ID',
  `rolecode` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '角色编号',
  `rolename` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '角色名字',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, '001', '总裁');
INSERT INTO `role` VALUES (2, '002', '院长');
INSERT INTO `role` VALUES (3, '003', '组长');

-- ----------------------------
-- Table structure for rolepermission
-- ----------------------------
DROP TABLE IF EXISTS `rolepermission`;
CREATE TABLE `rolepermission`  (
  `id` bigint(20) NOT NULL COMMENT '主键ID',
  `rolecode` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '角色编号',
  `permissioncode` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '权限编号',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of rolepermission
-- ----------------------------
INSERT INTO `rolepermission` VALUES (1, '001', '111');
INSERT INTO `rolepermission` VALUES (2, '002', '222');
INSERT INTO `rolepermission` VALUES (3, '003', '333');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` bigint(20) NOT NULL COMMENT '主键ID',
  `NAME` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '姓名',
  `age` int(11) NULL DEFAULT NULL COMMENT '年龄',
  `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'cuihua', 18, '<EMAIL>');
INSERT INTO `user` VALUES (2, 'huahua', 20, '<EMAIL>');
INSERT INTO `user` VALUES (3, 'cunhua', 28, '<EMAIL>');
INSERT INTO `user` VALUES (4, 'laowang', 21, '<EMAIL>');
INSERT INTO `user` VALUES (5, 'xiaomei', 24, '<EMAIL>');

-- ----------------------------
-- Table structure for userrole
-- ----------------------------
DROP TABLE IF EXISTS `userrole`;
CREATE TABLE `userrole`  (
  `id` bigint(20) NOT NULL COMMENT '主键ID',
  `username` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名',
  `rolecode` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '角色编号',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of userrole
-- ----------------------------
INSERT INTO `userrole` VALUES (1, 'cuihua', '001');
INSERT INTO `userrole` VALUES (2, 'chunhua', '003');
INSERT INTO `userrole` VALUES (3, 'huahua', '002');

SET FOREIGN_KEY_CHECKS = 1;
<file_sep>package com.mybatis.sqlsource;

import com.mybatis.sqlnode.MixedSqlNode;
import com.mybatis.sqlnode.SqlNode;

/**
 * 专门封装和处理带有${}和动态sql标签的sql语句
 * 
 * @author liu
 *
 */
public class DynamicSqlSource implements SqlSource {

	private SqlNode rootSqlNode;

	public DynamicSqlSource(MixedSqlNode rootSqlNode) {
		this.rootSqlNode = rootSqlNode;
	}

	/**
	 * 在sqlsession执行的时候，才调用该方法
	 */
	@Override
	public BoundSql getBoundSql(Object param) {
		//首先先调用SqlNode的处理，将动态标签和${}处理一下
		DynamicContext context = new DynamicContext(param);
		rootSqlNode.apply(context);
		
		// 再调用SqlSourceParser来处理#{}
		SqlSourceParser sqlSourceParser = new SqlSourceParser();
		SqlSource sqlSource = sqlSourceParser.parse(context.getSql());
		return sqlSource.getBoundSql(param);
	}

}
<file_sep>/*
 Navicat Premium Data Transfer

 Source Server         : 本地
 Source Server Type    : MySQL
 Source Server Version : 50730
 Source Host           : localhost:3306
 Source Schema         : yuque

 Target Server Type    : MySQL
 Target Server Version : 50730
 File Encoding         : 65001

 Date: 15/06/2021 10:29:41
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `birthday` datetime(0) NULL DEFAULT NULL,
  `sex` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, '张三', '2021-06-04 12:33:38', '男', '河北');
INSERT INTO `user` VALUES (2, '李四', '2021-06-04 13:01:36', '男', '北京');
INSERT INTO `user` VALUES (3, '王五', '2021-06-04 13:13:19', '男', '上海');

SET FOREIGN_KEY_CHECKS = 1;
<file_sep>package com.mybatis.sqlnode;

import com.mybatis.sqlsource.DynamicContext;
import com.mybatis.utils.OgnlUtils;

public class IfSqlNode implements SqlNode {

	/**
	 * 布尔表达式
	 */
	private String test;

	// 子SqlNode集合
	private SqlNode rootSqlNode;

	public IfSqlNode(String test, SqlNode rootSqlNode) {
		this.test = test;
		this.rootSqlNode = rootSqlNode;
	}

	@Override
	public void apply(DynamicContext context) {
		// 使用Ognl的api来对test标签属性中的布尔表达式进行处理，获取布尔值
		boolean evaluateBoolean = OgnlUtils.evaluateBoolean(test, context.getBindings().get("_parameter"));
		// 如果test标签属性中的表达式判断为true，才进行子节点的处理
		if (evaluateBoolean) {
			rootSqlNode.apply(context);
		}
	}

}
<file_sep>package com.mybatis.sqlnode;

import com.mybatis.sqlsource.DynamicContext;
import com.mybatis.utils.GenericTokenParser;
import com.mybatis.utils.OgnlUtils;
import com.mybatis.utils.SimpleTypeRegistry;
import com.mybatis.utils.TokenHandler;

public class TextSqlNode implements SqlNode {

	private String sqlText;

	public TextSqlNode(String sqlText) {
		this.sqlText = sqlText;
	}

	/**
	 * 要处理${} 比如说username like '%${username}' 入参username=“zhangsan” 处理之后username
	 * like '%zhangsan'
	 */
	@Override
	public void apply(DynamicContext context) {
		TokenHandler tokenHandler = new BindingTokenParser(context);
		GenericTokenParser tokenParser = new GenericTokenParser("${", "}", tokenHandler);
		// tokenParser.parse(sqlText)参数是未处理的，返回值是已处理的（没有${}）
		context.appendSql(tokenParser.parse(sqlText));
	}

	public boolean isDynamic() {
		if (sqlText.indexOf("${") > -1) {
			return true;
		}
		return false;
	}

	private static class BindingTokenParser implements TokenHandler {
		private DynamicContext context;

		public BindingTokenParser(DynamicContext context) {
			this.context = context;
		}

		/**
		 * expression：比如说${username}，那么expression就是username username也就是Ognl表达式
		 */
		@Override
		public String handleToken(String expression) {
			Object paramObject = context.getBindings().get("_parameter");
			if (paramObject == null) {
				// context.getBindings().put("value", null);
				return "";
			} else if (SimpleTypeRegistry.isSimpleType(paramObject.getClass())) {
				// context.getBindings().put("value", paramObject);
				return String.valueOf(paramObject);
			}

			// 使用Ognl api去获取相应的值
			Object value = OgnlUtils.getValue(expression, context.getBindings());
			String srtValue = value == null ? "" : String.valueOf(value);
			return srtValue;
		}

	}

}
<file_sep># yuque
与语雀文档同步，前后端知识库所对应的相关代码
