目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

Mybatis

# MyBatis

 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

快速入门

引入依赖

<?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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>geektime.spring.data</groupId>
	<artifactId>mybatis-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>mybatis-demo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>
		<dependency>
			<groupId>org.joda</groupId>
			<artifactId>joda-money</artifactId>
			<version>LATEST</version>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

application.properties

# 加载mapper.xml映射文件
#mybatis.mapper-locations=classpath*:mapper/**/*.xml
# 别名的扫描
#mybatis.type-aliases-package=类型别名的包名
# 转换类的包扫描
mybatis.type-handlers-package=geektime.spring.data.mybatisdemo.handler
# 下划线转驼峰
mybatis.configuration.map-underscore-to-camel-case=true

schema.sql

create table t_coffee (
    id bigint not null auto_increment,
    name varchar(255),
    price bigint not null,
    create_time timestamp,
    update_time timestamp,
    primary key (id)
);

MoneyTypeHandler

package geektime.spring.data.mybatisdemo.handler;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 在 Money 与 Long 之间转换的 TypeHandler,处理 CNY 人民币
 */
public class MoneyTypeHandler extends BaseTypeHandler<Money> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Money parameter, JdbcType jdbcType) throws SQLException {
        ps.setLong(i, parameter.getAmountMinorLong());
    }

    @Override
    public Money getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return parseMoney(rs.getLong(columnName));
    }

    @Override
    public Money getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return parseMoney(rs.getLong(columnIndex));
    }

    @Override
    public Money getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return parseMoney(cs.getLong(columnIndex));
    }

    private Money parseMoney(Long value) {
        return Money.of(CurrencyUnit.of("CNY"), value / 100.0);
    }
}

Coffee

package geektime.spring.data.mybatisdemo.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.joda.money.Money;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Coffee {
    private Long id;
    private String name;
    private Money price;
    private Date createTime;
    private Date updateTime;
}

CoffeeMapper

package geektime.spring.data.mybatisdemo.mapper;

import geektime.spring.data.mybatisdemo.model.Coffee;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

/**
 * 提示:insert、delete、update返回的是影响的记录的条数,而不是主键。
 */
@Mapper
public interface CoffeeMapper {
    @Insert("insert into t_coffee (name, price, create_time, update_time)"
            + "values (#{name}, #{price}, now(), now())")
    //使用数据库生成的key
    @Options(useGeneratedKeys = true)
    int save(Coffee coffee);

    @Select("select * from t_coffee where id = #{id}")
    @Results({
            @Result(id = true, column = "id", property = "id"),
            @Result(column = "create_time", property = "createTime"),
            // map-underscore-to-camel-case = true 可以实现一样的效果
            // @Result(column = "update_time", property = "updateTime"),
    })
    Coffee findById(@Param("id") Long id);
}

启动类

package geektime.spring.data.mybatisdemo;

import geektime.spring.data.mybatisdemo.mapper.CoffeeMapper;
import geektime.spring.data.mybatisdemo.model.Coffee;
import lombok.extern.slf4j.Slf4j;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@Slf4j
//扫描所有Mapper映射
@MapperScan("geektime.spring.data.mybatisdemo.mapper")
public class MybatisDemoApplication implements ApplicationRunner {
	@Autowired
	private CoffeeMapper coffeeMapper;

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

	@Override
	public void run(ApplicationArguments args) throws Exception {
		//保存一种咖啡:特浓
		Coffee c = Coffee.builder().name("espresso")
				.price(Money.of(CurrencyUnit.of("CNY"), 20.0)).build();
		int count = coffeeMapper.save(c);
		log.info("Save {} Coffee: {}", count, c); //除了已设置属性和回填属性,其他的属性不会自动回填
		//Save 1 Coffee: Coffee(id=1, name=espresso, price=CNY 20.00, createTime=null, updateTime=null)

		//保存一种咖啡:拿铁
		c = Coffee.builder().name("latte")
				.price(Money.of(CurrencyUnit.of("CNY"), 25.0)).build();
		count = coffeeMapper.save(c);
		log.info("Save {} Coffee: {}", count, c);
		//Save 1 Coffee: Coffee(id=2, name=latte, price=CNY 25.00, createTime=null, updateTime=null)

		//查询咖啡:根据ID
		c = coffeeMapper.findById(c.getId());
		log.info("Find Coffee: {}", c);
		//Find Coffee: Coffee(id=2, name=latte, price=CNY 25.00, createTime=Fri Jan 10 17:36:35 CST 2020, updateTime=Fri Jan 10 17:36:35 CST 2020)
	}
}

