目录

Life in Flow

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

X

Mybatis

# MyBatis

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

快速入门

引入依赖

 1<?xml version="1.0" encoding="UTF-8"?>
 2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4	<modelVersion>4.0.0</modelVersion>
 5	<parent>
 6		<groupId>org.springframework.boot</groupId>
 7		<artifactId>spring-boot-starter-parent</artifactId>
 8		<version>2.1.2.RELEASE</version>
 9		<relativePath/> <!-- lookup parent from repository -->
10	</parent>
11	<groupId>geektime.spring.data</groupId>
12	<artifactId>mybatis-demo</artifactId>
13	<version>0.0.1-SNAPSHOT</version>
14	<name>mybatis-demo</name>
15	<description>Demo project for Spring Boot</description>
16
17	<properties>
18		<java.version>1.8</java.version>
19	</properties>
20
21	<dependencies>
22		<dependency>
23			<groupId>org.springframework.boot</groupId>
24			<artifactId>spring-boot-starter</artifactId>
25		</dependency>
26		<dependency>
27			<groupId>org.mybatis.spring.boot</groupId>
28			<artifactId>mybatis-spring-boot-starter</artifactId>
29			<version>1.3.2</version>
30		</dependency>
31		<dependency>
32			<groupId>org.joda</groupId>
33			<artifactId>joda-money</artifactId>
34			<version>LATEST</version>
35		</dependency>
36
37		<dependency>
38			<groupId>com.h2database</groupId>
39			<artifactId>h2</artifactId>
40			<scope>runtime</scope>
41		</dependency>
42		<dependency>
43			<groupId>org.projectlombok</groupId>
44			<artifactId>lombok</artifactId>
45			<optional>true</optional>
46		</dependency>
47		<dependency>
48			<groupId>org.springframework.boot</groupId>
49			<artifactId>spring-boot-starter-test</artifactId>
50			<scope>test</scope>
51		</dependency>
52	</dependencies>
53
54	<build>
55		<plugins>
56			<plugin>
57				<groupId>org.springframework.boot</groupId>
58				<artifactId>spring-boot-maven-plugin</artifactId>
59			</plugin>
60		</plugins>
61	</build>
62</project>

application.properties

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

schema.sql

1create table t_coffee (
2    id bigint not null auto_increment,
3    name varchar(255),
4    price bigint not null,
5    create_time timestamp,
6    update_time timestamp,
7    primary key (id)
8);

MoneyTypeHandler

 1package geektime.spring.data.mybatisdemo.handler;
 2
 3import org.apache.ibatis.type.BaseTypeHandler;
 4import org.apache.ibatis.type.JdbcType;
 5import org.joda.money.CurrencyUnit;
 6import org.joda.money.Money;
 7
 8import java.sql.CallableStatement;
 9import java.sql.PreparedStatement;
10import java.sql.ResultSet;
11import java.sql.SQLException;
12
13/**
14 * 在 Money 与 Long 之间转换的 TypeHandler,处理 CNY 人民币
15 */
16public class MoneyTypeHandler extends BaseTypeHandler<Money> {
17    @Override
18    public void setNonNullParameter(PreparedStatement ps, int i, Money parameter, JdbcType jdbcType) throws SQLException {
19        ps.setLong(i, parameter.getAmountMinorLong());
20    }
21
22    @Override
23    public Money getNullableResult(ResultSet rs, String columnName) throws SQLException {
24        return parseMoney(rs.getLong(columnName));
25    }
26
27    @Override
28    public Money getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
29        return parseMoney(rs.getLong(columnIndex));
30    }
31
32    @Override
33    public Money getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
34        return parseMoney(cs.getLong(columnIndex));
35    }
36
37    private Money parseMoney(Long value) {
38        return Money.of(CurrencyUnit.of("CNY"), value / 100.0);
39    }
40}

Coffee

 1package geektime.spring.data.mybatisdemo.model;
 2
 3import lombok.AllArgsConstructor;
 4import lombok.Builder;
 5import lombok.Data;
 6import lombok.NoArgsConstructor;
 7import org.joda.money.Money;
 8
 9import java.util.Date;
