环境搭建
- 创建数据库
- 创建对应实体类User
- 导入依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
复制代码
- 配置属性
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8
复制代码
- 实现接口
//BaseMapper 默认提供了一系列的增删改查的基础理操作
@Repository
public interface UserMapper extends BaseMapper<User> {
}
复制代码
- 修改启动类
@SpringBootApplication
@MapperScan("com.mapper") //指向mapper所在的包
public class plus_01Application {
public static void main(String[] args) {
SpringApplication.run(plus_01Application.class,args);
}
}
复制代码
- 配置日志
在原有的配置文件中添加以下代码即可
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
复制代码
测试
class DemoApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
//List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
//queryWrapper 实体对象封装操作类(可以为 null)
//selectList根据传入的条件进行查询,这里传入null即查询全部
List<User> users = userMapper.selectList(null);
for (User user:users)
System.out.println(user);
}
}
复制代码
结果
CRUD与拓展
插入操作
@Test
public void testInsert(){
User u=new User();
u.setId(7);
u.setEmail("baomidou.com");
u.setName("jony");
userMapper.insert(u);
}
复制代码
主键生成策略
分布式系统唯一id生成: www.cnblogs.com/haoxinyue/p…
雪花算法:
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。具体实现的代码可以参看github.com/twitter/sno…
public enum IdType {
/**
* 数据库ID自增
* <p>该类型请确保数据库设置了 ID自增 否则无效</p>
*/
AUTO(0),
/**
* 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
*/
NONE(1),
/**
* 用户输入ID
* <p>该类型可以通过自己注册自动填充插件进行填充</p>
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 分配ID (主键类型为number或string),
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
*
* @since 3.3.0
*/
ASSIGN_ID(3),
/**
* 分配UUID (主键类型为 string)
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
*/
ASSIGN_UUID(4),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_ID}
*/
@Deprecated
ID_WORKER(3),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_ID}
*/
@Deprecated
ID_WORKER_STR(3),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_UUID}
*/
@Deprecated
UUID(4);
private final int key;
IdType(int key) {
this.key = key;
}
}
复制代码
/*
* 主键类型
* {@link IdType}
IdType type() default IdType.NONE;*/
//我们通过@TableId来指定主键的生成方式,设置主键自增需要在数据库也配置为自增
@TableId(type = IdType.AUTO)
private int id;
复制代码
更新操作
update 没有进行动态sql,解决在文末
MybatisPlus会自动帮我们进行sql的拼接
@Test
public void testUpdate(){
User u=new User();
u.setId(7);
u.setEmail("@baomidou.com");
//参数是对象类型的
//int updateById(@Param(Constants.ENTITY) T entity);
userMapper.updateById(u);
}
复制代码
@Test
public void testUpdate(){
User u=new User();
u.setId(7);
u.setName("hony");
u.setEmail("@baomidou.com");
userMapper.updateById(u);
}
复制代码
自动填充
数据库级别
在数据库增加两列
修改对应的实体类
public class User {
@TableId(type = IdType.AUTO)
private int id;
private Integer age;
private String name;
private String email;
//增加对应字段
private Date createTime;
private Date updateTime;
}
复制代码
代码级别
- 添加注解@TableField
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
@TableId(type = IdType.AUTO)
private int id;
private Integer age;
private String name;
private String email;
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE )
private Date updateTime;
@Version
private Integer version;
}
复制代码
/**
* 字段自动填充策略
* <p>
* 在对应模式下将会忽略 insertStrategy 或 updateStrategy 的配置,等于断言该字段必有值
*/
FieldFill fill() default FieldFill.DEFAULT;
复制代码
public enum FieldFill {
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入时填充字段
*/
INSERT,
/**
* 更新时填充字段
*/
UPDATE,
/**
* 插入和更新时填充字段
*/
INSERT_UPDATE
}
复制代码
- 处理策略
@Component //需要注册到ioc容器中
public class MymetObejectHandler implements MetaObjectHandler {
//插入时的策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("inserte fill start ---------");
//数据库字段,填充的值
setFieldValByName("createTime",new Date(),metaObject) ;
setFieldValByName("updateTime",new Date(),metaObject) ;
}
//更新时的策略
@Override
public void updateFill(MetaObject metaObject) {
log.info("update fill start ---------");
setFieldValByName("updateTime",new Date(),metaObject) ;
}
}
复制代码
乐观锁
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
- 在数据库添加字段version
2. 更新实体类,添加对应属性
@Version
private Integer version;
复制代码
- 注册到ioc容器中
@Configuration
@MapperScan("com.mapper")
@EnableTransactionManagement //开启事务支持
public class MyConfig {
@Bean
public MybatisPlusInterceptor optimisticLockerInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
复制代码
- 测试
@Test
public void testop2(){
User u=userMapper.selectById(1);
u.setEmail("t");
User u2=userMapper.selectById(1);
u2.setId(7);
u2.setEmail("T2");
userMapper.updateById(u2);
userMapper.updateById(u);
}
// Email被改为T2,而不是t
复制代码
查询操作
/**
* 根据 ID 查询
*
* @param id 主键ID
*/
T selectById(Serializable id);
/*================================================*/
/**
* 查询(根据ID 批量查询)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/*================================================*/
/**
* 查询(根据 columnMap 条件)
*
* @param columnMap 表字段 map 对象
*/
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
复制代码
Map查询会根据我们传入的参数进行动态sql
@Test
public void testSelect(){
//通过id进行查询
userMapper.selectById(1);
//根据传入的数组进行批量查询
userMapper.selectBatchIds(Arrays.asList(2,3,4));
Map<String, Object> Map = new HashMap<>();
Map.put("name","jony");
Map.put("age","18");
userMapper.selectByMap(Map);
}
复制代码
分页查询
使用MybatisPlus自带的分页插件进行分页
- 注册插件
public class MyConfig {
@Bean
public MybatisPlusInterceptor optimisticLockerInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}}
复制代码
- 使用
/**
* 分页构造函数
*
* @param current 当前页
* @param size 每页显示条数
*/
public Page(long current, long size) {
this(current, size, 0);
}
/*=================================================*/
/**
* 根据 entity 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
复制代码
@Test
public void testSelect(){
// 5条记录一页,显示第二页
Page<User> page = new Page<>(2,5);
userMapper.selectPage(page,null);
page.getRecords().forEach(System.out::println );
}
复制代码
删除操作
与上述的查询操作大同小异
/**
* 根据 ID 删除
*
* @param id 主键ID
*/
int deleteById(Serializable id);
/*===============================================*/
/**
* 根据 columnMap 条件,删除记录
*
* @param columnMap 表字段 map 对象
*/
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/*===============================================*/
/**
* 删除(根据ID 批量删除)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
复制代码
@Test
public void testDelete(){
userMapper.deleteById(8);
userMapper.deleteBatchIds(Arrays.asList(2,3,4));
Map<String, Object> Map = new HashMap<>();
Map.put("name","jony");
Map.put("age","18");
userMapper.deleteByMap(Map);
}
复制代码
逻辑删除(只对自动注入的sql起效:)
- 在数据库中添加字段deleted(名称固定),设置默认值为0
2. 修改实体类并添加对应注解
注意: 低版本需要注册组件
@Bean//逻辑删除组件
public ISqlInjector sqlInjector(){
return new LogicSqlInjector();
}
复制代码
- 修改配置文件
由于我们数据库设置deleted默认为0,所以这里我们设置logic-not-delete-value: 0 为不删除的条件。
logic-delete-value: 1为已删除的标志。
4. 测试
当我们删除id为8的用户时,执行的其实是更新操作
再次查询id为8的用户
性能分析插件
Mybatis-Plus在3.2版本以上移除了PerformanceInterceptor
- 导入依赖
该插件有性能损耗,不建议生产环境使用。
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>最新版本</version>
</dependency>
复制代码
- 修改原本的数据库属性
spring:
datasource:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
username: root
password: 123456
url: jdbc:p6spy:mysql://localhost:3306/mybatis_plus?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8
复制代码
- 添加配置文件spy.properties
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
复制代码
条件构造器wrapper
测试1 NotNull,lt
@Test
public void TestWrapper(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//姓名邮箱不为空的18岁
wrapper.isNotNull("name")
.isNotNull("email")
.lt("age",18);
userMapper.selectList(wrapper);
}
复制代码
测试2 eq
@Test
public void TestWrapper(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name","jony");
userMapper.selectOne(wrapper);
}
复制代码
测试3 between
@Test
public void TestWrapper(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age",18,20);
userMapper.selectCount(wrapper);
}
复制代码
测试4 like 模糊查询
@Test
public void TestWrapper(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.notLike("name","j")//%j%
.likeLeft("email","t");// %t
userMapper.selectList(wrapper).forEach(System.out::println);
}
复制代码
测试5 子查询
@Test
public void TestWrapper(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.inSql("id","select id from user where age>18");
userMapper.selectList(wrapper).forEach(System.out::println);
}
复制代码
测试6 排序
@Test
public void TestWrapper(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//Desc降序
wrapper.orderByDesc("id");
userMapper.selectList(wrapper).forEach(System.out::println);
}
复制代码
代码生成器
- 导入依赖
MyBatis-Plus 从 3.0.3 之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.2</version>
</dependency>
// 模板引擎依赖,可更改,更改后需要在 AutoGenerator 中 设置模板引擎
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
复制代码
- 编写代码
@Test
public void auto(){
AutoGenerator mpg=new AutoGenerator();
//全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");//设置输出地址
globalConfig.setAuthor("lin");//设置作者
globalConfig.setOpen(false);//设置生成完毕后是否打开文件夹
globalConfig.setFileOverride(false);//是否覆盖
globalConfig.setServiceName("%sService");//去除service的I前缀
globalConfig.setIdType(IdType.ASSIGN_ID);// 指定生成的主键的ID类型
globalConfig.setDateType(DateType.ONLY_DATE);//只使用 java.util.date 代替
globalConfig.setSwagger2(true);//使用swagger
mpg.setGlobalConfig(globalConfig);
//配置数据库
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=UTC");
dataSourceConfig.setDriverName("com.mysql.jdbc.Driver");
dataSourceConfig.setUsername("root");
dataSourceConfig.setPassword("123456");
dataSourceConfig.setDbType(DbType.MYSQL);
mpg.setDataSource(dataSourceConfig);
//包设置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setModuleName("blog");
packageConfig.setParent("com");
packageConfig.setEntity("entity");
packageConfig.setService("service");
packageConfig.setMapper("mapper");
packageConfig.setController("controller");
mpg.setPackageInfo(packageConfig);
// 策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("user");//设置映射的表名
strategyConfig.setNaming(NamingStrategy.underline_to_camel);//下划线转驼峰命名
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);//下划线转驼峰命名
strategyConfig.setSuperEntityClass("指定父类实体");//指定继承的父类
strategyConfig.setEntityLombokModel(true);//自动Lombok
strategyConfig.setRestControllerStyle(true);//restful风格
strategyConfig.setLogicDeleteFieldName("deleted");//设置逻辑删除字段
//自动填充
TableFill gmtCreat=new TableFill("gmt_creat", FieldFill.INSERT);//字段,条件
TableFill gmtModified=new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);
ArrayList<TableFill> arrayList= new ArrayList<>();
arrayList.add(gmtCreat);
arrayList.add(gmtModified);
strategyConfig.setTableFillList(arrayList);
//乐观锁
strategyConfig.setVersionFieldName("version");//指定字段
//驼峰转连字符/managerUserActionHistory -> /manager-user-action-history
strategyConfig.setControllerMappingHyphenStyle(true);
strategyConfig.setRestControllerStyle(true);//开启Restful
mpg.setStrategy(strategyConfig);
mpg.execute();
}
复制代码
测试结果:
debug
1. update 没有进行动态sql
原因:
-
int是基本数据类型,Integer是引用数据类型;
-
Ingeter是int的包装类,int的初值为0,Ingeter的初值为null;
对象中的数字被定义为int类型如果没赋值默认为0,然后mybatisplus就认为是你赋值为0而进行update,而不是对sql进行动态拼接。