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&characterEncoding=UTF-8&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
]
}