10
11@Data
12@AllArgsConstructor
13@NoArgsConstructor
14@Builder
15public class Coffee {
16    private Long id;
17    private String name;
18    private Money price;
19    private Date createTime;
20    private Date updateTime;
21}

CoffeeMapper

 1package geektime.spring.data.mybatisdemo.mapper;
 2
 3import geektime.spring.data.mybatisdemo.model.Coffee;
 4import org.apache.ibatis.annotations.Insert;
 5import org.apache.ibatis.annotations.Mapper;
 6import org.apache.ibatis.annotations.Options;
 7import org.apache.ibatis.annotations.Param;
 8import org.apache.ibatis.annotations.Result;
 9import org.apache.ibatis.annotations.Results;
10import org.apache.ibatis.annotations.Select;
11
12/**
13 * 提示:insert、delete、update返回的是影响的记录的条数,而不是主键。
14 */
15@Mapper
16public interface CoffeeMapper {
17    @Insert("insert into t_coffee (name, price, create_time, update_time)"
18            + "values (#{name}, #{price}, now(), now())")
19    //使用数据库生成的key
20    @Options(useGeneratedKeys = true)
21    int save(Coffee coffee);
22
23    @Select("select * from t_coffee where id = #{id}")
24    @Results({
25            @Result(id = true, column = "id", property = "id"),
26            @Result(column = "create_time", property = "createTime"),
27            // map-underscore-to-camel-case = true 可以实现一样的效果
28            // @Result(column = "update_time", property = "updateTime"),
29    })
30    Coffee findById(@Param("id") Long id);
31}

启动类

 1package geektime.spring.data.mybatisdemo;
 2
 3import geektime.spring.data.mybatisdemo.mapper.CoffeeMapper;
 4import geektime.spring.data.mybatisdemo.model.Coffee;
 5import lombok.extern.slf4j.Slf4j;
 6import org.joda.money.CurrencyUnit;
 7import org.joda.money.Money;
 8import org.mybatis.spring.annotation.MapperScan;
 9import org.springframework.beans.factory.annotation.Autowired;
10import org.springframework.boot.ApplicationArguments;
11import org.springframework.boot.ApplicationRunner;
12import org.springframework.boot.SpringApplication;
13import org.springframework.boot.autoconfigure.SpringBootApplication;
14
15@SpringBootApplication
16@Slf4j
17//扫描所有Mapper映射
18@MapperScan("geektime.spring.data.mybatisdemo.mapper")
19public class MybatisDemoApplication implements ApplicationRunner {
20	@Autowired
21	private CoffeeMapper coffeeMapper;
22
23	public static void main(String[] args) {
24		SpringApplication.run(MybatisDemoApplication.class, args);
25	}
26
27	@Override
28	public void run(ApplicationArguments args) throws Exception {
29		//保存一种咖啡:特浓
30		Coffee c = Coffee.builder().name("espresso")
31				.price(Money.of(CurrencyUnit.of("CNY"), 20.0)).build();
32		int count = coffeeMapper.save(c);
33		log.info("Save {} Coffee: {}", count, c); //除了已设置属性和回填属性,其他的属性不会自动回填
34		//Save 1 Coffee: Coffee(id=1, name=espresso, price=CNY 20.00, createTime=null, updateTime=null)
35
36		//保存一种咖啡:拿铁
37		c = Coffee.builder().name("latte")
38				.price(Money.of(CurrencyUnit.of("CNY"), 25.0)).build();
39		count = coffeeMapper.save(c);
40		log.info("Save {} Coffee: {}", count, c);
41		//Save 1 Coffee: Coffee(id=2, name=latte, price=CNY 25.00, createTime=null, updateTime=null)
42
43		//查询咖啡:根据ID
44		c = coffeeMapper.findById(c.getId());
45		log.info("Find Coffee: {}", c);
46		//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)
47	}
48}

MyBatis Generator (Maven Plugin)

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

  • POJO
  • Mapper 接口类
  • SQL Map XML

