在關係型數據庫中,隨處可見表之間的鏈接,對級聯的表進行增刪改查也是程序員必備的基礎技能。關於Spring Boot整合Mybatis在以前已經詳細寫過,不熟悉的能夠回顧Spring Boot整合Mybatis並完成CRUD操做,這是本文操做的基礎。本文先準備一個測試的數據庫,而後使用MyBatis Generator進行部分代碼自動生成,再以一個例子來展現稍微高級點的操做:使用Mybatis完成級聯一對多的CRUD操做。php
數據庫用到三張表:user表,role表,user_role表。user表用來存儲用戶的信息;role表用來存儲角色信息;user_role表用來將user和role相關聯,存儲user和role的映射關係,使得一個用戶能夠有多個角色,每一個角色對應其中的一條記錄。html
新建user表前端
CREATE TABLE user(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(20),
password VARCHAR(20)
)
複製代碼
新建role表並插入數據java
CREATE TABLE role(
id INT PRIMARY KEY AUTO_INCREMENT,
rolename VARCHAR(20)
)
複製代碼
INSERT INTO `role`(`rolename`) VALUES ('後臺');
INSERT INTO `role`(`rolename`) VALUES ('前端');
INSERT INTO `role`(`rolename`) VALUES ('客戶端');
INSERT INTO `role`(`rolename`) VALUES ('AI');
INSERT INTO `role`(`rolename`) VALUES ('大數據');
複製代碼
結果如圖:mysql
新建關聯表user_rolegit
CREATE TABLE user_role(
id INT PRIMARY KEY AUTO_INCREMENT,
userid INT,
roleid INT
)
複製代碼
MyBatis Generator 是MyBatis 官方出品的一款代碼生成器,爲全部版本的MyBatis以及版本2.2.0以後的iBATIS版本生成代碼。咱們可使用它自動生成MyBatis的 mapper、dao、entity ,省去最簡單的重複代碼編寫。更多詳細狀況能夠查看官網。程序員
<!-- 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>
複製代碼
<?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" 大數據字段,不分表 -->
<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" /> <!-- 是否生成註釋代時間戳-->
</commentGenerator>
<!--數據庫連接-->
<jdbcConnection driverClass="${spring.datasource.driverClassName}" connectionURL="${spring.datasource.url}" userId="${spring.datasource.username}" password="${spring.datasource.password}">
</jdbcConnection>
<!-- 類型轉換 -->
<javaTypeResolver>
<!-- 是否使用bigDecimal, false可自動轉化如下類型(Long, Integer, Short, etc.) -->
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!--生成Model類存放位置-->
<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>
<!-- 數據庫表的信息 -->
<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>
複製代碼
mybatis-generator:generate
命令,點擊Run
按鈕便可自動生成代碼。自動生成代碼時遇到的一些坑github
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.
意思是在說不建議在沒有服務器身份驗證的狀況下創建SSL鏈接。根據MySQL 5.5.45+、5.6.26+和5.7.6+的要求,若是沒有設置顯式選項,則默認狀況下必須創建SSL鏈接。您須要經過設置useSSL=false
顯式禁用SSL,或者設置useSSL=true
併爲服務器證書驗證提供信任存儲。web
解決方案:在配置文件application.properties中數據庫鏈接後面加上&useSSL=true
。spring
[WARNING] Table Configuration user matched more than one table (spring_boot..user,mysql..user,webshop..user,jeece-iyangcong..user)...
具體信息如圖:
這是由於MySQL 8.0版本驅動將參數nullCatalogMeansCurrent
的默認值由true
改成了false
,在使用MyBatis Generator生成表對應的xml等時會掃描整個服務器裏面的所有數據庫中的表,而不是掃描對應數據庫的表。
解決方案:在配置文件application.properties中數據庫鏈接後面加上&nullCatalogMeansCurrent=true
。
若是不出意外的話,將會自動生成3個實體類文件,3個dao層文件,3個mapper.xml。這些代碼很長且沒有技術含量,在這裏就不貼出來的,真有須要能夠到文末的GitHub地址去查看。
接下來須要在Service和ServiceImpl中對dao層進行簡單的封裝,估計你們都知道該怎麼寫,在這裏也先不貼代碼了,詳見文末的GitHub地址。
添加用戶的邏輯是這樣的:後臺須要分兩步處理前端傳過來的username,password和roleids。第一步把username和password存入user表;第二步把第一步生成的userid和前端傳過來的roleids存入user_role表。這兩個操做步驟顯然是知足事務的ACID特性的。
Spring 支持編程式事務管理和聲明式事務管理兩種方式。編程式事務指的是經過編碼方式實現事務;聲明式事務基於 AOP,將具體業務邏輯與事務處理解耦。聲明式事務管理使業務代碼邏輯不受污染, 所以在實際使用中聲明式事務用的比較多。聲明式事務有兩種方式,一種是在配置文件(xml)中作相關的事務規則聲明,另外一種是基於 @Transactional 註解的方式。本文直接使用@Transactional註解實現事務,由於這種方式操做簡潔,代碼可讀性很高。
UserController中增長用戶的代碼以下:
@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測試添加用戶:
接口返回添加「添加成功」字樣,而後去數據庫中查看,user表中多了一條數據,user_role表中也插入了三條數據。顯然,這正是須要的結果,這是一個正確的操做。
刪除用戶的邏輯和添加用戶的邏輯很類似:第一步根據前端傳過來的id刪除user表中的記錄;第二步userid刪除user_role表中的記錄;這兩個步驟也是知足事務特性,也是使用@Transactional註解來實現。
代碼以下:
@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測試刪除用戶:
接口返回添加「刪除成功」字樣,而後去數據庫中查看,user表中id爲1的記錄被刪除了,user_role表中userid爲1的三條記錄也都刪除了。顯然,這正是須要的結果。
修改用戶邏輯:在user表中修改username和password,同時在user_role表中修改用戶和角色的映射記錄。修改用戶和角色映射記錄也就是先按照userid進行刪除記錄,而後再插入新的映射信息。在這裏一樣使用@Transactional註解來實現事務。 代碼以下:
@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進行刪除
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);
}
複製代碼
注意:當使用PostMan進行測試的時候,發現報錯:org.apache.ibatis.binding.BindingException: Parameter 'username' not found. Available parameters are [0, 1, 2, param3, param1, param2]
。
解決辦法:在dao層的Mapper.java代碼的參數加上@param
註解 。例如:
void updateByPrimaryKey(@Param("id")Integer id, @Param("username")String username, @Param("password")String password);
複製代碼
使用PostMan進行測試修改用戶:
返回結果沒有問題,再去數據庫查看,數據庫也沒有問題,更新操做完成。
查詢用戶的信息不只須要user表中的用戶信息,還須要user_role表中的用戶角色映射關係,還須要role表的角色信息。這也是須要表之間聯合的操做。
本文采用的方法是新建一個AccountDetail類來整合信息。
public class UserDetail {
private Integer userid;
private String username;
private String password;
private List<Integer> roleids;
private List<String> rolenames;
//省略getter和setter
}
複製代碼
這是整合信息的關鍵代碼:
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, "查詢成功", list);
}
複製代碼
使用PostMan進行測試查詢用戶信息:
能夠看到這個接口返回了全部的用戶信息,包括用戶的基本信息和角色信息,準確無誤。
Spring和MyBatis實現一對多關聯的增刪改查也有多種方式:可使用MyBatis來自定義SQL語句來實現;也可使用Spring的註解結合MyBatis自動生成的代碼來實現。我更喜歡後者,由於經過Mybatis Generator自動生成代碼之後,這些代碼就不須要再修改了,能夠直接封裝service和controller。但願本文對你們有用。
完整代碼:GitHub地址