MyBatis Generator (Maven Plugin)

 根据数据库表生成相关代码。

  • POJO
  • Mapper 接口类
  • SQL Map XML

pom.xml

<?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>com.songguoliang</groupId>
    <artifactId>spring-boot-mybatis-generator-mysql</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>spring-boot-mybatis-generator-mysql</name>
    <description>Spring Boot教程(十五):Spring Boot集成mybatis generator自动生成代码插件</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- mybatis ,添加此依赖只为了生成的代码idea不提示错误而已,和mybatis generator没有关系-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
            <scope>provided</scope>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!--mybatis自动生成代码插件-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.6</version>
                <configuration>
                    <!-- 是否覆盖,true表示会替换生成的JAVA文件,false则不覆盖,为了方便查看效果,此处改成不覆盖 -->
                    <overwrite>false</overwrite>
                </configuration>
                <dependencies>
                    <!--mysql驱动包-->
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>8.0.13</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
</project>

createTable.sql

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for tbl_user
-- ----------------------------
DROP TABLE IF EXISTS `tbl_user`;
CREATE TABLE `tbl_user` (
  `user_id` bigint(20) NOT NULL,
  `user_name` varchar(50) DEFAULT NULL,
  `user_age` int(11) DEFAULT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of tbl_user
-- ----------------------------
BEGIN;
INSERT INTO `tbl_user` VALUES (1, '张三', 27);
INSERT INTO `tbl_user` VALUES (2, '李四', 30);
INSERT INTO `tbl_user` VALUES (3, '王五', 20);
COMMIT

SET FOREIGN_KEY_CHECKS = 1;

application.properties

spring.datasource.url=jdbc:mysql://192.168.31.201:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis.mapper-locations=classpath:**/*Mapper.xml
mybatis.type-aliases-package=com.songguoliang.springboot.entity
mybatis.configuration.map-underscore-to-camel-case=true
logging.level.com.songguoliang.mybatis=debug

src/main/resources/generatorConfig.xml


运行插件
运行插件
发现生成 POJO、Mapper Dao、Mapper XML 。
发现生成

MyBatis Generator (Java 程序)

**引入依赖 **

<dependency>  
 <groupId>org.mybatis.spring.boot</groupId>  
 <artifactId>mybatis-spring-boot-starter</artifactId>  
 <version>1.3.0</version>  
 </dependency>  
    
 <dependency>  
 <groupId>org.mybatis.generator</groupId>  
 <artifactId>mybatis-generator-core</artifactId>  
 <scope>test</scope>  
 <version>1.3.2</version>  
 <optional>true</optional>  
 </dependency>  
  
 <dependency>  
 <groupId>commons-io</groupId>  
 <artifactId>commons-io</artifactId>  
 <version>2.5</version>  
 </dependency>

引入插件
test/java/org.mybatis.generator.plugins 包下
AddLimitOffsetPlugin

package org.mybatis.generator.plugins;  
  
import org.mybatis.generator.api.IntrospectedTable;  
import org.mybatis.generator.api.PluginAdapter;  
import org.mybatis.generator.api.dom.java.*;  
import org.mybatis.generator.api.dom.xml.Attribute;  
import org.mybatis.generator.api.dom.xml.TextElement;  
import org.mybatis.generator.api.dom.xml.XmlElement;  
  
import java.util.List;  
  
public class AddLimitOffsetPlugin extends PluginAdapter {  
  
 public boolean validate(List<String> warnings) {  
 return true;  
 }  
  
 public boolean modelExampleClassGenerated(TopLevelClass topLevelClass,  
 IntrospectedTable introspectedTable) {  
 FullyQualifiedJavaType offsetType = new FullyQualifiedJavaType(  
 "javax.annotation.Generated");  
 topLevelClass.addImportedType(offsetType);  
 topLevelClass.addAnnotation("@Generated(\"" + introspectedTable.getTableConfiguration().getTableName() + "\")");  
 PrimitiveTypeWrapper integerWrapper = FullyQualifiedJavaType.getIntInstance().getPrimitiveTypeWrapper();  
    
 Field limit = new Field();  
 limit.setName("limit");  
 limit.setVisibility(JavaVisibility.PRIVATE);  
 limit.setType(integerWrapper);  
 topLevelClass.addField(limit);  
  
 Method limitSet = new Method();  
 limitSet.setVisibility(JavaVisibility.PUBLIC);  
 limitSet.setName("setLimit");  
 limitSet.addParameter(new Parameter(integerWrapper, "limit"));  
 limitSet.addBodyLine("this.limit = limit;");  
 topLevelClass.addMethod(limitSet);  
  
 Method limitGet = new Method();  
 limitGet.setVisibility(JavaVisibility.PUBLIC);  
 limitGet.setReturnType(integerWrapper);  
 limitGet.setName("getLimit");  
 limitGet.addBodyLine("return limit;");  
 topLevelClass.addMethod(limitGet);  
  
 Field offset = new Field();  
 offset.setName("offset");  
 offset.setVisibility(JavaVisibility.PRIVATE);  
 offset.setType(integerWrapper);  
 topLevelClass.addField(offset);  
  
 Method offsetSet = new Method();  
 offsetSet.setVisibility(JavaVisibility.PUBLIC);  
 offsetSet.setName("setOffset");  
 offsetSet.addParameter(new Parameter(integerWrapper, "offset"));  
 offsetSet.addBodyLine("this.offset = offset;");  
 topLevelClass.addMethod(offsetSet);  
  
 Method offsetGet = new Method();  
 offsetGet.setVisibility(JavaVisibility.PUBLIC);  
 offsetGet.setReturnType(integerWrapper);  
 offsetGet.setName("getOffset");  
 offsetGet.addBodyLine("return offset;");  
 topLevelClass.addMethod(offsetGet);  
  
 return true;  
 }  
  
 public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(  
 XmlElement element, IntrospectedTable introspectedTable) {  
 XmlElement isNotNullElement = new XmlElement("if");  
 isNotNullElement.addAttribute(new Attribute("test", "limit > 0"));  
 isNotNullElement.addElement(new TextElement("limit ${limit}"));  
 element.getElements().add(isNotNullElement);  
  
 isNotNullElement = new XmlElement("if");  
 isNotNullElement.addAttribute(new Attribute("test", "offset > 0"));  
 isNotNullElement.addElement(new TextElement("offset ${offset}"));  
 element.getElements().add(isNotNullElement);  
 return true;  
 }  
}

Generator

package org.mybatis.generator.plugins;  
  
import org.apache.commons.io.IOUtils;  
import org.junit.Test;  
import org.mybatis.generator.api.MyBatisGenerator;  
import org.mybatis.generator.api.ProgressCallback;  
import org.mybatis.generator.config.Configuration;  
import org.mybatis.generator.config.Context;  
import org.mybatis.generator.config.xml.ConfigurationParser;  
import org.mybatis.generator.internal.DefaultShellCallback;  
  
import java.io.*;  
import java.util.ArrayList;  
import java.util.Arrays;  
import java.util.List;  
import java.util.regex.Matcher;  
import java.util.regex.Pattern;  
  
/**  
 * mybatis代码生成插件调用者.  
 */  
public class Generator {  
  
 @Test  
 public void generate() throws Exception {  
 List<String> warnings = new ArrayList<String>();  
 boolean overwrite = true;  
  
 InputStream stream = Generator.class.getClassLoader().getResourceAsStream("generatorConfig.xml");  
  
 ConfigurationParser cp = new ConfigurationParser(warnings);  
 InputStreamReader reader = new InputStreamReader(stream, "utf-8");  
 BufferedReader bufferedReader = new BufferedReader(reader);  
  
 String line = bufferedReader.readLine();  
 StringBuffer buffer = new StringBuffer();  
 while (line != null) {  
 buffer.append(line + "\n");  
 line = bufferedReader.readLine();  
 }  
 String xmlWithParam = buffer.toString();  
    
 System.out.println("------- xml config begin -------");  
 System.out.println(xmlWithParam);  
 System.out.println("------- xml config end -------");  
  
 final Configuration config = cp.parseConfiguration(new ByteArrayInputStream(xmlWithParam.getBytes("utf-8")));  
 DefaultShellCallback callback = new DefaultShellCallback(overwrite);  
 MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);  
 System.out.println("------- generator begin -------");  
 ProgressCallback cb = new ProgressCallback() {  
 private Pattern SqlProviderPattern = Pattern.compile("\\w*SqlProvider\\.java");  
 private List<String> taskNames = new ArrayList();  
  
 @Override  
 public void startTask(String taskName) {  
 /*  
 * 输出这些信息从而知道 生成那些类了   
 * Generating Example class for table demo  
 * Generating Primary Key class for table demo   
 * Generating Record class for table demo   
 * Generating Mapper Interface for table demo   
 * Generating SQL Provider for table demo   
 * Saving file DemoExample.java  
 * Saving file DemoKey.java Saving file Demo.java  
 * Saving file DemoMapper.java  
 * Saving file DemoSqlProvider.java  
 */  
 // System.out.println(taskName);  
 taskNames.add( taskName);  
 }  
  
 @Override  
 public void saveStarted(int arg0) {}  
  
 @Override  
 public void introspectionStarted(int arg0) {}  
  
 @Override  
 public void generationStarted(int arg0) {}  
  
 @Override  
 public void done() {  
 for(String taskName : taskNames){  
 Matcher matcher = SqlProviderPattern.matcher(taskName);  
 if (matcher.find()) {  
 final String SqlProviderFilename = matcher.group();  
 System.out.println("处理生成文件,selectByExample  增加mysql分页: " + SqlProviderFilename);  
 List<Context> contexts = config.getContexts();  
 FilenameFilter filter = new FilenameFilter() {  
 @Override  
 public boolean accept(File dir, String name) {  
 return SqlProviderFilename.equalsIgnoreCase(name);  
 }  
 };  
 boolean done = false;  
 for (Context ctx : contexts) {  
 if(done){  
 break;  
 }  
 String targetProject = ctx.getJavaClientGeneratorConfiguration().getTargetProject();  
 String targetPackage = ctx.getJavaClientGeneratorConfiguration().getTargetPackage();  
 String dir = targetProject.replaceAll("\\.", "\\\\") + "\\" + targetPackage.replaceAll("\\.", "\\\\");  
 System.out.println(System.getProperty("user.dir") + dir);  
 File mapperDir = new File(System.getProperty("user.dir"), dir);  
 File[] files = mapperDir.listFiles(filter);  
 if (files != null && files.length > 0) {  
 File sqlProviderFile = files[0];  
 try {  
 Generator.addMysqlLimit(sqlProviderFile);  
 done = true;  
 } catch (Exception e) {  
 e.printStackTrace();  
 }  
 }  
 }  
 if(!done){  
 System.out.println("转换失败!!!! selectByExample  增加mysql分页: " + SqlProviderFilename);  
 } else {  
 System.out.println("转换成功!!!! selectByExample  增加mysql分页: " + SqlProviderFilename);  
 }  
 }  
 }  
 }  
  
 @Override  
 public void checkCancel() throws InterruptedException {}  
 };  
 myBatisGenerator.generate(cb);  
 for (String warning : warnings) {  
 System.out.println(warning);  
 }  
 System.out.println("------- generator end -------");  
 // System.out.println(config.getClassPathEntries());  
 }  
  
 private static void addMysqlLimit(File sqlProviderFile) throws Exception {  
 /*  
 * 这是自动生成的selectByExample 中的代码片段  
 if (example != null && example.getOrderByClause() != null) {  
 ORDER_BY(example.getOrderByClause()); // 整个文件唯一  
 }  
    
 return SQL();  
    
 * 将return SQL()改为下面片段即可:  
  
 String sqlString = SQL();  
 if (example != null && example.getLimit() != null) {  
 sqlString += " limit " + example.getLimit();  
 }  
 if (example != null && example.getOffset() != null) {  
 sqlString += " offset " + example.getOffset();  
 }  
 return sqlString;  
 */  
 BufferedReader reader = new BufferedReader( new FileReader(sqlProviderFile));  
 List<String> lines = IOUtils.readLines(reader);  
 reader.close();  
 String limitString = "        String sqlString = SQL();\n" +  
 "        if (example != null && example.getLimit() != null) {\n" +  
 "            sqlString += \" limit \" + example.getLimit();\n" +  
 "        }\n" +  
 "        if (example != null && example.getOffset() != null) {\n" +  
 "            sqlString += \" offset \" + example.getOffset();\n" +  
 "        }\n" +  
 "        return sqlString;";  
 ArrayList<String> newLines = new ArrayList<String>();  
  
    
 for (int i=0; i< lines.size();++i) {  
 String line = lines.get(i);  
 newLines.add(line );  
 if(line.replaceAll(" ", "") .equalsIgnoreCase("ORDER_BY(example.getOrderByClause());")) {  
 // 添加下一行大括号和空白行  
 ++i;  
 newLines.add(lines.get(i));  
 ++i;  
 newLines.add(lines.get(i));  
    
 ++i; // 跳过 return SQL();  
 newLines.addAll(Arrays.asList( limitString.split("\n")));  
 }  
 }  
    
//        for (String line : newLines) {  
//            System.out.println(line);  
//        }  
 FileOutputStream writer = new FileOutputStream(sqlProviderFile);  
 IOUtils.writeLines(newLines, "\n",writer,"UTF-8");  
 writer.close();  
 }  
    
 public static void main(String[] args) throws Exception {  
 new Generator().generate();  
 }  
}

