如果想直接看如何解决请看第三点mybatisplus-plus
场景:
- 十多个表
- 每个表有可能多条数据
- 每个表都需要多个键才能确定唯一数据
相信大家第一眼看到这样的需求就会想起这个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
自带的方法中有saveOrUpdateBatch
方法,可以批量的处理我的多条数据(本文不介绍如何使用mybatis-plus,相信大家都会)
我跟大家也一样,也是用了,结果报空指针异常,查看了底层代码,在这一层报的错
我们跟进getById方法…
他就是根据主键返回一个对象,但是如果这个表存在多个主键,那么根据第一个主键扫出来的数据就不会只有一条,所以这里就会报错!
不要说不会有这种一个表多个主键的情况,那是因为现在大家都规范了,在以前erp用的是sql server 的时候很多就是多个(所以使用mybatis-plus根本实现不了,它只支持一个主键的增删查改)
可以理解成mybatis的加强版的加强版
本章只介绍第一种,也就是:根据多个字段联合主键增删查改
其他可查看:mybatisplus-plus对mybatisplus的增强
<dependencies>
......//其他依赖
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.0version>
dependency>
<dependency>
<groupId>com.github.jeffreyninggroupId>
<artifactId>mybatisplus-plusartifactId>
<version>1.5.1-RELEASEversion>
dependency>
dependencies>
启动类上加上@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);
}
}
在主键字段上加上@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;
/**
*
* 成绩单实体类
*
* @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;
}
}
继承MppBaseMapper
类
package com.chenly.mpp.mapper;
import com.chenly.mpp.entity.Score;
import com.github.jeffreyning.mybatisplus.base.MppBaseMapper;
/**
*
* Mapper 接口
*
* @author chenly
* @since 2022-11-18
*/
public interface ScoreMapper extends MppBaseMapper<Score> {
}
service层接口继承IMppService
package com.chenly.mpp.service;
import com.chenly.mpp.entity.Score;
import com.github.jeffreyning.mybatisplus.service.IMppService;
/**
*
* 服务接口
*
*
* @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;
/**
*
* 服务实现类
*
* @author chenly
* @since 2022-11-18
*/
@Service
public class ScoreServiceImpl extends MppServiceImpl<ScoreMapper, Score> implements ScoreService {
}
调用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的,所以发生了很多问题
这是因为我们实体有@TableId
主键表名主键,而在mybatisplus-plus中使用@MppMultiId标明的,所以报错
只需要改成@TableField
即可
还有可能是因为没有设置@TableField(value = “xxx”)导致的
一般报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();
}
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。
所有叫id的属性都自动注册为主键
实体类的联合主键字段不要使用“Id”,换成其他的
@MppMultiId
@TableField("id") // 属性名换掉,指定数据表列名
private String userId;