在关系型数据库中,随处可见表之间的链接,对级联的表进行增删改查也是程序员必备的基础技能。关于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地址