generatorConfig.xml 配置文件
test/resources/generatorConfig.xml

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >  
<generatorConfiguration>  
  
 <context id="entmobile" targetRuntime="MyBatis3">  
  
 <plugin type="org.mybatis.generator.plugins.AddLimitOffsetPlugin" />  
 <plugin type="org.mybatis.generator.plugins.ToStringPlugin" />  
  
 <commentGenerator>  
 <property name="suppressDate" value="true" />  
 <!-- remove comments -->  
 <property name="suppressAllComments" value="true" />  
 </commentGenerator>  
  
 <jdbcConnection driverClass="com.mysql.jdbc.Driver"  
 connectionURL="jdbc:mysql://192.168.31.201:3306/shop?useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false"  
 userId="root" password="123456" />  
  
 <!-- java type resolver -->  
 <javaTypeResolver>  
 <property name="forceBigDecimals" value="false" />  
 </javaTypeResolver>  
  
 <!-- gem entity -->  
 <!-- data -->  
 <javaModelGenerator targetPackage="com.xdclass.couponapp.domain"  
 targetProject="./src/main/java">  
 <property name="enableSubPackages" value="true" />  
 <property name="trimStrings" value="false" />  
 </javaModelGenerator>  
  
 <!-- //////////////////////////////////////////////////////////////////////////////////////////////////////////////// -->  
 <!-- gem annotated mapper -->  
 <!-- data -->  
 <javaClientGenerator type="ANNOTATEDMAPPER"  
 targetPackage="com.xdclass.couponapp.mapper"  
 targetProject="./src/main/java">  
 <property name="enableSubPackages" value="true" />  
 </javaClientGenerator>  
  
 <table tableName="t_user_coupon">  
 <generatedKey column="id" sqlStatement="JDBC"/>  
 </table>  
  
 <table tableName="t_coupon">  
 <generatedKey column="id" sqlStatement="JDBC"/>  
 </table>  
  
 </context>  