pom.xml

 1<?xml version="1.0" encoding="UTF-8"?>
 2<project xmlns="http://maven.apache.org/POM/4.0.0"
 3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5    <modelVersion>4.0.0</modelVersion>
 6
 7    <groupId>com.songguoliang</groupId>
 8    <artifactId>spring-boot-mybatis-generator-mysql</artifactId>
 9    <version>1.0-SNAPSHOT</version>
10
11    <name>spring-boot-mybatis-generator-mysql</name>
12    <description>Spring Boot教程(十五):Spring Boot集成mybatis generator自动生成代码插件</description>
13    <parent>
14        <groupId>org.springframework.boot</groupId>
15        <artifactId>spring-boot-starter-parent</artifactId>
16        <version>2.1.2.RELEASE</version>
17        <relativePath/> <!-- lookup parent from repository -->
18    </parent>
19    <properties>
20        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
21        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
22        <java.version>1.8</java.version>
23    </properties>
24
25    <dependencies>
26        <!-- mybatis ,添加此依赖只为了生成的代码idea不提示错误而已,和mybatis generator没有关系-->
27        <dependency>
28            <groupId>org.mybatis.spring.boot</groupId>
29            <artifactId>mybatis-spring-boot-starter</artifactId>
30            <version>1.3.2</version>
31        </dependency>
32
33        <dependency>
34            <groupId>org.projectlombok</groupId>
35            <artifactId>lombok</artifactId>
36            <version>1.16.10</version>
37            <scope>provided</scope>
38        </dependency>
39        <!-- mysql -->
40        <dependency>
41            <groupId>mysql</groupId>
42            <artifactId>mysql-connector-java</artifactId>
43        </dependency>
44    </dependencies>
45
46    <build>
47        <plugins>
48            <!--mybatis自动生成代码插件-->
49            <plugin>
50                <groupId>org.mybatis.generator</groupId>
51                <artifactId>mybatis-generator-maven-plugin</artifactId>
52                <version>1.3.6</version>
53                <configuration>
54                    <!-- 是否覆盖,true表示会替换生成的JAVA文件,false则不覆盖,为了方便查看效果,此处改成不覆盖 -->
55                    <overwrite>false</overwrite>
56                </configuration>
57                <dependencies>
58                    <!--mysql驱动包-->
59                    <dependency>
60                        <groupId>mysql</groupId>
61                        <artifactId>mysql-connector-java</artifactId>
62                        <version>8.0.13</version>
63                    </dependency>
64                </dependencies>
65            </plugin>
66        </plugins>
67    </build>
68</project>

createTable.sql

 1SET NAMES utf8mb4;
 2SET FOREIGN_KEY_CHECKS = 0;
 3
 4-- ----------------------------
 5-- Table structure for tbl_user
 6-- ----------------------------
 7DROP TABLE IF EXISTS `tbl_user`;
 8CREATE TABLE `tbl_user` (
 9  `user_id` bigint(20) NOT NULL,
10  `user_name` varchar(50) DEFAULT NULL,
11  `user_age` int(11) DEFAULT NULL,
12  PRIMARY KEY (`user_id`)
13) ENGINE=InnoDB DEFAULT CHARSET=utf8;
14
15-- ----------------------------
16-- Records of tbl_user
17-- ----------------------------
18BEGIN;
19INSERT INTO `tbl_user` VALUES (1, '张三', 27);
20INSERT INTO `tbl_user` VALUES (2, '李四', 30);
21INSERT INTO `tbl_user` VALUES (3, '王五', 20);
22COMMIT
23
24SET FOREIGN_KEY_CHECKS = 1;

application.properties

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

src/main/resources/generatorConfig.xml

1

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

MyBatis Generator (Java 程序)

**引入依赖 **

 1<dependency>  
 2 <groupId>org.mybatis.spring.boot</groupId>  
 3 <artifactId>mybatis-spring-boot-starter</artifactId>  
 4 <version>1.3.0</version>  
 5 </dependency>  
 6    
 7 <dependency>  
 8 <groupId>org.mybatis.generator</groupId>  
 9 <artifactId>mybatis-generator-core</artifactId>  
