MybatisPlus配置联合主键 背景是这样的,最近着手修改一个老项目,平时其他项目用的都是 JPA、倒不是不会用 Mybatis,只是这个项目是用来解析 xml 为对象并进行存储的,而且有的类字段特别多,觉得写 mapper 和 sql 太麻烦了,于是想到了 mybatis-plus(以下简称mp)。
本来以为很好解决的事情,没想到默认的 mp 不支持联合主键,于是查询了一些资料,有一个 mybatisplus-plus(以下简称mpp) 的工具,因此总结一个 demo 出来,包括整合过程和其中遇到的问题和解决方式。
1、引入依赖
1 2 3 4 <properties > <mybatis-plus.version > 3.4.0</mybatis-plus.version > <mybatis-plus-plus.version > 1.5.1-RELEASE</mybatis-plus-plus.version > </properties >
1 2 3 4 5 6 7 8 9 10 <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、实体类定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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)定义
1 2 3 4 5 6 7 8 9 10 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、简单使用
如果你只是想要使用它进行简单的持久化操作,现在已经足够了,例如下面的用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 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 定义
1 2 3 4 5 6 7 8 package com.xsdl.service;import com.github.jeffreyning.mybatisplus.service.IMppService;import com.xsdl.pojo.User;public interface UserService extends IMppService <User> { }
注意事项:
6、ServiceImpl 定义
1 2 3 4 5 6 7 8 9 10 11 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、使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 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); } } }
可能出现的问题:
我在第二步 实体类定义 这里强调说,虽然数据库字段名是 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") 标记一下
因为我接手的这个项目早期是 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 查看源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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
1 2 3 4 5 <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5 .5 </version> </dependency>
不过我后来发现 mp 和 springboot 的依赖中其实已经引入了 mybatis 的依赖,似乎把老的删了就行,总之这个问题跟 mybatis 的版本有关系