</generatorConfiguration>

运行 Generator 生成逆向工程

example 简介

 example 是 mybatis-generator 生成的工具包,用于构建查询语句。

  • Example 类可以用来生成一个几乎无限的 where 子句。
  • Example 类包含一个内部静态类 Criteria 包含一个用 anded 组合在 where 子句中的条件列表。 Example 类包含一个 List 属性,所有内部类 Criteria 中的子句会用 ored 组合在一起。 使用不同属性的 Criteria 类允许您生成无限类型的 where 子句。
  • 创建 Criteria 对象 可以使用 Example 类中的 createCriteria() 或者 or() . 如果 Criteria 对象是用 createCriteria() 创建的,它会自动为 List 属性添加一个 Criteria 对象 - 这使得它更容易写一个简单的 where 子句, 如果您不需要 or 或者其他几个子句组合的话。 用 or(Criteria criteria) 方法创建 Criteria 对象, 方法里的 criteria 对象会被添加进 Criteria 对象的列表中。

启动类

package com.songguoliang.springboot;

import com.songguoliang.springboot.entity.User;
import com.songguoliang.springboot.entity.UserExample;
import com.songguoliang.springboot.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.List;

@SpringBootApplication
@MapperScan("com.songguoliang.springboot.mapper")
@Slf4j
public class MybatisGeneratorDemoApplication implements ApplicationRunner {
	@Autowired
	private UserMapper userMapper;

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

