Mybatis-Plus適配聯(lián)合主鍵實(shí)現(xiàn)方式
Mybatis-Plus適配聯(lián)合主鍵
背景是這樣的,最近著手修改一個(gè)老項(xiàng)目,平時(shí)其他項(xiàng)目用的都是 JPA、倒不是不會(huì)用 Mybatis,只是這個(gè)項(xiàng)目是用來(lái)解析 xml 為對(duì)象并進(jìn)行存儲(chǔ)的,而且有的類字段特別多,覺(jué)得寫(xiě) mapper 和 sql 太麻煩了,于是想到了 mybatis-plus(以下簡(jiǎn)稱mp)。
本來(lái)以為很好解決的事情,沒(méi)想到默認(rèn)的 mp 不支持聯(lián)合主鍵,于是查詢了一些資料,有一個(gè) mybatisplus-plus(以下簡(jiǎn)稱mpp) 的工具,因此總結(jié)一個(gè) demo 出來(lái),包括整合過(guò)程和其中遇到的問(wèn)題和解決方式。
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>
注意事項(xiàng):
- mp 和 mpp 的版本號(hào)對(duì)應(yīng)關(guān)系是有要求的,版本不匹配會(huì)出現(xiàn)意想不到的錯(cuò)誤
2、實(shí)體類定義
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;
}
我在項(xiàng)目中日常遇到的聯(lián)合主鍵就是 uuid + xh 來(lái)解決,當(dāng)然用 uuid 做主鍵,再用另一個(gè)字段來(lái)關(guān)聯(lián)其他表也可以,而且也沒(méi)有聯(lián)合主鍵這個(gè)問(wèn)題。
注意事項(xiàng):
- 使用的是
@MppMultiId而不是@TableId - 雖然數(shù)據(jù)庫(kù)字段叫 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> {
}
注意事項(xiàng):
- 繼承的是 MppBaseMapper,而不是 BaseMapper
4、簡(jiǎn)單使用
如果你只是想要使用它進(jìn)行簡(jiǎn)單的持久化操作,現(xiàn)在已經(jīng)足夠了,例如下面的用法
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);
}
}
}
注意事項(xiàng):
- 這里的 update 操作,我用的是 updateByMultiId,不能用 updateById
但是我都用聯(lián)合主鍵了,說(shuō)明我大概率是想要批量保存的,而 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> {
}
注意事項(xiàng):
- 繼承了 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 {
}
注意事項(xiàng):
- 先繼承 MppServiceImpl,并把對(duì)應(yīng)的 Mapper 傳入再實(shí)現(xiàn)對(duì)應(yīng)的 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);
}
}
}
注意啟動(dòng)類上需要加上: @EnableMPP 注解
@SpringBootApplication
@MapperScan("com.xsdl.mapper")
@EnableMPP
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
可能出現(xiàn)的問(wèn)題:
1、我在第二步 實(shí)體類定義 這里強(qiáng)調(diào)說(shuō),雖然數(shù)據(jù)庫(kù)字段名是 id,但我的屬性名卻是 uuid,如果設(shè)置為 id,啟動(dòng)項(xiàng)目會(huì)出現(xiàn)這樣的錯(cuò)誤
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
大概意思就是說(shuō)創(chuàng)建 UserMapper 失敗,原因是找不到 id 列,我沒(méi)詳細(xì)去解決這個(gè)錯(cuò)誤,推測(cè)原因可能是 mp 里把類里的 id 屬性自動(dòng)解釋為主鍵,解決方式就是像我一樣,給它起個(gè)新的名字,然后使用 @TableField(value = "id") 標(biāo)記一下
2、因?yàn)槲医邮值倪@個(gè)項(xiàng)目早期是 mybatis 嘛,我雖然引入了 mp,但不敢刪除之前的 mybatis 的依賴,于是二者共存了,有機(jī)會(huì)遇到這樣的錯(cuò)誤
Error creating bean with name ‘com.github.jeffreyning.mybatisplus.conf.PlusConfig’: Invocation of init method failed; nested exception is java.lang.NoSuchFieldException: CLASS_RESOLVER
這個(gè)錯(cuò)誤就通俗易懂多了,說(shuō)創(chuàng)建這個(gè) bean 失敗,原因是根本就沒(méi)有一個(gè) CLASS_RESOLVER 的字段,進(jìn)入 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 行通過(guò)工具類向 OgnlCache 的 CLASS_RESOLVER 設(shè)置值,但是這個(gè)字段在 OgnlCache 類不存在,這個(gè)其實(shí)是 mybatis 和 mpp 版本問(wèn)題,mybatis 的早期版本中,OgnlCache 沒(méi)有這個(gè)字段,因此修改下 mybatis 的版本就可以了,我這里修改到了 3.5.5
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency>
不過(guò)我后來(lái)發(fā)現(xiàn) mp 和 springboot 的依賴中其實(shí)已經(jīng)引入了 mybatis 的依賴,似乎把老的刪了就行,總之這個(gè)問(wèn)題跟 mybatis 的版本有關(guān)系
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- MyBatis/MyBatis-Plus同事務(wù)循環(huán)調(diào)用存儲(chǔ)過(guò)程獲取主鍵重復(fù)問(wèn)題分析及解決
- MyBatis-Plus主鍵生成策略的實(shí)現(xiàn)方法
- MyBatis-Plus 主鍵生成策略的幾種實(shí)現(xiàn)方式
- Mybatis-plus 雙主鍵的實(shí)現(xiàn)示例
- mybatis-plus插入一條數(shù)據(jù),獲取插入數(shù)據(jù)自動(dòng)生成的主鍵問(wèn)題
- Mybatis-plus插入數(shù)據(jù)遇到主鍵沒(méi)有默認(rèn)值的情況
- mybatis-plus內(nèi)置雪花算法主鍵重復(fù)問(wèn)題解決
相關(guān)文章
SpringBoot頂層接口實(shí)現(xiàn)類注入項(xiàng)目的方法示例
本文主要介紹了SpringBoot頂層接口實(shí)現(xiàn)類注入項(xiàng)目的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06
java二進(jìn)制運(yùn)算基礎(chǔ)知識(shí)點(diǎn)詳解
在本文里小編給大家分享了關(guān)于java二進(jìn)制運(yùn)算基礎(chǔ)知識(shí)點(diǎn)以及實(shí)例代碼內(nèi)容,需要的朋友們參考學(xué)習(xí)下。2019-08-08
Spring中@Conditional注解的詳細(xì)講解及示例
這篇文章主要介紹了Spring中@Conditional注解的詳細(xì)講解及示例,@Conditional是Spring4新提供的注解,它的作用是按照一定的條件進(jìn)行判斷,滿足條件給容器注冊(cè)bean,需要的朋友可以參考下2023-11-11
RocketMQ?producer容錯(cuò)機(jī)制源碼解析
這篇文章主要為大家介紹了RocketMQ?producer容錯(cuò)機(jī)制源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
java實(shí)現(xiàn)多人聊天工具(socket+多線程)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)多人聊天工具,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
基于SpringBoot與Mybatis實(shí)現(xiàn)SpringMVC Web項(xiàng)目
這篇文章主要介紹了基于SpringBoot與Mybatis實(shí)現(xiàn)SpringMVC Web項(xiàng)目的相關(guān)資料,需要的朋友可以參考下2017-04-04

