解决mybatis-plus无法根据多个主键增删查改


接到新需求,数据同步时,当存在就修改,不存在就添加。

如果想直接看如何解决请看第三点mybatisplus-plus

场景:

  • 十多个表
  • 每个表有可能多条数据
  • 每个表都需要多个键才能确定唯一数据

一、ON DUPLICATE KEY UPDATE

相信大家第一眼看到这样的需求就会想起这个ON DUPLICATE KEY UPDATE

用法:

insert into(a,b,c) values(1,2,3)
on duplicate key update 
a=11,b=22,c=33

使用该方法虽然能实现,但有点麻烦,不仅要自己写sql,而且不适合我用反射了,因为我得根据不同的表调用不同的方法了ON DUPLICATE KEY UPDATE后面跟上的第一个字段 必须是 唯一索引,而且还有可能失效(这种场景我没出现过,我同事出现过使用ON DUPLICATE KEY UPDATE失效的情况),我又很懒,根本就不想手写sql,所以就想到第二种(mybatis-plus)

二、mybatis-plus

相信很多同学第一时间会想到这个,在mybatis-plus自带的方法中有saveOrUpdateBatch方法,可以批量的处理我的多条数据(本文不介绍如何使用mybatis-plus,相信大家都会)

我跟大家也一样,也是用了,结果报空指针异常,查看了底层代码,在这一层报的错

image-20230203171211911

我们跟进getById方法…

image-20230203171441031

他就是根据主键返回一个对象,但是如果这个表存在多个主键,那么根据第一个主键扫出来的数据就不会只有一条,所以这里就会报错!

不要说不会有这种一个表多个主键的情况,那是因为现在大家都规范了,在以前erp用的是sql server 的时候很多就是多个(所以使用mybatis-plus根本实现不了,它只支持一个主键的增删查改)

三、mybatisplus-plus

可以理解成mybatis的加强版的加强版

  • 根据多个字段联合主键增删改查
  • 优化分页插件实现在不分页时进行排序操作
  • 自动填充优化功能 & 自动扫描Entity类构建ResultMap功能

本章只介绍第一种,也就是:根据多个字段联合主键增删查改

其他可查看:mybatisplus-plus对mybatisplus的增强

1、依赖

<dependencies>
    ......//其他依赖
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.0</version>
    </dependency>
    <dependency>
        <groupId>com.github.jeffreyning</groupId>
        <artifactId>mybatisplus-plus</artifactId>
        <version>1.5.1-RELEASE</version>
    </dependency>

</dependencies>

2、启动类

启动类上加上@EnableMPP注解

package com.chenly.mpp;

import com.github.jeffreyning.mybatisplus.conf.EnableMPP;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author: chenly
 * @date: 2022-11-18 16:55
 * @description:
 * @version: 1.0
 */
@SpringBootApplication
@MapperScan("com.chenly.mpp.mapper")
@EnableMPP
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

3、实体类

在主键字段上加上@MppMultiId注解

package com.chenly.mpp.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;

/**
 * <p>
 * 成绩单实体类
 * </p>
 * @author chenly
 * @since 2022-11-18
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("SCORE")
public class Score extends Model<Score> {
    private static final long serialVersionUID = 1L;

     /** "学号" */
    @MppMultiId
    @TableField("STUDENT_ID")
    private Integer studentId;

    /** "课程号" */
    @MppMultiId
    @TableField("COURSE_NO")
    private Integer courseNo;

    /** "分数" */
    @TableField("SCORE")
    private Integer score;
    
    @Override
    protected Serializable pkVal() {
        return this.studentId;
    }

}

4、Mapper

继承MppBaseMapper

package com.chenly.mpp.mapper;

import com.chenly.mpp.entity.Score;
import com.github.jeffreyning.mybatisplus.base.MppBaseMapper;

/**
 * <p>
 *  Mapper 接口
 * </p>
 * @author chenly
 * @since 2022-11-18
 */