	@Override
	public void run(ApplicationArguments applicationArguments) throws Exception {
		playWithArtifacts();
	}

	private void playWithArtifacts() {
		User tutu = new User().withUserId(4l)
				.withUserName("秃秃")
				.withUserAge(30);
		userMapper.insert(tutu);
		log.info("User {}", tutu);
		//User User [Hash = 95322593, userId=4, userName=秃秃, userAge=30]

		UserExample userExample = new UserExample();
		userExample.createCriteria().andUserAgeEqualTo(30);
		List<User> users = userMapper.selectByExample(userExample);
		users.forEach(e -> log.info("selectByExample: {}", e));
		//selectByExample: User [Hash = 1611221523, userId=2, userName=李四, userAge=30]
		//selectByExample: User [Hash = 1523510420, userId=4, userName=秃秃, userAge=30]

	}
}

MyBatis PageHelp

 PageHelper-Spring-Boot-Starter 帮助你集成分页插件到 Spring Boot。
官网

  • 支持多种数据库
  • 支持多种分页方式
  • SpringBoot 支持

引入依赖

<dependency>
			<groupId>com.github.pagehelper</groupId>
			<artifactId>pagehelper-spring-boot-starter</artifactId>
			<version>1.2.10</version>
		</dependency>

application.properties