10 <scope>test</scope>  
11 <version>1.3.2</version>  
12 <optional>true</optional>  
13 </dependency>  
14  
15 <dependency>  
16 <groupId>commons-io</groupId>  
17 <artifactId>commons-io</artifactId>  
18 <version>2.5</version>  
19 </dependency>

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

 1package org.mybatis.generator.plugins;  
 2  
 3import org.mybatis.generator.api.IntrospectedTable;  
 4import org.mybatis.generator.api.PluginAdapter;  
 5import org.mybatis.generator.api.dom.java.*;  
 6import org.mybatis.generator.api.dom.xml.Attribute;  
 7import org.mybatis.generator.api.dom.xml.TextElement;  
 8import org.mybatis.generator.api.dom.xml.XmlElement;  
 9  
10import java.util.List;  
11  
12public class AddLimitOffsetPlugin extends PluginAdapter {  
13  
14 public boolean validate(List<String> warnings) {  
15 return true;  
16 }  
17  
18 public boolean modelExampleClassGenerated(TopLevelClass topLevelClass,  
19 IntrospectedTable introspectedTable) {  
20 FullyQualifiedJavaType offsetType = new FullyQualifiedJavaType(  
21 "javax.annotation.Generated");  
22 topLevelClass.addImportedType(offsetType);  
23 topLevelClass.addAnnotation("@Generated(\"" + introspectedTable.getTableConfiguration().getTableName() + "\")");  
24 PrimitiveTypeWrapper integerWrapper = FullyQualifiedJavaType.getIntInstance().getPrimitiveTypeWrapper();  
25    
26 Field limit = new Field();  
27 limit.setName("limit");  
28 limit.setVisibility(JavaVisibility.PRIVATE);  
29 limit.setType(integerWrapper);  
30 topLevelClass.addField(limit);  
31  
32 Method limitSet = new Method();  
33 limitSet.setVisibility(JavaVisibility.PUBLIC);  
34 limitSet.setName("setLimit");  
35 limitSet.addParameter(new Parameter(integerWrapper, "limit"));  
36 limitSet.addBodyLine("this.limit = limit;");  
37 topLevelClass.addMethod(limitSet);  
38  
39 Method limitGet = new Method();  
40 limitGet.setVisibility(JavaVisibility.PUBLIC);  
41 limitGet.setReturnType(integerWrapper);  
42 limitGet.setName("getLimit");  
43 limitGet.addBodyLine("return limit;");  
44 topLevelClass.addMethod(limitGet);  
45  
46 Field offset = new Field();  
47 offset.setName("offset");  
48 offset.setVisibility(JavaVisibility.PRIVATE);  
49 offset.setType(integerWrapper);  
50 topLevelClass.addField(offset);  
51  
52 Method offsetSet = new Method();  
53 offsetSet.setVisibility(JavaVisibility.PUBLIC);  
54 offsetSet.setName("setOffset");  
55 offsetSet.addParameter(new Parameter(integerWrapper, "offset"));  
56 offsetSet.addBodyLine("this.offset = offset;");  
57 topLevelClass.addMethod(offsetSet);  
58  
59 Method offsetGet = new Method();  
60 offsetGet.setVisibility(JavaVisibility.PUBLIC);  
61 offsetGet.setReturnType(integerWrapper);  
62 offsetGet.setName("getOffset");  
63 offsetGet.addBodyLine("return offset;");  
64 topLevelClass.addMethod(offsetGet);  
65  
66 return true;  
67 }  
68  
69 public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(  
70 XmlElement element, IntrospectedTable introspectedTable) {  
71 XmlElement isNotNullElement = new XmlElement("if");  
72 isNotNullElement.addAttribute(new Attribute("test", "limit > 0"));  
73 isNotNullElement.addElement(new TextElement("limit ${limit}"));  
74 element.getElements().add(isNotNullElement);  
75  
76 isNotNullElement = new XmlElement("if");  
77 isNotNullElement.addAttribute(new Attribute("test", "offset > 0"));  
78 isNotNullElement.addElement(new TextElement("offset ${offset}"));  
79 element.getElements().add(isNotNullElement);  
80 return true;  
81 }  
82}

