Mybatis-Plus适配联合主键实现方式

 更新时间:2025年11月04日 09:24:20   作者:紫菜紫薯紫甘蓝  
本文介绍了如何使用Mybatis-Plus(MP)和Mybatis-Plus-Plus(MPP)工具来处理联合主键的问题,并详细描述了整合过程、遇到的问题及解决方式

Mybatis-Plus适配联合主键

背景是这样的,最近着手修改一个老项目,平时其他项目用的都是 JPA、倒不是不会用 Mybatis,只是这个项目是用来解析 xml 为对象并进行存储的,而且有的类字段特别多,觉得写 mapper 和 sql 太麻烦了,于是想到了 mybatis-plus(以下简称mp)。

本来以为很好解决的事情,没想到默认的 mp 不支持联合主键,于是查询了一些资料,有一个 mybatisplus-plus(以下简称mpp) 的工具,因此总结一个 demo 出来,包括整合过程和其中遇到的问题和解决方式。

1、引入依赖

	<properties>
		<mybatis-plus.version>3.4.0</mybatis-plus.version>
		<mybatis-plus-plus.version>1.5.1-RELEASE</mybatis-plus-plus.version>
	</properties>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <dependency>
            <groupId>com.github.jeffreyning</groupId>
            <artifactId>mybatisplus-plus</artifactId>
            <version>${mybatis-plus-plus.version}</version>
        </dependency>

注意事项:

  • mp 和 mpp 的版本号对应关系是有要求的,版本不匹配会出现意想不到的错误

2、实体类定义

package com.xsdl.pojo;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
import lombok.Data;

@Data
@TableName(value = "MP_USER")
public class User {

    @MppMultiId
    @TableField(value = "id")
    private String uuid;

    @MppMultiId
    @TableField(value = "xh")
    private Integer xh;

    private String name;

    private Integer age;

    private String email;

}

我在项目中日常遇到的联合主键就是 uuid + xh 来解决,当然用 uuid 做主键,再用另一个字段来关联其他表也可以,而且也没有联合主键这个问题。

注意事项:

  • 使用的是 @MppMultiId 而不是 @TableId
  • 虽然数据库字段叫 id, 但我在类中的定义却是 uuid

3、Mapper(dao)定义

package com.xsdl.mapper;

import com.github.jeffreyning.mybatisplus.base.MppBaseMapper;
import com.xsdl.pojo.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends MppBaseMapper<User> {

}

注意事项:

  • 继承的是 MppBaseMapper,而不是 BaseMapper

4、简单使用

如果你只是想要使用它进行简单的持久化操作,现在已经足够了,例如下面的用法

package com.xsdl.controller;

import com.xsdl.mapper.UserMapper;
import com.xsdl.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Autowired
    private UserMapper userMapper;

    @GetMapping("/list")
    public void list() {
        List<User> users = userMapper.selectList(null);
        for (User user : users) {
            log.info("user:{}", user);
        }
        for (int i = 0; i < users.size(); i++) {
            User user = users.get(i);
            user.setName("xsdl" + i);
            userMapper.updateByMultiId(user);
        }
    }

}

注意事项:

  • 这里的 update 操作,我用的是 updateByMultiId,不能用 updateById

但是我都用联合主键了,说明我大概率是想要批量保存的,而 MppBaseMapper 并不支持这样的操作,于是需要引入 Service

5、Service 定义

package com.xsdl.service;

import com.github.jeffreyning.mybatisplus.service.IMppService;
import com.xsdl.pojo.User;

public interface UserService extends IMppService<User> {

}

注意事项:

  • 继承了 IMppService

6、ServiceImpl 定义

package com.xsdl.service;

import com.github.jeffreyning.mybatisplus.service.MppServiceImpl;
import com.xsdl.mapper.UserMapper;
import com.xsdl.pojo.User;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserServiceImpl extends MppServiceImpl<UserMapper, User> implements UserService {

}

注意事项:

  • 先继承 MppServiceImpl,并把对应的 Mapper 传入再实现对应的 Service

7、使用

package com.xsdl.controller;

import com.xsdl.mapper.UserMapper;
import com.xsdl.pojo.User;
import com.xsdl.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserService userService;

    @GetMapping("/list")
    public void list() {
        List<User> users = userMapper.selectList(null);
        for (User user : users) {
            log.info("user:{}", user);
        }
        for (int i = 0; i < users.size(); i++) {
            User user = users.get(i);
            user.setName("zss" + i);
            userMapper.updateByMultiId(user);
        }

        List<User> userList = IntStream.rangeClosed(1, 10).mapToObj(i -> {
            User user = new User();
            user.setUuid(i + "" + i);
            user.setXh(i);
            user.setName("zss" + i);
            user.setAge(i);
            user.setEmail("zss" + i + "@qq.com");
            return user;
        }).collect(Collectors.toList());
        userService.saveBatch(userList);

        List<User> list = userService.list();
        for (User user : list) {
            log.info("user:{}", user);
        }
    }

}

注意启动类上需要加上: @EnableMPP 注解

@SpringBootApplication
@MapperScan("com.xsdl.mapper")
@EnableMPP
public class SpringbootApplication {
	public static void main(String[] args) {
		SpringApplication.run(SpringbootApplication.class, args);
	}
}

