Spring boot整合Mybatis實(shí)現(xiàn)級(jí)聯(lián)一對(duì)多CRUD操作的完整步驟
前言
在關(guān)系型數(shù)據(jù)庫(kù)中,隨處可見(jiàn)表之間的連接,對(duì)級(jí)聯(lián)的表進(jìn)行增刪改查也是程序員必備的基礎(chǔ)技能。關(guān)于Spring Boot整合Mybatis在之前已經(jīng)詳細(xì)寫(xiě)過(guò),不熟悉的可以回顧Spring Boot整合Mybatis并完成CRUD操作,這是本文操作的基礎(chǔ)。本文先準(zhǔn)備一個(gè)測(cè)試的數(shù)據(jù)庫(kù),然后使用MyBatis Generator進(jìn)行部分代碼自動(dòng)生成,再以一個(gè)例子來(lái)展示稍微高級(jí)點(diǎn)的操作:使用Mybatis完成級(jí)聯(lián)一對(duì)多的CRUD操作。
數(shù)據(jù)庫(kù)準(zhǔn)備
數(shù)據(jù)庫(kù)用到三張表:user表,role表,user_role表。user表用來(lái)存儲(chǔ)用戶(hù)的信息;role表用來(lái)存儲(chǔ)角色信息;user_role表用來(lái)將user和role相關(guān)聯(lián),存儲(chǔ)user和role的映射關(guān)系,使得一個(gè)用戶(hù)可以有多個(gè)角色,每個(gè)角色對(duì)應(yīng)其中的一條記錄。
新建user表
CREATE TABLE user( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(20), password VARCHAR(20) )
新建role表并插入數(shù)據(jù)
CREATE TABLE role( id INT PRIMARY KEY AUTO_INCREMENT, rolename VARCHAR(20) )
INSERT INTO `role`(`rolename`) VALUES ('后臺(tái)'); INSERT INTO `role`(`rolename`) VALUES ('前端'); INSERT INTO `role`(`rolename`) VALUES ('客戶(hù)端'); INSERT INTO `role`(`rolename`) VALUES ('AI'); INSERT INTO `role`(`rolename`) VALUES ('大數(shù)據(jù)');
結(jié)果如圖:
新建關(guān)聯(lián)表user_role
CREATE TABLE user_role( id INT PRIMARY KEY AUTO_INCREMENT, userid INT, roleid INT )
自動(dòng)生成代碼
MyBatis Generator 是MyBatis 官方出品的一款代碼生成器,為所有版本的MyBatis以及版本2.2.0之后的iBATIS版本生成代碼。我們可以使用它自動(dòng)生成MyBatis的 mapper、dao、entity ,省去最簡(jiǎn)單的重復(fù)代碼編寫(xiě)。更多詳細(xì)情況可以查看官網(wǎng)。
1、pom.xml添加依賴(lài)
<!-- MyBatis Generator插件 --> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.7</version> <configuration> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version> </dependency> </dependencies> </plugin>
2、在項(xiàng)目根目錄下面添加自動(dòng)生成代碼的配置文件generatorConfig.xml:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <!--配置文件信息--> <properties resource="application.properties"/> <!--defaultModelType="flat" 大數(shù)據(jù)字段,不分表 --> <context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat"> <property name="autoDelimitKeywords" value="true" /> <property name="beginningDelimiter" value="`" /> <property name="endingDelimiter" value="`" /> <property name="javaFileEncoding" value="utf-8" /> <plugin type="org.mybatis.generator.plugins.SerializablePlugin" /> <plugin type="org.mybatis.generator.plugins.ToStringPlugin" /> <!-- 注釋 --> <commentGenerator > <property name="suppressAllComments" value="true"/><!-- 是否取消注釋 --> <property name="suppressDate" value="true" /> <!-- 是否生成注釋代時(shí)間戳--> </commentGenerator> <!--數(shù)據(jù)庫(kù)鏈接--> <jdbcConnection driverClass="${spring.datasource.driverClassName}" connectionURL="${spring.datasource.url}" userId="${spring.datasource.username}" password="${spring.datasource.password}"> </jdbcConnection> <!-- 類(lèi)型轉(zhuǎn)換 --> <javaTypeResolver> <!-- 是否使用bigDecimal, false可自動(dòng)轉(zhuǎn)化以下類(lèi)型(Long, Integer, Short, etc.) --> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <!--生成Model類(lèi)存放位置--> <javaModelGenerator targetPackage="com.shangguan.mybatis1.entity" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> <property name="trimStrings" value="true"/> </javaModelGenerator> <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources" > <property name="enableSubPackages" value="false" /> </sqlMapGenerator> <javaClientGenerator targetPackage="com.shangguan.mybatis1.dao" targetProject="src/main/java" type="XMLMAPPER" > <property name="enableSubPackages" value="false" /> </javaClientGenerator> <!-- 數(shù)據(jù)庫(kù)表的信息 --> <table tableName="user" enableCountByExample="true" enableUpdateByExample="true" enableDeleteByExample="true" enableSelectByExample="true" selectByExampleQueryId="true"> <generatedKey column="id" sqlStatement="Mysql" identity="true" /> </table> <table tableName="role" enableCountByExample="true" enableUpdateByExample="true" enableDeleteByExample="true" enableSelectByExample="true" selectByExampleQueryId="true"> <generatedKey column="id" sqlStatement="Mysql" identity="true" /> </table> <table tableName="user_role" enableCountByExample="true" enableUpdateByExample="true" enableDeleteByExample="true" enableSelectByExample="true" selectByExampleQueryId="true"> <generatedKey column="id" sqlStatement="Mysql" identity="true" /> </table> </context> </generatorConfiguration>
3、使用Maven生成代碼:
我使用的是Eclipse,工程目錄右鍵Run as --> Maven build,在Goals中輸入mybatis-generator:generate命令,點(diǎn)擊Run按鈕即可自動(dòng)生成代碼。
自動(dòng)生成代碼時(shí)遇到的一些坑
報(bào)錯(cuò)信息:
Establishing SSL connection without server's identity verification is not recommended.
According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL
connection must be established by default if explicit option isn't set.
For compliance with existing applications not using
SSL the verifyServerCertificate property is set to 'false'.
You need either to explicitly disable SSL by setting useSSL=false,
or set useSSL=true and provide truststore for server certificate verification.
意思是在說(shuō)不建議在沒(méi)有服務(wù)器身份驗(yàn)證的情況下建立SSL連接。根據(jù)MySQL 5.5.45+、5.6.26+和5.7.6+的要求,如果沒(méi)有設(shè)置顯式選項(xiàng),則默認(rèn)情況下必須建立SSL連接。您需要通過(guò)設(shè)置useSSL=false顯式禁用SSL,或者設(shè)置useSSL=true并為服務(wù)器證書(shū)驗(yàn)證提供信任存儲(chǔ)。
解決方案:在配置文件application.properties中數(shù)據(jù)庫(kù)連接后面加上&useSSL=true。
雖然Maven提示“BUILD SUCCESS”,但是仔細(xì)看生成的代碼和數(shù)據(jù)庫(kù)是不匹配的,提示代碼被重寫(xiě),報(bào)錯(cuò)[WARNING] Table Configuration user matched more than one table (spring_boot..user,mysql..user,webshop..user,jeece-iyangcong..user)...具體信息如圖:
這是因?yàn)镸ySQL 8.0版本驅(qū)動(dòng)將參數(shù)nullCatalogMeansCurrent的默認(rèn)值由true改為了false,在使用MyBatis Generator生成表對(duì)應(yīng)的xml等時(shí)會(huì)掃描整個(gè)服務(wù)器里面的全部數(shù)據(jù)庫(kù)中的表,而不是掃描對(duì)應(yīng)數(shù)據(jù)庫(kù)的表。
解決方案:在配置文件application.properties中數(shù)據(jù)庫(kù)連接后面加上&nullCatalogMeansCurrent=true。
如果不出意外的話(huà),將會(huì)自動(dòng)生成3個(gè)實(shí)體類(lèi)文件,3個(gè)dao層文件,3個(gè)mapper.xml。這些代碼很長(zhǎng)且沒(méi)有技術(shù)含量,在這里就不貼出來(lái)的,真有需要可以到文末的GitHub地址去查看。
開(kāi)始CRUD
接下來(lái)需要在Service和ServiceImpl中對(duì)dao層進(jìn)行簡(jiǎn)單的封裝,估計(jì)大家都知道該怎么寫(xiě),在這里也先不貼代碼了,詳見(jiàn)文末的GitHub地址。
添加用戶(hù)
添加用戶(hù)的邏輯是這樣的:后臺(tái)需要分兩步處理前端傳過(guò)來(lái)的username,password和roleids。第一步把username和password存入user表;第二步把第一步生成的userid和前端傳過(guò)來(lái)的roleids存入user_role表。這兩個(gè)操作步驟顯然是滿(mǎn)足事務(wù)的ACID特性的。
Spring 支持編程式事務(wù)管理和聲明式事務(wù)管理兩種方式。編程式事務(wù)指的是通過(guò)編碼方式實(shí)現(xiàn)事務(wù);聲明式事務(wù)基于 AOP,將具體業(yè)務(wù)邏輯與事務(wù)處理解耦。聲明式事務(wù)管理使業(yè)務(wù)代碼邏輯不受污染, 因此在實(shí)際使用中聲明式事務(wù)用的比較多。聲明式事務(wù)有兩種方式,一種是在配置文件(xml)中做相關(guān)的事務(wù)規(guī)則聲明,另一種是基于 @Transactional 注解的方式。本文直接使用@Transactional注解實(shí)現(xiàn)事務(wù),因?yàn)檫@種方式操作簡(jiǎn)潔,代碼可讀性很高。
UserController中增加用戶(hù)的代碼如下:
@RequestMapping("/addUser") @Transactional(rollbackFor={RuntimeException.class, Exception.class}) public Result saveUser(@RequestParam(value = "username") String username, @RequestParam(value = "password") String password, @RequestParam(value = "roleids") List<Integer> roleids) { User user = new User(); user.setUsername(username); user.setPassword(password); userService.addUser(user); for(int i=0;i<roleids.size();i++) { UserRole userRole = new UserRole(); userRole.setUserid(user.getId()); userRole.setRoleid(roleids.get(i)); userRoleService.addUserRole(userRole); } return ResultUtils.result(States.errorCode.SUCCESS, "添加成功", null); }
使用PostMan測(cè)試添加用戶(hù):
接口返回添加“添加成功”字樣,然后去數(shù)據(jù)庫(kù)中查看,user表中多了一條數(shù)據(jù),user_role表中也插入了三條數(shù)據(jù)。顯然,這正是需要的結(jié)果,這是一個(gè)正確的操作。
刪除用戶(hù)
刪除用戶(hù)的邏輯和添加用戶(hù)的邏輯很相似:第一步根據(jù)前端傳過(guò)來(lái)的id刪除user表中的記錄;第二步userid刪除user_role表中的記錄;這兩個(gè)步驟也是滿(mǎn)足事務(wù)特性,也是使用@Transactional注解來(lái)實(shí)現(xiàn)。
代碼如下:
@RequestMapping("/deleteUserById") @Transactional(rollbackFor={RuntimeException.class, Exception.class}) public Result deleteUserById(Integer id) { userService.deleteUserById(id); List<UserRole> list = userRoleService.selectByUserId(id); for(int i=0;i<list.size();i++) { userRoleService.deleteByPrimaryKey(list.get(i).getId()); } return ResultUtils.result(States.errorCode.SUCCESS, "刪除成功", null); }
使用PostMan測(cè)試刪除用戶(hù):
接口返回添加“刪除成功”字樣,然后去數(shù)據(jù)庫(kù)中查看,user表中id為1的記錄被刪除了,user_role表中userid為1的三條記錄也都刪除了。顯然,這正是需要的結(jié)果。
修改用戶(hù)
修改用戶(hù)邏輯:在user表中修改username和password,同時(shí)在user_role表中修改用戶(hù)和角色的映射記錄。修改用戶(hù)和角色映射記錄也就是先按照userid進(jìn)行刪除記錄,然后再插入新的映射信息。在這里同樣使用@Transactional注解來(lái)實(shí)現(xiàn)事務(wù)。
代碼如下:
@RequestMapping("/updateUser") @Transactional(rollbackFor={RuntimeException.class, Exception.class}) public Result updateUser(@RequestParam(value = "id")Integer id, @RequestParam(value = "username") String username, @RequestParam(value = "password") String password, @RequestParam(value = "roleids") List<Integer> roleids) { userService.updateUser(id, username, password); //查找user_role然后按照id進(jìn)行刪除 List<UserRole> list = userRoleService.selectByUserId(id); for(int i=0;i<list.size();i++) { userRoleService.deleteByPrimaryKey(list.get(i).getId()); } //插入新的roleids for(int i=0;i<roleids.size();i++) { UserRole record = new UserRole(); record.setUserid(id); record.setRoleid(roleids.get(i)); userRoleService.addUserRole(record); } return ResultUtils.result(States.errorCode.SUCCESS, "更新成功", null); }
注意:當(dāng)使用PostMan進(jìn)行測(cè)試的時(shí)候,發(fā)現(xiàn)報(bào)錯(cuò):org.apache.ibatis.binding.BindingException: Parameter 'username' not found.
Available parameters are [0, 1, 2, param3, param1, param2]。
解決辦法:在dao層的Mapper.java代碼的參數(shù)加上@param注解 。例如:
void updateByPrimaryKey(@Param("id")Integer id, @Param("username")String username, @Param("password")String password);
使用PostMan進(jìn)行測(cè)試修改用戶(hù):
返回結(jié)果沒(méi)有問(wèn)題,再去數(shù)據(jù)庫(kù)查看,數(shù)據(jù)庫(kù)也沒(méi)有問(wèn)題,更新操作完成。
查詢(xún)用戶(hù)信息
查詢(xún)用戶(hù)的信息不僅需要user表中的用戶(hù)信息,還需要user_role表中的用戶(hù)角色映射關(guān)系,還需要role表的角色信息。這也是需要表之間聯(lián)合的操作。
本文采用的方法是新建一個(gè)AccountDetail類(lèi)來(lái)整合信息。
public class UserDetail { private Integer userid; private String username; private String password; private List<Integer> roleids; private List<String> rolenames; //省略getter和setter }
這是整合信息的關(guān)鍵代碼:
public List<UserDetail> selectAll(){ List<User> userList = new ArrayList<>(); List<UserDetail> details = new ArrayList<>(); try{ userList = userMapper.selectAll(); details = getUserDetails(userList); }catch(Exception e){ e.printStackTrace(); return details; } return details; } public List<UserDetail> getUserDetails(List<User> lists){ List<UserDetail> details = new ArrayList<>(); if(lists == null || lists.size() < 1){ return details; } Map<Integer, String> nameMap = roleService.getNameMap(); for(int i=0; i< lists.size();i++){ User user = lists.get(i); UserDetail detail = new UserDetail(); detail.setUserid(user.getId()); detail.setUsername(user.getUsername()); detail.setPassword(user.getPassword()); List<Integer> roleids = new ArrayList<>(); List<String> rolenames = new ArrayList<>(); List<UserRole> userroles = userRoleMapper.selectByUserId(user.getId()); for(int j=0;j<userroles.size();j++) { roleids.add(userroles.get(j).getRoleid()); rolenames.add(nameMap.get(userroles.get(j).getRoleid())); } detail.setRoleids(roleids); detail.setRolenames(rolenames); details.add(detail); } return details; }
這是封裝的接口:
@RequestMapping("/getAllUser") public Result getAllUser() { List<UserDetail> list = userService.selectAll(); return ResultUtils.result(States.errorCode.SUCCESS, "查詢(xún)成功", list); }
使用PostMan進(jìn)行測(cè)試查詢(xún)用戶(hù)信息:
可以看到這個(gè)接口返回了所有的用戶(hù)信息,包括用戶(hù)的基本信息和角色信息,準(zhǔn)確無(wú)誤。
總結(jié)
Spring和MyBatis實(shí)現(xiàn)一對(duì)多關(guān)聯(lián)的增刪改查也有多種方式:可以使用MyBatis來(lái)自定義SQL語(yǔ)句來(lái)實(shí)現(xiàn);也可以使用Spring的注解結(jié)合MyBatis自動(dòng)生成的代碼來(lái)實(shí)現(xiàn)。我更喜歡后者,因?yàn)橥ㄟ^(guò)Mybatis Generator自動(dòng)生成代碼以后,這些代碼就不需要再修改了,可以直接封裝service和controller。希望本文對(duì)大家有用。
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。
- Mybatis實(shí)現(xiàn)增刪改查(CRUD)實(shí)例代碼
- Mybatis實(shí)現(xiàn)數(shù)據(jù)的增刪改查實(shí)例(CRUD)
- Spring Boot整合Mybatis并完成CRUD操作的實(shí)現(xiàn)示例
- 基于Mybatis-Plus的CRUD的實(shí)現(xiàn)
- MyBatis Plus配置日志CRUD的使用詳解
- MybatisPlus,無(wú)XML分分鐘實(shí)現(xiàn)CRUD操作
- Mybatis-Plus 通用CRUD的詳細(xì)操作
- mybatisplus?復(fù)合主鍵(多主鍵)?CRUD示例詳解
- MyBatis實(shí)現(xiàn)CRUD的示例代碼
相關(guān)文章
Spring占位符Placeholder的實(shí)現(xiàn)原理解析
這篇文章主要介紹了Spring占位符Placeholder的實(shí)現(xiàn)原理,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03SpringBoot項(xiàng)目實(shí)戰(zhàn)之?dāng)?shù)據(jù)交互篇
這篇文章主要給大家介紹了關(guān)于SpringBoot項(xiàng)目實(shí)戰(zhàn)之?dāng)?shù)據(jù)交互篇的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-03-03淺談Java中的final關(guān)鍵字與C#中的const, readonly關(guān)鍵字
下面小編就為大家?guī)?lái)一篇淺談Java中的final關(guān)鍵字與C#中的const, readonly關(guān)鍵字。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10SpringBoot?Loki安裝簡(jiǎn)介及實(shí)戰(zhàn)思路
這篇文章主要為大家介紹了SpringBoot?Loki安裝簡(jiǎn)介及實(shí)戰(zhàn)思路詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪的相關(guān)資料2022-11-11簡(jiǎn)單了解spring cloud 網(wǎng)關(guān)服務(wù)
這篇文章主要介紹了簡(jiǎn)單了解spring cloud 網(wǎng)關(guān)服務(wù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10Spring框架事務(wù)屬性中事務(wù)隔離級(jí)別與傳播行為全面講解
這篇文章主要介紹了Spring框架聲明式事務(wù)的事務(wù)隔離級(jí)別和事務(wù)傳播行為,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-11-11SpringBoot+Redis執(zhí)行l(wèi)ua腳本的5種方式總結(jié)
Lua是一種快速、輕量級(jí)的腳本語(yǔ)言,廣泛應(yīng)用于各種領(lǐng)域,包括數(shù)據(jù)庫(kù),Redis作為一個(gè)內(nèi)嵌Lua解釋器的NoSQL數(shù)據(jù)庫(kù),允許通過(guò)Lua腳本在服務(wù)器端執(zhí)行一些復(fù)雜的操作,本文給大家介紹了使用SpringBoot Redis執(zhí)行l(wèi)ua腳本的五種方式,需要的朋友可以參考下2023-11-11