Generator

  1package org.mybatis.generator.plugins;  
  2  
  3import org.apache.commons.io.IOUtils;  
  4import org.junit.Test;  
  5import org.mybatis.generator.api.MyBatisGenerator;  
  6import org.mybatis.generator.api.ProgressCallback;  
  7import org.mybatis.generator.config.Configuration;  
  8import org.mybatis.generator.config.Context;  
  9import org.mybatis.generator.config.xml.ConfigurationParser;  
 10import org.mybatis.generator.internal.DefaultShellCallback;  
 11  
 12import java.io.*;  
 13import java.util.ArrayList;  
 14import java.util.Arrays;  
 15import java.util.List;  
 16import java.util.regex.Matcher;  
 17import java.util.regex.Pattern;  
 18  
 19/**  
 20 * mybatis代码生成插件调用者.  
 21 */  
 22public class Generator {  
 23  
 24 @Test  
 25 public void generate() throws Exception {  
 26 List<String> warnings = new ArrayList<String>();  
 27 boolean overwrite = true;  
 28  
 29 InputStream stream = Generator.class.getClassLoader().getResourceAsStream("generatorConfig.xml");  
 30  
 31 ConfigurationParser cp = new ConfigurationParser(warnings);  
 32 InputStreamReader reader = new InputStreamReader(stream, "utf-8");  
 33 BufferedReader bufferedReader = new BufferedReader(reader);  
 34  
 35 String line = bufferedReader.readLine();  
 36 StringBuffer buffer = new StringBuffer();  
 37 while (line != null) {  
 38 buffer.append(line + "\n");  
 39 line = bufferedReader.readLine();  
 40 }  
 41 String xmlWithParam = buffer.toString();  
 42    
 43 System.out.println("------- xml config begin -------");  
 44 System.out.println(xmlWithParam);  
 45 System.out.println("------- xml config end -------");  
 46  
 47 final Configuration config = cp.parseConfiguration(new ByteArrayInputStream(xmlWithParam.getBytes("utf-8")));  
 48 DefaultShellCallback callback = new DefaultShellCallback(overwrite);  
 49 MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);  
 50 System.out.println("------- generator begin -------");  
 51 ProgressCallback cb = new ProgressCallback() {  
 52 private Pattern SqlProviderPattern = Pattern.compile("\\w*SqlProvider\\.java");  
 53 private List<String> taskNames = new ArrayList();  
 54  
 55 @Override  
 56 public void startTask(String taskName) {  
 57 /*  
 58 * 输出这些信息从而知道 生成那些类了   
 59 * Generating Example class for table demo  
 60 * Generating Primary Key class for table demo   
 61 * Generating Record class for table demo   
 62 * Generating Mapper Interface for table demo   
 63 * Generating SQL Provider for table demo   
 64 * Saving file DemoExample.java  
 65 * Saving file DemoKey.java Saving file Demo.java  
 66 * Saving file DemoMapper.java  
 67 * Saving file DemoSqlProvider.java  
 68 */  
 69 // System.out.println(taskName);  
 70 taskNames.add( taskName);  
 71 }  
 72  
 73 @Override  
 74 public void saveStarted(int arg0) {}  
 75  
 76 @Override  
 77 public void introspectionStarted(int arg0) {}  
 78  
 79 @Override  
 80 public void generationStarted(int arg0) {}  
 81  
 82 @Override  
 83 public void done() {  
 84 for(String taskName : taskNames){  
 85 Matcher matcher = SqlProviderPattern.matcher(taskName);  
 86 if (matcher.find()) {  
 87 final String SqlProviderFilename = matcher.group();  
 88 System.out.println("处理生成文件,selectByExample  增加mysql分页: " + SqlProviderFilename);  
 89 List<Context> contexts = config.getContexts();  
 90 FilenameFilter filter = new FilenameFilter() {  
 91 @Override  
 92 public boolean accept(File dir, String name) {  
 93 return SqlProviderFilename.equalsIgnoreCase(name);  
 94 }  
 95 };  
 96 boolean done = false;  
 97 for (Context ctx : contexts) {  
 98 if(done){  
 99 break;  
100 }  
101 String targetProject = ctx.getJavaClientGeneratorConfiguration().getTargetProject();  
102 String targetPackage = ctx.getJavaClientGeneratorConfiguration().getTargetPackage();  
103 String dir = targetProject.replaceAll("\\.", "\\\\") + "\\" + targetPackage.replaceAll("\\.", "\\\\");  
104 System.out.println(System.getProperty("user.dir") + dir);  
105 File mapperDir = new File(System.getProperty("user.dir"), dir);  
106 File[] files = mapperDir.listFiles(filter);  
107 if (files != null && files.length > 0) {  
108 File sqlProviderFile = files[0];  
109 try {  
110 Generator.addMysqlLimit(sqlProviderFile);  
111 done = true;  
112 } catch (Exception e) {  
113 e.printStackTrace();  
114 }  
115 }  
116 }  
117 if(!done){  
118 System.out.println("转换失败!!!! selectByExample  增加mysql分页: " + SqlProviderFilename);  
119 } else {  
120 System.out.println("转换成功!!!! selectByExample  增加mysql分页: " + SqlProviderFilename);  
121 }  
122 }  
123 }  
124 }  
125  
126 @Override  
127 public void checkCancel() throws InterruptedException {}  
128 };  
129 myBatisGenerator.generate(cb);  
130 for (String warning : warnings) {  
131 System.out.println(warning);  
132 }  
133 System.out.println("------- generator end -------");  
134 // System.out.println(config.getClassPathEntries());  
135 }  
136  
137 private static void addMysqlLimit(File sqlProviderFile) throws Exception {  
138 /*  
139 * 这是自动生成的selectByExample 中的代码片段  
140 if (example != null && example.getOrderByClause() != null) {  
141 ORDER_BY(example.getOrderByClause()); // 整个文件唯一  
142 }  
143    
144 return SQL();  
145    
146 * 将return SQL()改为下面片段即可:  
147  
148 String sqlString = SQL();  
149 if (example != null && example.getLimit() != null) {  
150 sqlString += " limit " + example.getLimit();  
151 }  
152 if (example != null && example.getOffset() != null) {  
153 sqlString += " offset " + example.getOffset();  
154 }  
155 return sqlString;  
156 */  
157 BufferedReader reader = new BufferedReader( new FileReader(sqlProviderFile));  
158 List<String> lines = IOUtils.readLines(reader);  
159 reader.close();  
160 String limitString = "        String sqlString = SQL();\n" +  
161 "        if (example != null && example.getLimit() != null) {\n" +  
162 "            sqlString += \" limit \" + example.getLimit();\n" +  
163 "        }\n" +  
164 "        if (example != null && example.getOffset() != null) {\n" +  
165 "            sqlString += \" offset \" + example.getOffset();\n" +  
166 "        }\n" +  
167 "        return sqlString;";  
168 ArrayList<String> newLines = new ArrayList<String>();  
169  
170    
171 for (int i=0; i< lines.size();++i) {  
172 String line = lines.get(i);  
173 newLines.add(line );  
174 if(line.replaceAll(" ", "") .equalsIgnoreCase("ORDER_BY(example.getOrderByClause());")) {  
175 // 添加下一行大括号和空白行  
176 ++i;  
177 newLines.add(lines.get(i));  
178 ++i;  
179 newLines.add(lines.get(i));  
180    
181 ++i; // 跳过 return SQL();  
182 newLines.addAll(Arrays.asList( limitString.split("\n")));  
183 }  
184 }  
185    
186//        for (String line : newLines) {  
187//            System.out.println(line);  
188//        }  
189 FileOutputStream writer = new FileOutputStream(sqlProviderFile);  
190 IOUtils.writeLines(newLines, "\n",writer,"UTF-8");  
191 writer.close();  
192 }  
193    
194 public static void main(String[] args) throws Exception {  
195 new Generator().generate();  
196 }  
197}

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

 1<?xml version="1.0" encoding="UTF-8" ?>  
 2<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >  
 3<generatorConfiguration>  
 4  
 5 <context id="entmobile" targetRuntime="MyBatis3">  
 6  
 7 <plugin type="org.mybatis.generator.plugins.AddLimitOffsetPlugin" />  
 8 <plugin type="org.mybatis.generator.plugins.ToStringPlugin" />  
 9  