public interface ScoreMapper extends MppBaseMapper<Score> {

}

5、Service

service层接口继承IMppService

package com.chenly.mpp.service;

import com.chenly.mpp.entity.Score;
import com.github.jeffreyning.mybatisplus.service.IMppService;

/**
 * <p>
 *  服务接口
 * </p>
 *
 * @author chenly
 * @since 2022-11-18
 */
public interface ScoreService extends IMppService<Score> {

}

service实现类继承MppServiceImpl

package com.chenly.mpp.service.impl;

import com.chenly.mpp.entity.Score;
import com.chenly.mpp.mapper.ScoreMapper;
import com.chenly.mpp.service.ScoreService;
import com.github.jeffreyning.mybatisplus.service.MppServiceImpl;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 * @author chenly
 * @since 2022-11-18
 */
@Service
public class ScoreServiceImpl extends MppServiceImpl<ScoreMapper, Score> implements ScoreService {

}

6、Test

调用saveOrUpdateBatchByMultiId()方法根据多主键批量保存或更新

package com.chenly.mpp.controller;

import com.chenly.mpp.entity.Score;
import com.chenly.mpp.service.ScoreService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author: chenly
 * @date: 2022-11-18 17:44
 * @description:
 * @version: 1.0
 */
@RestController
@RequestMapping("/score")
public class ScoreController {

    @Autowired
    private ScoreService scoreService;

    @PostMapping("/save")
    public List<Score> querySchoolStudent2(@RequestBody List<Score> product){
        scoreService.saveOrUpdateBatchByMultiId(product);
        return product;
    }


}

四、遇到的问题

因为我的项目一开始是集成mybatis-plus的,后面改成用mybatisplus-plus的,所以发生了很多问题

1、nested exception is java.lang.RuntimeException: not found column for xxxxx

这是因为我们实体有@TableId主键表名主键,而在mybatisplus-plus中使用@MppMultiId标明的,所以报错

只需要改成@TableField即可

还有可能是因为没有设置@TableField(value = “xxx”)导致的

image-20230204084630960

2、Invalid bound statement (not found): XXXMapper.selectByMultiId

一般报Invalid bound statement (not found)是没加@EnableMPP
如果加了@EnableMPP后仍然报Invalid bound statement (not found)
需要检查是否实现了自定义的SqlSessionFactory,如果实现自定义的SqlSessionFactory则需要手工注入 MppSqlInjector(否则引发Invalid bound statement)

@Bean(name = "mySqlServerSqlSessionFactory")
    public SqlSessionFactory sqlServerSqlSessionFactory(@Qualifier("mySqlServerDataSource") DataSource sqlServerDataSource, 			MppSqlInjector mppSqlInjector, MybatisPlusProperties properties) throws Exception {
        final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
        GlobalConfig globalConfig = properties.getGlobalConfig();
        globalConfig.setSqlInjector(mppSqlInjector);
        sessionFactory.setGlobalConfig(globalConfig);
        sessionFactory.setDataSource(sqlServerDataSource);
        sessionFactory.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources(MroSqlServerDataSourceConfig.MAPPER_LOCATION));
        return sessionFactory.getObject();
    }

如果还是出现这个报错,请把mybatisplus-plus升级到最新版本或1.7以上,因为1.5…的版本没有兼容

3、NoSuchFieldException: modifiers

org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'com.github.jeffreyning.mybatisplus.conf.PlusConfig': 
Invocation of init method failed; nested exception is java.lang.NoSuchFieldException: modifiers

应该是jdk11与自定义ognl加载机制不兼容导致的。 mybatisplus-plus1.7.0删除了自定义ognl根路径功能,兼容jdk11。

4、not found column for ‘id’ mybatis-plus

所有叫id的属性都自动注册为主键

实体类的联合主键字段不要使用“Id”,换成其他的

@MppMultiId
@TableField("id") // 属性名换掉,指定数据表列名
private String userId;
  • 14
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值