mybatis.type-handlers-package=geektime.spring.data.mybatisdemo.handler
mybatis.configuration.map-underscore-to-camel-case=true

# pagehelper
pagehelper.offset-as-page-num=true
pagehelper.reasonable=true
pagehelper.page-size-zero=true
pagehelper.support-methods-arguments=true

data.sql

insert into t_coffee (name, price, create_time, update_time) values ('espresso', 2000, now(), now());
insert into t_coffee (name, price, create_time, update_time) values ('latte', 2500, now(), now());
insert into t_coffee (name, price, create_time, update_time) values ('capuccino', 2500, now(), now());
insert into t_coffee (name, price, create_time, update_time) values ('mocha', 3000, now(), now());
insert into t_coffee (name, price, create_time, update_time) values ('macchiato', 3000, now(), now());

CoffeeMapper

package geektime.spring.data.mybatisdemo;

import com.github.pagehelper.PageInfo;
import geektime.spring.data.mybatisdemo.mapper.CoffeeMapper;
import geektime.spring.data.mybatisdemo.model.Coffee;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.RowBounds;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.List;

@SpringBootApplication
@Slf4j
@MapperScan("geektime.spring.data.mybatisdemo.mapper")
public class MybatisDemoApplication implements ApplicationRunner {
	@Autowired
	private CoffeeMapper coffeeMapper;

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