10 <commentGenerator>  
11 <property name="suppressDate" value="true" />  
12 <!-- remove comments -->  
13 <property name="suppressAllComments" value="true" />  
14 </commentGenerator>  
15  
16 <jdbcConnection driverClass="com.mysql.jdbc.Driver"  
17 connectionURL="jdbc:mysql://192.168.31.201:3306/shop?useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false"  
18 userId="root" password="123456" />  
19  
20 <!-- java type resolver -->  
21 <javaTypeResolver>  
22 <property name="forceBigDecimals" value="false" />  
23 </javaTypeResolver>  
24  
25 <!-- gem entity -->  
26 <!-- data -->  
27 <javaModelGenerator targetPackage="com.xdclass.couponapp.domain"  
28 targetProject="./src/main/java">  
29 <property name="enableSubPackages" value="true" />  
30 <property name="trimStrings" value="false" />  
31 </javaModelGenerator>  
32  
33 <!-- //////////////////////////////////////////////////////////////////////////////////////////////////////////////// -->  
34 <!-- gem annotated mapper -->  
35 <!-- data -->  
36 <javaClientGenerator type="ANNOTATEDMAPPER"  
37 targetPackage="com.xdclass.couponapp.mapper"  
38 targetProject="./src/main/java">  
39 <property name="enableSubPackages" value="true" />  
40 </javaClientGenerator>  
41  
42 <table tableName="t_user_coupon">  
43 <generatedKey column="id" sqlStatement="JDBC"/>  
44 </table>  
45  
46 <table tableName="t_coupon">  
47 <generatedKey column="id" sqlStatement="JDBC"/>  
48 </table>  
49  
50 </context>  
51</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 对象的列表中。

