一.问题背景
mybatis-plus提供了许多常用方法,能满足基本的增删改查需求,省去了xml、mapper、service的编写,也不用担心实体的映射,十分方便。
但实际开发中很多时候会用到组合主键,mybatis-plus原本并不支持组合主键,手写sql和方法又觉得麻烦,可以通过三方jar包对mybatis-plus进行增强,以适用于组合主键的场景。
二.代码实现
1.引入依赖
默认已经集成了mybatis-plus,引入jeffreyning支持组合主键
<dependency>
<groupId>com.github.jeffreyning</groupId>
<artifactId>mybatisplus-plus</artifactId>
<version>1.7.4-RELEASE</version>
</dependency>
2.启动类
启动类增加@EnableMPP注解
package com.bjtumem;
import com.github.jeffreyning.mybatisplus.conf.EnableMPP;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
/**
* 启动程序
*
* @author bjtumem
*/
@EnableMPP
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class BjtuMemApplication
{
public static void main(String[] args)
{
SpringApplication.run(BjtuMemApplication.class, args);
}
}
3.实体类
用@MppMultiId注解修饰主键字段,在组合主键场景下@TableId就不适用了。
package com.bjtumem.system.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.bjtumem.common.utils.ValidatorGroup;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Date;
/**
* @author daixin
* @version 1.0
* @description: TODO
* @date 2023/8/10 11:00
*/
@Data
@TableName("p_collect")
public class PCollect implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 布告id
*/
@NotNull(groups = {ValidatorGroup.Insert.class}, message = "bulletinId is not null")
@MppMultiId
private Long bulletinId;
/**
* 用户id
*/
@MppMultiId
private Long userId;
/**
* 收藏时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(value = "create_time")
private Date createTime;
}
4.Mapper
继承MppBaseMapper
package com.bjtumem.system.mapper;
import com.bjtumem.system.domain.PCollect;
import com.github.jeffreyning.mybatisplus.base.MppBaseMapper;
/**
* @author daixin
* @version 1.0
* @description: TODO
* @date 2023/8/10 11:04
*/
public interface PCollectMapper extends MppBaseMapper<PCollect> {
}
5.Service
继承IMppService
package com.bjtumem.system.service;
import com.bjtumem.system.domain.PCollect;
import com.github.jeffreyning.mybatisplus.service.IMppService;
/**
* @author daixin
* @version 1.0
* @description: TODO
* @date 2023/8/10 11:05
*/
public interface IPCollectService extends IMppService<PCollect> {
}
6.ServiceImpl
继承MppServiceImpl
package com.bjtumem.system.service.impl;
import com.bjtumem.system.domain.PCollect;
import com.bjtumem.system.mapper.PCollectMapper;
import com.bjtumem.system.service.IPCollectService;
import com.github.jeffreyning.mybatisplus.service.MppServiceImpl;
import org.springframework.stereotype.Service;
/**
* @author daixin
* @version 1.0
* @description: TODO
* @date 2023/8/10 11:05
*/
@Service
public class PCollectServiceImpl extends MppServiceImpl<PCollectMapper, PCollect> implements IPCollectService {
}
7.Controller
直接调用继承自IMppService的xxxByMultiId()方法,组合主键即可生效。
/**
* 收藏
* @param pCollect
* @return
*/
@PostMapping(value = "/collect")
public AjaxResult collect(@Validated({ValidatorGroup.Insert.class})PCollect pCollect)
{
pCollect.setUserId(getUserId());
PCollect existPCollect = pCollectService.selectByMultiId(pCollect);
if(existPCollect !=null){
return AjaxResult.success();
}
pCollectService.save(pCollect);
return AjaxResult.success();
}
/**
* 取消收藏
* @param pCollect
* @return
*/
@PostMapping(value = "/cancelCollect")
public AjaxResult cancelCollect(@Validated({ValidatorGroup.Insert.class})PCollect pCollect)
{
pCollect.setUserId(getUserId());
pCollectService.deleteByMultiId(pCollect);
return AjaxResult.success();
}
8.运行测试
以下为控制台打出的sql,已经执行了组合主键。
三.问题与解决方案
1.Invalid bound statement (not found): xxxMapper.selectByMultiId
本项目调试过程中报错Invalid bound statement (not found): xxxMapper.selectByMultiId的原因有如下两种情况。
(1)自定义SqlSessionFactory
检查是否实现了自定义的SqlSessionFactory,如果实现了自定义的SqlSessionFactory则需要配置MppSqlInjector的注入,否则报错Invalid bound statement (not found): xxxMapper.selectByMultiId。
本项目基于若依框架开发,一般使用mybatis-plus都需要实现自定义SqlSessionFactory,检查MyBatisConfig配置,注入MppSqlInjector,问题解决。
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource, MppSqlInjector mppSqlInjector, MybatisPlusProperties properties) 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 MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
GlobalConfig globalConfig = properties.getGlobalConfig();
globalConfig.setSqlInjector(mppSqlInjector);
sessionFactory.setGlobalConfig(globalConfig);
sessionFactory.setDataSource(dataSource);
sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
return sessionFactory.getObject();
}
(2)jar包冲突
检查项目是否同时引入了不同版本的jar包,如下图,mybatis-plus相关jar都有两个版本,jar包冲突导致使用组合主键时报错Invalid bound statement (not found): xxxMapper.selectByMultiId。
由于jar包是由不同开发人员在不同时间引入,且单主键功能正常,所以这个问题不容易发现,检查pom文件,将不兼容版本的引用删掉或替换即可。