	@Override
	public void run(ApplicationArguments args) throws Exception {
		//RowBounds演示:每页大小3,第一页
		coffeeMapper.findAllWithRowBounds(new RowBounds(1, 3))
				.forEach(c -> log.info("Page(1) Coffee {}", c));
		//Page(1) Coffee Coffee(id=1, name=espresso, price=CNY 20.00, createTime=Sun Jan 12 14:17:45 CST 2020, updateTime=Sun Jan 12 14:17:45 CST 2020)
		//Page(1) Coffee Coffee(id=2, name=latte, price=CNY 25.00, createTime=Sun Jan 12 14:17:45 CST 2020, updateTime=Sun Jan 12 14:17:45 CST 2020)
		//Page(1) Coffee Coffee(id=3, name=capuccino, price=CNY 25.00, createTime=Sun Jan 12 14:17:45 CST 2020, updateTime=Sun Jan 12 14:17:45 CST 2020)

		//RowBounds演示:每页大小3,第二页
		coffeeMapper.findAllWithRowBounds(new RowBounds(2, 3))
				.forEach(c -> log.info("Page(2) Coffee {}", c));
		//Page(2) Coffee Coffee(id=4, name=mocha, price=CNY 30.00, createTime=Sun Jan 12 14:17:45 CST 2020, updateTime=Sun Jan 12 14:17:45 CST 2020)
		//Page(2) Coffee Coffee(id=5, name=macchiato, price=CNY 30.00, createTime=Sun Jan 12 14:17:45 CST 2020, updateTime=Sun Jan 12 14:17:45 CST 2020)
		log.info("===================");

		//RowBounds演示:每页大小0,第一页(相当于取出所有记录)
		coffeeMapper.findAllWithRowBounds(new RowBounds(1, 0))
				.forEach(c -> log.info("Page(1) Coffee {}", c));
		log.info("===================");

		//findAllWithParam演示:每页大小3,第一页
		coffeeMapper.findAllWithParam(1, 3)
				.forEach(c -> log.info("Page(1) Coffee {}", c));

		//PageInfo演示:每页大小3,第二页
		List<Coffee> list = coffeeMapper.findAllWithParam(2, 3);
		PageInfo page = new PageInfo(list);
		log.info("PageInfo: {}", page);
		//PageInfo: PageInfo{pageNum=2, pageSize=3, size=2, startRow=4, endRow=5, total=5, pages=2, list=Page{count=true, pageNum=2, pageSize=3, startRow=3, endRow=6, total=5, pages=2, reasonable=true, pageSizeZero=true}[Coffee(id=4, name=mocha, price=CNY 30.00, createTime=Sun Jan 12 14:17:45 CST 2020, updateTime=Sun Jan 12 14:17:45 CST 2020), Coffee(id=5, name=macchiato, price=CNY 30.00, createTime=Sun Jan 12 14:17:45 CST 2020, updateTime=Sun Jan 12 14:17:45 CST 2020)], prePage=1, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true, hasNextPage=false, navigatePages=8, navigateFirstPage=1, navigateLastPage=2, navigatepageNums=[1, 2]}
	}
}

PageInfo

PageInfo: PageInfo{
  pageNum=2, //当前页码
  pageSize=3,//每页记录数
  size=2,//当前页的记录数
  startRow=4,//当前页在数据库中的起始行
  endRow=5,//当前页在数据库中的结束行
  total=5,//总记录数
  pages=2, //总页数
  list=Page{
    count=true,
    pageNum=2,
    pageSize=3,
    startRow=3,
    endRow=6,
    total=5,
    pages=2,
    reasonable=true,
    pageSizeZero=true
  }[
    Coffee(id=4,
    name=mocha,
    price=CNY30.00,
    createTime=SunJan1214: 17: 45CST2020,
    updateTime=SunJan1214: 17: 45CST2020),
    Coffee(id=5,
    name=macchiato,
    price=CNY30.00,
    createTime=SunJan1214: 17: 45CST2020,
    updateTime=SunJan1214: 17: 45CST2020)
  ],
  prePage=1,
  nextPage=0,
  isFirstPage=false,
  isLastPage=true,
  hasPreviousPage=true,
  hasNextPage=false,
  navigatePages=8,
  navigateFirstPage=1,
  navigateLastPage=2,
  navigatepageNums=[
    1,
    2
  ]
}

作者:Soulboy