启动类

 1package com.songguoliang.springboot;
 2
 3import com.songguoliang.springboot.entity.User;
 4import com.songguoliang.springboot.entity.UserExample;
 5import com.songguoliang.springboot.mapper.UserMapper;
 6import lombok.extern.slf4j.Slf4j;
 7import org.mybatis.spring.annotation.MapperScan;
 8import org.springframework.beans.factory.annotation.Autowired;
 9import org.springframework.boot.ApplicationArguments;
10import org.springframework.boot.ApplicationRunner;
11import org.springframework.boot.SpringApplication;
12import org.springframework.boot.autoconfigure.SpringBootApplication;
13import java.util.List;
14
15@SpringBootApplication
16@MapperScan("com.songguoliang.springboot.mapper")
17@Slf4j
18public class MybatisGeneratorDemoApplication implements ApplicationRunner {
19	@Autowired
20	private UserMapper userMapper;
21
22	public static void main(String[] args) {
23		SpringApplication.run(MybatisGeneratorDemoApplication.class,args);
24	}
25
26	@Override
27	public void run(ApplicationArguments applicationArguments) throws Exception {
28		playWithArtifacts();
29	}
30
31	private void playWithArtifacts() {
32		User tutu = new User().withUserId(4l)
33				.withUserName("秃秃")
34				.withUserAge(30);
35		userMapper.insert(tutu);
36		log.info("User {}", tutu);
37		//User User [Hash = 95322593, userId=4, userName=秃秃, userAge=30]
38
39		UserExample userExample = new UserExample();
40		userExample.createCriteria().andUserAgeEqualTo(30);
41		List<User> users = userMapper.selectByExample(userExample);
42		users.forEach(e -> log.info("selectByExample: {}", e));
43		//selectByExample: User [Hash = 1611221523, userId=2, userName=李四, userAge=30]
44		//selectByExample: User [Hash = 1523510420, userId=4, userName=秃秃, userAge=30]
45
46	}
47}

MyBatis PageHelp

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

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

引入依赖

1<dependency>
2			<groupId>com.github.pagehelper</groupId>
3			<artifactId>pagehelper-spring-boot-starter</artifactId>
4			<version>1.2.10</version>
5		</dependency>

application.properties