可能出现的问题:

1、我在第二步 实体类定义 这里强调说,虽然数据库字段名是 id,但我的属性名却是 uuid,如果设置为 id,启动项目会出现这样的错误

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘userController’: Unsatisfied dependency expressed through field ‘userMapper’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘userMapper’ defined in file Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: java.lang.RuntimeException: not found column for id

大概意思就是说创建 UserMapper 失败,原因是找不到 id 列,我没详细去解决这个错误,推测原因可能是 mp 里把类里的 id 属性自动解释为主键,解决方式就是像我一样,给它起个新的名字,然后使用 @TableField(value = "id") 标记一下

2、因为我接手的这个项目早期是 mybatis 嘛,我虽然引入了 mp,但不敢删除之前的 mybatis 的依赖,于是二者共存了,有机会遇到这样的错误

Error creating bean with name ‘com.github.jeffreyning.mybatisplus.conf.PlusConfig’: Invocation of init method failed; nested exception is java.lang.NoSuchFieldException: CLASS_RESOLVER

这个错误就通俗易懂多了,说创建这个 bean 失败,原因是根本就没有一个 CLASS_RESOLVER 的字段,进入 PlusConfig 查看源码

package com.github.jeffreyning.mybatisplus.conf;

@Import({PlusACUtils.class, MppSqlInjector.class})
@Configuration
public class PlusConfig {
    private static final Logger logger = LoggerFactory.getLogger(PlusConfig.class);
    @Autowired
    private Environment env;

    public PlusConfig() {
    }

    @PostConstruct
    public void initRM() throws Exception {
        NhOgnlClassResolver resolver = new NhOgnlClassResolver();
        resolver.baseList.add("com.github.jeffreyning.mybatisplus.check");
        LambdaUtil.setValue(OgnlCache.class, "CLASS_RESOLVER", resolver);
        String utilBasePaths = this.env.getProperty("mpp.utilBasePath");
    }
}

17 行通过工具类向 OgnlCache 的 CLASS_RESOLVER 设置值,但是这个字段在 OgnlCache 类不存在,这个其实是 mybatis 和 mpp 版本问题,mybatis 的早期版本中,OgnlCache 没有这个字段,因此修改下 mybatis 的版本就可以了,我这里修改到了 3.5.5

   	<dependency>
   		<groupId>org.mybatis</groupId>
   		<artifactId>mybatis</artifactId>
   		<version>3.5.5</version>
   	</dependency>

不过我后来发现 mp 和 springboot 的依赖中其实已经引入了 mybatis 的依赖,似乎把老的删了就行,总之这个问题跟 mybatis 的版本有关系

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 利用Java反射机制实现对象相同字段的复制操作

    利用Java反射机制实现对象相同字段的复制操作

    这篇文章主要介绍了利用Java反射机制实现对象相同字段的复制操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • 如何使用Java 8函数式编程优雅处理多层嵌套数据

    如何使用Java 8函数式编程优雅处理多层嵌套数据

    Java8是Java语言历史上的一个重大更新,它带来了许多新的特性和改进,其中函数式编程的引入是其亮点之一,这篇文章主要介绍了如何使用Java 8函数式编程优雅处理多层嵌套数据的相关资料,需要的朋友可以参考下
    2026-01-01
  • 使用Java7的Files工具类和Path接口来访问文件的方法

    使用Java7的Files工具类和Path接口来访问文件的方法

    下面小编就为大家分享一篇使用Java7的Files工具类和Path接口来访问文件的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-11-11
  • JAVA获取本地MAC地址的方法

    JAVA获取本地MAC地址的方法

    这篇文章主要介绍了JAVA获取本地MAC地址的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • SpringCloud如何解决服务之间的通信问题

    SpringCloud如何解决服务之间的通信问题

    本文主要介绍了SpringCloud如何解决服务之间的通信问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-08-08
  • java实现时间控制的几种方案

    java实现时间控制的几种方案

    这篇文章主要介绍了java实现时间控制的几种方案,本文从多个方面给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-07-07
  • JAVAEE model1模型实现商品浏览记录(去除重复的浏览记录)(一)

    JAVAEE model1模型实现商品浏览记录(去除重复的浏览记录)(一)

    这篇文章主要为大家详细介绍了JAVAEE model1模型实现商品浏览记录,去除重复的浏览记录,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-11-11
  • SpringBoot属性注入的两种方法

    SpringBoot属性注入的两种方法

    这篇文章主要介绍了SpringBoot属性注入的两种方法,帮助大家更好的理解和使用springboot框架,感兴趣的朋友可以了解下
    2020-11-11
  • sharding-jdbc中的事务详细解读

    sharding-jdbc中的事务详细解读

    这篇文章主要介绍了sharding-jdbc中的事务详细解读,sharding-jdbc在分库分表方面提供了很大的便利性,在使用DB的时候,通常都会涉及到事务这个概念,而在分库分表的环境上再加上事务,就会使事情变得复杂起来,需要的朋友可以参考下
    2023-12-12
  • 如何在Java中优雅地判空详解

    如何在Java中优雅地判空详解

    这篇文章主要大家介绍了关于如何在Java中优雅地判空的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-11-11

最新评论