MybatisPlus
一、注解
1.@TableName
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
public @interface TableName {
String value() default "";
String schema() default "";
boolean keepGlobalPrefix() default false;
String resultMap() default "";
boolean autoResultMap() default false;
String[] excludeProperty() default {};
}
标注在实体类上,声明实体类对应的表
excludeProperty
设置需要排除的属性名(不是表中的字段)
2.@TableId
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableId {
String value() default "";
IdType type() default IdType.NONE;
}
标注在实体类的字段上,声明主键
type
表示主键策略
public enum IdType {
AUTO(0), // 主键自增,数据库中主键必须为自增才生效
NONE(1), // 未设置主键,默认使用ASSIGN_ID(雪花算法)
INPUT(2), // insert前自己设置主键
ASSIGN_ID(3), // 使用雪花算法分配主键
ASSIGN_UUID(4); // 使用UUID分配主键
private final int key;
private IdType(int key) {
this.key = key;
}
public int getKey() {
return this.key;
}
}
3.@TableField
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableField {
String value() default "";
boolean exist() default true;
String condition() default "";
String update() default "";
FieldStrategy insertStrategy() default FieldStrategy.DEFAULT;
FieldStrategy updateStrategy() default FieldStrategy.DEFAULT;
FieldStrategy whereStrategy() default FieldStrategy.DEFAULT;
FieldFill fill() default FieldFill.DEFAULT;
boolean select() default true;
boolean keepGlobalFormat() default false;
String property() default "";
JdbcType jdbcType() default JdbcType.UNDEFINED;
Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;
boolean javaType() default false;
String numericScale() default "";
}
标注实体类的字段上,表示表中的字段非主键
exists
设置该属性是否为数据库的字段
fill
设置字段自动填充策略
public enum FieldFill {
DEFAULT, // 默认不处理
INSERT, // 插入时填充字段
UPDATE, // 更新时填充字段
INSERT_UPDATE; // 插入和更新时填充字段
private FieldFill() {
}
}
4.@Version
标注实体类的字段上,表示乐观锁字段
5.@TableLogic
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableLogic {
String value() default ""; //逻辑未删除值
String delval() default ""; //逻辑删除值
}
标注实体类的字段上,表示逻辑删除字段
二、主键策略
IdType
:
值 | 描述 |
---|---|
AUTO | 主键自增,数据库中主键必须为自增才生效 |
NONE | **(默认)**未设置主键,相当于ASSIGN_ID(雪花算法) |
INPUT | insert前自己设置主键 |
ASSIGN_ID | 使用雪花算法分配主键 |
ASSIGN_UUID | 使用UUID分配主键 |
pojo类:
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {
@TableId // 相等于@TableId(type = IdType.ASSIGN_ID)
private String username;
private String password;
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@Version
private Long version;
}
@Autowired
private UserMapper userMapper;
/**
* 插入用户
*/
@Test
void contextLoads1() {
User user = new User();
user.setPassword("123");
int insert = userMapper.insert(user);
System.out.println(insert == 1 ? "添加用户成功!" : "添加用户失败!!!");
}
当主键策略为IdType.NONE
,插入数据时未设置主键,则使用雪花算法生成主键,若设置了主键则根据设置的值插入
修改主键策略为IdType.AUTO
:
@TableId(type = IdType.AUTO)
private String username;
数据库对应的User表的username字段不是自增长的,所以报错
修改主键策略为IdType.INPUT
:
@TableId(type = IdType.INPUT)
private String username;
插入数据时需要自己设置主键
修改主键策略为IdType.ASSIGN_UUID
:
@TableId(type = IdType.ASSIGN_UUID)
private String username;
当主键策略为IdType.ASSIGN_UUID
,插入数据时未设置主键,则使用UUID算法生成主键,若设置了主键则根据设置的值插入
三、条件构造器Wrapper
AbstractWrapper
函数:
函数名 | 解释 |
---|---|
allEq | 全部属性相等或个别属性为空 |
eq | 等于 |
ne | 不等于 |
gt | 大于 |
ge | 大于等于 |
lt | 小于 |
le | 小于等于 |
between | 在两值之间 |
notBetween | 不在两值之间 |
like | 包括某个值(%值%) |
notLike | 不包括某个值 |
likeLeft | 以某个值结尾(%值) |
likeRight | 以某个值开头(值%) |
isNull | 字段为空 |
isNotNull | 字段不为空 |
groupBy | 分组 |
orderByAsc | 升序排序 |
orderByDesc | 降序排序 |
四、自动填充
在实体类逻辑删除字段上标记@TableField
,设置fill
属性
public enum FieldFill {
DEFAULT, //默认不处理
INSERT, //插入时填充字段
UPDATE, //更新时填充字段
INSERT_UPDATE; //插入和更新时填充字段
private FieldFill() {
}
}
自动填充创建时间createTime
、修改时间updateTime
和是否删除deleted
初始值0
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
向容器中注入组件MetaObjectHandler
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
//插入数据时间初始化
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
//插入数据deleted初始值0
this.setFieldValByName("deleted", 0, metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
//修改数据更新时间
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
添加用户:
修改用户信息:
五、逻辑删除
在实体类逻辑删除字段上标记@TableLogic
/**
* 逻辑删除
*/
@Test
void contextLoads2() {
User user = new User();
user.setUsername("admin");
int delete = userMapper.deleteById(user);
System.out.println(delete == 1 ? "删除用户成功!" : "删除用户失败!!!");
}
六、分页插件
配置分页插件(拦截器实现):
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//分页
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
/**
* 分页
*/
@Test
void contextLoads5() {
Page<User> page = new Page<>(1, 5);
Page<User> userPage = userMapper.selectPage(page, null);
userPage.getRecords().forEach(System.out::println);
System.out.println("记录总数:" + userPage.getTotal());
System.out.println("总页数:" + userPage.getPages());
}
七、乐观锁插件
乐观锁
:当要更新一条记录的时候,希望这条记录没有被别人更新
在实体类的字段上加上@Version
注解
@Version
private Long version;
配置乐观锁插件(拦截器实现):
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//乐观锁
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
/**
* 乐观锁修改(需要先查询出数据然后对其修改,乐观锁才生效)
*/
@Test
void contextLoads3() {
User user = userMapper.selectById("1520683956281499649");
user.setPassword("asd");
int update = userMapper.updateById(user);
System.out.println(update == 1 ? "修改用户成功!" : "修改用户失败!!!");
}
八、防止全表更新删除插件
配置防全表更新删除插件(拦截器实现):
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//阻止恶意的全表更新删除
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
}
/**
* 阻止恶意的全表更新删除
*/
@Test
void contextLoads4() {
int delete = userMapper.delete(null);
System.out.println(delete >= 1 ? "删除表成功!" : "删除表失败!!!");
}
九、p6spy组件打印Sql分析
导入依赖:
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
修改数据库连接url
、driver-class-name
:
spring:
datasource:
username: root
password: 1234
# 添加 p6spy:
url: jdbc:p6spy:mysql://localhost:3306/jdbc?useSSL=false&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF8
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
添加spy.properties
:
#3.2.1以上使用
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
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 1 秒
outagedetectioninterval=1
测试:
十、数据库连接加密
@SpringBootTest
public class AESEncryptTest {
@Test
void test1(){
//String randomKey = AES.generateRandomKey(); //生成 16 位随机 AES 密钥
//System.out.println(randomKey); //记录下这个秘钥用于加密
String randomKey = "77775f697e419128";
String url = "jdbc:p6spy:mysql://localhost:3306/jdbc" +
"?useSSL=false&serverTimezone=GMT%2B8&useUnicode=true&" +
"characterEncoding=UTF8";
String username = "root";
String password = "1234";
// 随机密钥加密
String result_url = AES.encrypt(url, randomKey);
String result_username = AES.encrypt(username, randomKey);
String result_password = AES.encrypt(password, randomKey);
System.out.println(randomKey+"||"+result_url);
System.out.println(randomKey+"||"+result_username);
System.out.println(randomKey+"||"+result_password);
}
}
修改数据库连接配置:
# 添加mpw:前缀,后面为加密后的值
spring:
datasource:
# 数据库连接加密
username: mpw:279X16wMW5LQXIP/EjLpdA==
password: mpw:os4s3WqyAe0K48kCefjI7Q==
url: mpw:lDBCPwJ6MZPMMPpAP+05ADsYfbcE/JKhyZfVPH3e287XD5+TfOsT9/rkemZOiWLQbAwDVcYyNxIKzG42aO5HacXy2DZlPChia1uyVtdUSW4tNdq0UIOd/dMbUOmXg/pA/Xha5KLd5K65IENymJO2WxopDRm9UNvDSojKr9SnExU=
# 原始
#username: root
#password: 1234
#url: jdbc:p6spy:mysql://localhost:3306/jdbc?useSSL=false&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF8
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
在项目启动类中配置程序实参--mpw.key=秘钥
:
测试:
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
public interface UserService extends IService<User> {
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/list")
public List<User> list(){
return userService.list();
}
}
十一、代码生成器
通过数据库表自动生成对应的实体类、Mapper层(包括Mapper.xml文件)、Service层以及
Controller层通用代码,快速开发
导入依赖:
<!--代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.2</version>
</dependency>
<!--freemarker模版引擎-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
public class CodeGenerator {
//只需修改数据库名称、用户名和密码即可使用
private static String dbName = "spring_security";
private static String username = "root";
private static String password = "1234";
/**
* 数据源配置
*/
private static final DataSourceConfig.Builder DATA_SOURCE_CONFIG = new DataSourceConfig
.Builder("jdbc:mysql://localhost:3306/" + dbName + "?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT%2B8", username, password)
.dbQuery(new MySqlQuery()) //数据库查询
.schema(dbName) //数据库schema(部分数据库适用)
.typeConvert(new MySqlTypeConvert()) //数据库类型转换器
.keyWordsHandler(new MySqlKeyWordsHandler()); //数据库关键字处理器
public static void main(String[] args) {
FastAutoGenerator.create(DATA_SOURCE_CONFIG)
.globalConfig((scanner, builder) -> builder
.fileOverride() //覆盖已生成文件,默认值:false
.outputDir(System.getProperty("user.dir") + "/src/main/java") //指定输出目录,默认值: windows:D://
.author(scanner.apply("请输入开发者名称:"))//作者名,baomidou 默认值:作者
.dateType(DateType.TIME_PACK)//时间策略,DateType.ONLY_DATE(数据库中日期类型字段生成对应实体类中Date类型属性) 默认值: DateType.TIME_PACK(数据库中日期类型字段生成对应实体类中LocalDateTime类型属性)
.commentDate("yyyy-MM-dd")//注释日期,默认值: yyyy-MM-dd
.disableOpenDir()// 禁止打开输出目录,默认值:true
//.enableKotlin() //开启 kotlin 模式,默认值:false
.enableSwagger() //开启 swagger 模式,默认值:false
.build()//加入构建队列
)
.packageConfig((scanner, builder) -> builder
.parent(scanner.apply("请输入包名:"))//父包名,默认值:com.baomidou
//.moduleName("sys")//父包模块名,默认值:无
.entity("pojo")//pojo 包名,默认值:entity
.service("service")//Service 包名,默认值:service
.serviceImpl("service.impl")//Service Impl 包名,默认值:service.impl
.mapper("mapper")//Mapper 包名,默认值:mapper
.xml("mapper.xml")//Mapper XML 包名,默认值:mapper.xml
.controller("controller")//Controller 包名,默认值:controller
//.other("other")//自定义文件包名, 输出自定义文件时所用到的包名
.pathInfo(Collections.singletonMap(OutputFile.xml, "src/main/resources/mapper")) //路径配置信息,Collections.singletonMap(OutputFile.mapperXml, "D://")
.build()//加入构建队列
)
.strategyConfig((scanner, builder) -> builder
/** 基本参数配置 */
.enableCapitalMode()//开启大写命名,默认值:false
.enableSkipView()//开启跳过视图,默认值:false
.disableSqlFilter()//禁用 sql 过滤,默认值:true,语法不能支持使用 sql 过滤表的话,可以考虑关闭此开关
.likeTable(new LikeTable("t_user_login"))//模糊表匹配(sql 过滤) likeTable 与 notLikeTable 只能配置一项
/** 设置需要生成的表名 */
.addInclude(scanner.apply("请输入要生成的表名,多个表使用空格分割:").split(" "))// 增加表匹配(内存过滤),include 与 exclude 只能配置一项
/** 前缀配置*/
//.addTablePrefix("t_")//增加过滤表前缀
//.addFieldSuffix("_")//增加过滤表后缀
//.addFieldPrefix("ul_")//增加过滤字段前缀 本人不建议使用,去掉后缀,会导致驼峰命名实体类的变量名不带前缀,去掉后,错误:Username,正确:ulUsername
//.addFieldSuffix("_")//增加过滤字段后缀
/** 实体策略配置 */
.entityBuilder()//实体策略配置
.disableSerialVersionUID()//禁用生成 serialVersionUID,默认值:true
.enableLombok()//开启 lombok 模型,默认值:false
.enableChainModel()//开启链式模型,默认值:false
.enableRemoveIsPrefix()//开启 Boolean 类型字段移除 is 前缀,默认值:false
.enableTableFieldAnnotation()//开启生成实体时生成字段注解,默认值:false
.enableActiveRecord()//开启 ActiveRecord 模型,默认值:false
.naming(NamingStrategy.underline_to_camel)//数据库表映射到实体的命名策略,默认下划线转驼峰命名:NamingStrategy.underline_to_camel
.columnNaming(NamingStrategy.underline_to_camel)//数据库表字段映射到实体属性的命名策略,默认为 null,未指定按照 naming 执行
.idType(IdType.AUTO)//全局主键类型
//.formatFileName("%sBean")//格式化文件名称,生成实体的后缀,建议这样使用,生成后:UserLoginBean
//.superClass(BaseEntity.class)//设置父类
//.enableColumnConstant()//开启生成字段常量,默认值:false
//.addIgnoreColumns("age")//添加忽略字段
//.nameConvert(INameConvert)//名称转换实现
.addTableFills(
new Column("create_time", FieldFill.INSERT), //创建时间字段添加自动填充属性
new Column("update_time", FieldFill.INSERT_UPDATE), //修改时间字段添加自动填充属性
new Column("deleted", FieldFill.INSERT) //逻辑删除字段添加自动填充属性
)
.versionColumnName("version")//乐观锁字段名(数据库)
.versionPropertyName("version")//乐观锁属性名(实体)
.logicDeleteColumnName("deleted")//逻辑删除字段名(数据库)
.logicDeletePropertyName("deleted")//逻辑删除属性名(实体)
//.enableSchema()//启用 schema,默认值:false,多 schema 场景的时候打开
//.addExclude("t_simple")//增加表排除匹配(内存过滤),include 与 exclude 只能配置一项
//.notLikeTable(new LikeTable("USER"))//模糊表排除(sql 过滤) likeTable 与 notLikeTable 只能配置一项
/** controller 策略配置 */
.controllerBuilder()//controller 策略配置
.enableHyphenStyle()//开启驼峰转连字符,默认值:false
.enableRestStyle()//开启生成@RestController 控制器,默认值:false
.formatFileName("%sController")//格式化文件名称——controller包下自动生成的类后缀,例如UserLoginController
/** Service 策略配置 */
.serviceBuilder()//service 策略配置
.formatServiceFileName("%sService")//转换 service 接口文件名称,例如:UserLoginService
.formatServiceImplFileName("%sServiceImpl")//转换 service 实现类文件名称,例如:UserLoginServiceImpl
/** mapper 策略配置 */
.mapperBuilder()//mapper 策略配置
.superClass(BaseMapper.class)//设置父类,BaseMapper是com.baomidou.mybatisplus.core.mapper中的
.enableMapperAnnotation()//开启 @Mapper 注解,默认值:false
.enableBaseResultMap()//启用 BaseResultMap 生成,默认值:false
.enableBaseColumnList()//启用 BaseColumnList,默认值:false
.formatMapperFileName("%sMapper")//转换 mapper 接口文件名称后缀,mapper目录下的,例如:UserLoginMapper(有@Mapper)
.formatXmlFileName("%sMapper")//转换 xml 文件名称后缀,例如:UserLoginMapper.xml,Mybatis的xml映射文件
.build()//加入构建队列
)
/**
* 模板引擎
*/
//Velocity
//.templateEngine(new VelocityTemplateEngine())
//Beetl
//.templateEngine(new BeetlTemplateEngine())
//Freemarker
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
}