1mybatis.type-handlers-package=geektime.spring.data.mybatisdemo.handler
2mybatis.configuration.map-underscore-to-camel-case=true
3
4# pagehelper
5pagehelper.offset-as-page-num=true
6pagehelper.reasonable=true
7pagehelper.page-size-zero=true
8pagehelper.support-methods-arguments=true

data.sql

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

CoffeeMapper

 1package geektime.spring.data.mybatisdemo;
 2
 3import com.github.pagehelper.PageInfo;
 4import geektime.spring.data.mybatisdemo.mapper.CoffeeMapper;
 5import geektime.spring.data.mybatisdemo.model.Coffee;
 6import lombok.extern.slf4j.Slf4j;
 7import org.apache.ibatis.session.RowBounds;
 8import org.mybatis.spring.annotation.MapperScan;
 9import org.springframework.beans.factory.annotation.Autowired;
10import org.springframework.boot.ApplicationArguments;
11import org.springframework.boot.ApplicationRunner;
12import org.springframework.boot.SpringApplication;
13import org.springframework.boot.autoconfigure.SpringBootApplication;
14
15import java.util.List;
16
17@SpringBootApplication
18@Slf4j
19@MapperScan("geektime.spring.data.mybatisdemo.mapper")
20public class MybatisDemoApplication implements ApplicationRunner {
21	@Autowired
22	private CoffeeMapper coffeeMapper;
23
24	public static void main(String[] args) {
25		SpringApplication.run(MybatisDemoApplication.class, args);
26	}
27
28	@Override
29	public void run(ApplicationArguments args) throws Exception {
30		//RowBounds演示:每页大小3,第一页
31		coffeeMapper.findAllWithRowBounds(new RowBounds(1, 3))
32				.forEach(c -> log.info("Page(1) Coffee {}", c));
33		//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)
34		//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)
35		//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)
36
37		//RowBounds演示:每页大小3,第二页
38		coffeeMapper.findAllWithRowBounds(new RowBounds(2, 3))
39				.forEach(c -> log.info("Page(2) Coffee {}", c));
40		//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)
41		//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)
42		log.info("===================");
43
44		//RowBounds演示:每页大小0,第一页(相当于取出所有记录)
45		coffeeMapper.findAllWithRowBounds(new RowBounds(1, 0))
46				.forEach(c -> log.info("Page(1) Coffee {}", c));
47		log.info("===================");
48
49		//findAllWithParam演示:每页大小3,第一页
50		coffeeMapper.findAllWithParam(1, 3)
51				.forEach(c -> log.info("Page(1) Coffee {}", c));
52
53		//PageInfo演示:每页大小3,第二页
54		List<Coffee> list = coffeeMapper.findAllWithParam(2, 3);
55		PageInfo page = new PageInfo(list);
56		log.info("PageInfo: {}", page);
57		//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]}
58	}
59}

PageInfo

 1PageInfo: PageInfo{
 2  pageNum=2, //当前页码
 3  pageSize=3,//每页记录数
 4  size=2,//当前页的记录数
 5  startRow=4,//当前页在数据库中的起始行
 6  endRow=5,//当前页在数据库中的结束行
 7  total=5,//总记录数
 8  pages=2, //总页数
 9  list=Page{
10    count=true,
11    pageNum=2,
12    pageSize=3,
13    startRow=3,
14    endRow=6,
15    total=5,
16    pages=2,
17    reasonable=true,
18    pageSizeZero=true
19  }[
20    Coffee(id=4,
21    name=mocha,
22    price=CNY30.00,
23    createTime=SunJan1214: 17: 45CST2020,
24    updateTime=SunJan1214: 17: 45CST2020),
25    Coffee(id=5,
26    name=macchiato,
27    price=CNY30.00,
28    createTime=SunJan1214: 17: 45CST2020,
29    updateTime=SunJan1214: 17: 45CST2020)
30  ],
31  prePage=1,
32  nextPage=0,
33  isFirstPage=false,
34  isLastPage=true,
35  hasPreviousPage=true,
36  hasNextPage=false,
37  navigatePages=8,
38  navigateFirstPage=1,
39  navigateLastPage=2,
40  navigatepageNums=[
41    1,
42    2
43  ]
44}

作者:Soulboy