随着项目的开发,有时候需要使用新的ORM,使用mybatis的项目需要集成mybatis-plus,可以参考以下方式
1 导入mybatis-plus的相关依赖
<!-- mybatis-plus,注释掉 org.mybatis.spring.boot -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis.plus.boot.starter.version}</version>
<exclusions>
<exclusion>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
</exclusion>
<exclusion>
<artifactId>jsqlparser</artifactId>
<groupId>com.github.jsqlparser</groupId>
</exclusion>
</exclusions>
</dependency>
2 如果项目中存在sqlSessionFactory的配置信息,那么需要处理一下分页拦截器
@Configuration
public class MyBatisConfig {
@Autowired
private Environment env;
@Autowired
** private MybatisPlusInterceptor mybatisPlusInterceptor;**
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
public static String setTypeAliasesPackage(String typeAliasesPackage) {
ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
List<String> allResult = new ArrayList<String>();
try {
for (String aliasesPackage : typeAliasesPackage.split(",")) {
List<String> result = new ArrayList<String>();
aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
Resource[] resources = resolver.getResources(aliasesPackage);
if (resources != null && resources.length > 0) {
MetadataReader metadataReader = null;
for (Resource resource : resources) {
if (resource.isReadable()) {
metadataReader = metadataReaderFactory.getMetadataReader(resource);
try {
result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
if (result.size() > 0) {
HashSet<String> hashResult = new HashSet<String>(result);
allResult.addAll(hashResult);
}
}
if (allResult.size() > 0) {
typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
} else {
throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
}
} catch (IOException e) {
e.printStackTrace();
}
return typeAliasesPackage;
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
String mapperLocations = env.getProperty("mybatis.mapperLocations");
String configLocation = env.getProperty("mybatis.configLocation");
typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
VFS.addImplClass(SpringBootVFS.class);
// final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
//改为使用mybaits-plus
**final MybatisSqlSessionFactoryBean sessionFactory=new MybatisSqlSessionFactoryBean();**
sessionFactory.setDataSource(dataSource);
sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
** sessionFactory.setPlugins(mybatisPlusInterceptor);
**
return sessionFactory.getObject();
}
}
/**
* Mybatis Plus 配置
*
* @author ruoyi
*/
@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor());
// 乐观锁插件
interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
// 阻断插件
interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
return interceptor;
}
/**
* 分页插件,自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html
*/
public PaginationInnerInterceptor paginationInnerInterceptor() {
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
// 设置数据库类型为mysql
paginationInnerInterceptor.setDbType(DbType.MYSQL);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
paginationInnerInterceptor.setMaxLimit(-1L);
return paginationInnerInterceptor;
}
/**
* 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html
*/
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
return new OptimisticLockerInnerInterceptor();
}
/**
* 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html
*/
public BlockAttackInnerInterceptor blockAttackInnerInterceptor() {
return new BlockAttackInnerInterceptor();
}
}
3 Mybatis-Plus常用点
1. yml配置
以下为整理的常用yml配置,配置后无需配置mybatis且无需引入mybatis依赖
mybatis-plus:
mapper-locations: classpath:mapper/**/*.xml # mapper映射文件(默认存在)
type-aliases-package: com.itheima.mp.demain # 别名包
configuration:
map-underscore-to-camel-case: true # 是否开启下划线与驼峰的映射(默认打开)
cache-enabled: false # 是否开启二级缓存(默认关闭)
global-config: # 全局配置
db-config:
id-type: auto #id+1自增策略(默认assign_id雪花算法)
update-strategy: not_empty #null与''都忽略(默认not_null:只忽略null)
# 下面的配置:将所有的删除语句更改为修改语句
logic-delete-field: isDelete # 是否删除;全局逻辑删除的实体字段名,字段类型可以是boolean、integer
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
2 注解用法
以下为整理的常用注解,基本都是标记类表之间的对应关系
@Data
@TableName("tb_user")
public class User {
//此处配置主键策略优先级 > yml文件
@TableId(value = "id",type = IdType.AUTO)
private Long id;
//与数据库关键字冲突(必加)
@TableField("`order`")
private Integer order;
//is前缀且类型为Boolean(必加)
@TableField("is_delete")
private Boolean isDelete;
//数据表中不存在的(必加)
@TableField(exist = false)
private String address;
}
3 DB静态工具
Db静态工具跟IService功能几乎一致,主要用来处理依赖冲突问题,在两个service中可以相互依赖
import com.baomidou.mybatisplus.extension.toolkit.Db;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private AddressMapper addressMapper;//不推荐
@Override
public void test() {
//在UserServiceImpl中不推荐注入AddressMapper
addressMapper.saveAddress(new Address());
Db.lambdaUpdate(Address.class).update(new Address());//推荐使用Db静态工具
}
}
4 枚举处理器
枚举处理器主要用来处理表中数据存储为0,1状态时,通过枚举类对0,1表示内容添加备注
- 1 配置全局枚举处理器
mybatis-plus:
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler #全局枚举处理器
- 2 编写枚举类
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
@Getter
public enum UserStatus {
STOP_USING(1,"已停用"),
USING(0,"启用中");
@EnumValue //表示与数据库交互的值
private final Integer status;
@JsonValue //表示返回给前端的值
private final String desc;
UserStatus(Integer status, String desc) {
this.status = status;
this.desc = desc;
}
- 3 查询数据,查询0状态的用户,返回启用中(注:直接打印不会出现想要字符)
@PostMapping("test")
public List<User> test01(){
return userService.lambdaQuery()
.eq(User::getStatus,UserStatus.USING).list();
}
5 dto与vo的分页查询
mybatis-plus依旧可以使用常规分页方法,这边介绍mybatis-plus自带的。
- 1 添加分页插件,配置mp的拦截器
@Configuration
public class MybatisConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 1.初始化核心插件
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 2.添加分页插件
PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
pageInterceptor.setMaxLimit(1000L); // 设置分页上限
interceptor.addInnerInterceptor(pageInterceptor);
return interceptor;
}
}
-
- 写两个分页父类,先介绍参数类
@Data
public class PageParam {
/**
* 页码
*/
@TableField(exist = false)
private Integer pageNum = 1;
/**
* 总页数
*/
@TableField(exist = false)
private Integer pageSize = 5;
/**
* 排序字段
*/
@TableField(exist = false)
private String sortBy;
/**
* 是否升序
*/
@TableField(exist = false)
private Boolean isAsc;
/**
* 空排序字段传递排序规则
* @param items
* @return
* @param <T>
*/
public <T> Page<T> toMpPage(OrderItem ... items){
// 分页条件
Page<T> page = Page.of(pageNum, pageSize);
if(StrUtil.isNotBlank(sortBy)){
page.addOrder(new OrderItem(sortBy, isAsc));
}else if(sortBy != null){
page.addOrder(items);
}
return page;
}
/**
* 设定默认值:"更新字段"降序
* @return
* @param <T>
*/
public <T> Page<T> toMpPageDefaultSortByUpdateTime(){
return toMpPage(new OrderItem("update_time", false));
}
}
- 3 返回类型
@Data
public class PageResult<T> {
/**
* 总条数
*/
private Long total;
/**
* 总页数
*/
private Long pages;
/**
* 当前页数据
*/
private List<T> list;
/**
* 通过BeanUtil拷贝的形式,实现 dto -> vo
* @param po
* @param clazz
* @return
* @param <PO>
* @param <VO>
*/
public static <PO, VO> PageResult<VO> of(Page<PO> po, Class<VO> clazz){
PageResult<VO> result = new PageResult<>();
// 总条数
result.setTotal(po.getTotal());
// 总页数
result.setPages(po.getPages());
// 当前页数据
List<PO> records = po.getRecords();
if(CollUtil.isEmpty(records)){
result.setList(Collections.emptyList());
return result;
}
// 拷贝VO
result.setList(BeanUtil.copyToList(records, clazz));
return result;
}
/**
* 通过传递函数式接口定义规则,实现 dto -> vo
* @param po
* @param convertor
* @return
* @param <PO>
* @param <VO>
*/
public static <PO, VO> PageResult<VO> of(Page<PO> po, Function<PO, VO> convertor){
PageResult<VO> result = new PageResult<>();
// 总条数
result.setTotal(po.getTotal());
// 总页数
result.setPages(po.getPages());
// 当前页数据
List<PO> records = po.getRecords();
if(CollUtil.isEmpty(records)){
result.setList(Collections.emptyList());
return result;
}
// 拷贝VO
result.setList(records.stream().map(convertor).collect(Collectors.toList()));
return result;
}
}
- 4 使用例子
PageResult<UserVo> list = userService.pageSelect(user);
public PageResult<UserVo> pageSelect(User user) {
//通过传递来的参数点出方法包装为page对象
Page<User> page = user.toMpPageDefaultSortByUpdateTime();
//分页+条件查询
String username = user.getUsername();
Page<User> pageSelect = lambdaQuery()
.like(username != null, User::getUsername, username)
.page(page);
//一般情况,使用基础的BeanUtil赋值,需要Vo和user字段名一致
return PageResult.of(pageSelect, UserVo.class);
}
PageResult<UserVo> list = userService.pageSelect2(user);
@Override
public PageResult<UserVo> pageSelect2(User user) {
//通过传递来的参数点出方法包装为page对象
Page<User> page = user.toMpPageDefaultSortByUpdateTime();
//分页+条件查询
String username = user.getUsername();
Page<User> pageSelect = lambdaQuery()
.like(username != null, User::getUsername, username)
.page(page);
return PageResult.of(pageSelect,
//传递匿名内部类 - 处理查询出的数据与Vo的转化
userTest -> {
//这里演示拷贝赋值+处理username最后两个字符为**
UserVo userVo = BeanUtil.copyProperties(userTest, UserVo.class);
userVo.setTest(userVo.getUsername().substring(0, userVo.getUsername().length() - 2) + "**");
return userVo;
});
}