linux怎么查看本机内存大小
246
2022-10-04
使用AOP+反射实现自定义Mybatis多表关联查询
目录一、需求二、核心代码MapToDoMapIDualMapperDualMapperDoMapAspect三、使用方法SysUserSysRoleSysPermissionSysUserServiceDoMapTests测试数据测试结果
一、需求
目前使用的ORM框架是Mybatis Plus,是Mybatis的增强框架,基础的CRUD的方法都集成了,开发起来很是方便。但是项目中总是需要多表关联查询。
Mybatis的多表关联有两种
一、在Mapper中使用@Result @One @Many注解
二、在xml文件中配置对应的resultMap和关联标签
使用起来很不方便。JPA倒是有多表关联的注解实现,但是不想再引入另一个ORM框架。
目前的需求是增强现有的查询,使用简单的注解即可实现多表关联。
二、核心代码
github:https://github.com/sushengbuyu/mybatis-mapping-demo
实现该功能总共需要四个文件
两个自定义注解,一个虚拟Mapper,一个切面处理类
源码
MapTo
自定义映射注解,标注需要映射处理的字段
import java.lang.annotation.*;
/**
* @author victor
* @desc 自定义多表关联映射注解
* @date 2022/5/23
*/
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MapTo {
/**
* 映射目标
*/
Class> targetClass();
/**
* 执行SQL
*/
String sql();
/**
* 嵌套处理
* 为true时,如果映射的对象类中有映射字段,也执行映射操作
*/
boolean doDeep() default false;
}
DoMap
自定义映射处理注解,标注需要执行映射的方法
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author victor
* @desc 标注该需要执行映射处理的方法
* @date 2022/5/23
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DoMap {
/**
* 需要处理映射的类
* @return Class
*/
Class> targetClass();
/**
* spel表达式
* 默认为空
* @return String
*/
String spel() default "";
}
IDualMapper
虚拟Mapper,用来执行自定义SQL
import java.util.List;
import java.util.Map;
/**
* @author victor
* @desc 虚拟Mapper
* @date 2022/5/23
*/
@Mapper
public interface IDualMapper {
/**
* 执行自定义SQL
* @param sql sql
* @return List
*/
List
}
DualMapper
使用者自行实现DualMapper,解除mybatis强依赖
package sushengbuyu.maptodemo.sys.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import sushengbuyu.maptodemo.aop.IDualMapper;
import java.util.List;
import java.util.Map;
/**
* @author victor
* @desc 虚拟Mapper
* @date 2022/5/23
*/
@Mapper
public interface DualMapper extends IDualMapper {
/**
* 执行自定义SQL
* @param sql sql
* @return List
*/
@Select("${sql}")
List
}
DoMapAspect
切面处理类,核心代码,执行映射操作
package sushengbuyu.maptodemo.aop;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author victor
* @iCGECocbndesc 自定义关联映射切面
* @date 2022/5/23
*/
@Component
@Aspect
public class DoMapAspect {
private final static Logger log = LoggerFactory.getLogger(DoMapAspect.class);
/**
* 保存MapTo映射关系
* key 映射字段所在类
* value 映射字段集合
*/
private static final Map
private final IDualMapper dualMapper;
public DoMapAspect(DualMapper dualMapper) {
this.dualMapper = dualMapper;
}
/**
* 初始化映射关系
* 扫描指定包下所有类,找出带有MapTo注解的字段
* 存储映射数据
*/
@PostConstruct
public void initMap() {
// 初始化所有MapTo对象
// 扫描所有类
Set
int totalField = 0;
// 找出使用MapTo注解的对象
for (Class> c : classes) {
Field[] fields = c.getDeclaredFields();
for (Field f : fields) {
if (null != f.getAnnotation(MapTo.class)){
log.info("找到需要映射的字段: 类名:{} - 字段名:{}", c.getName(), f.getName());
// 保存映射关系
Set
if (MAPPING.containsKey(c)) {
set = MAPPING.get(c);
} else {
set = new HashSet<>();
}
set.add(f.getName());
MAPPING.put(c, set);
totalField++;
}
}
}
log.info("总计{}个映射类,{}个映射字段", MAPPING.size(), totalField);
}
/**
* 切点
* @param doMap 执行映射注解
*/
@Pointcut("@annotation(doMap)")
public void point(DoMap doMap){}
/**
* 处理关联映射
* @param point 切点
* @param doMap 映射处理配置
* @return Object
* @throws Throwable 异常
*/
@Around(value = "@annotation(doMap)")
public Object doMap(ProceedingJoinPoint point, DoMap doMap) throws Throwable {
// 执行切面方法
Object obj = point.proceed();
try {
Object relObj = obj;
if (StringUtils.hasLength(doMap.spel())) {
// 如果使用了SPEL表达式,则从返回值中获取处理对象
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(doMap.spel());
relObj = expression.getValue(obj);
}
// 获取映射类
Class> c = doMap.targetClass();
// 映射处理
doMapping(c, relObj);
} catch (Exception e) {
log.error("映射异常", e);
}
log.info("返回对象:{}", obj);
return obj;
}
private void doMapping(Class> c, Object obj) throws Exception {
if (obj instanceof Collection) {
// 集合
Collection> co = (Collection>) obj;
for (Object o : co) {
mapping(c, o);
}
} else {
// 单个对象
mapping(c, obj);
}
}
private void mapping(Class> c, Object obj) throws Exception {
// 判断是否有映射关系
if (MAPPING.containsKey(c)) {
log.info("处理映射类:{}", c.getName());
// 从缓存中获取映射字段名称
Set
for (String fieldName : filedNames) {
Field f = c.getDeclaredField(fieldName);
log.info("处理映射字段:{}", f.getName());
// 获取映射注解
MapTo mapTo = f.getAnnotation(MapTo.class);
log.info("映射配置:{}", mapTo);
// 设置私有字段访问权限
f.setAccessible(true);
// 执行SQL
String sql = mapTo.sql();
// 处理SQL变量
List
log.info("SQL变量:{}", res);
for (String re : res) {
Field ff = obj.getClass().getDeclaredField(re.substring(2, re.length()-1));
ff.setAccessible(true);
Object o = ff.get(obj);
sql = sql.replace(re, o.toString());
}
log.info("最终SQL:{}", sql);
List
Object v = null;
if (Collection.class.isAssignableFrom(f.getType())) {
// 集合对象
if (results.size() > 0) {
v = results.stream()
.map(r -> mapToBean(r, mapTo.targetClass()))
.collect(Collectors.toList());
}
} else {
// 单个对象
if (results.size() > 1) {
log.error("预计返回一条数据,实际返回多条数据。执行SQL: {}", sql);
} else if (results.size() == 1) {
// 转换结果,赋值
v = mapToBean(results.get(0), mapTo.targetClass());
}
}
if (v != null && mapTo.doDeep()) {
doMapping(mapTo.targetClass(), v);
}
f.set(obj, v);
}
}
}
/**
* Map对象转Bean
* @param map map
* @param clazz bean
* @return bean
*/
private Object mapToBean(Map, ?> map, Class> clazz) {
try {
return BeanUtil.fillBeanWithMap(map, clazz.newInstance(), true);
} catch (InstantiationException | IllegalAccessException e) {
log.error("实例化异常", e);
return null;
}
}
}
三、使用方法
测试类
SysUser
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import java.util.List;
import java.util.StringJoiner;
/**
* @author victor
* @desc 系统用户
* @date 2022/5/17
*/
public class SysUser implements Serializable {
private static final long serialVersionUID = 4855472141572371097L;
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 登录用户名
*/
private String username;
/**
* 登录密码
*/
private String password;
/**
* 昵称
*/
private String nickName;
@MapTo(targetClass = SysRole.class
, doDeep = true
, sql = "SELECT * FROM sys_role WHERE user_id=${id}")
@TableField(exist = false)
private SysRole sysRole;
@MapTo(targetClass = SysRole.class
, doDeep = true
, sql = "SELECT * FROM sys_role WHERE user_id=${id}")
@TableField(exist = false)
private List
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public SysRole getSysRole() {
return sysRole;
}
public void setSysRole(SysRole sysRole) {
this.sysRole = sysRole;
}
public List
return roleList;
}
public void setRoleList(List
this.roleList = roleList;
}
@Override
public String toString() {
return new StringJoiner(", ", SysUser.class.getSimpleName() + "[", "]")
.add("id=" + id)
.add("username='" + username + "'")
.add("password='" + password + "'")
.add("nickName='" + nickName + "'")
.add("sysRole=" + sysRole)
.add("roleList=" + roleList)
.toString();
}
}
SysRole
package sushengbuyu.maptodemo.sys.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import sushengbuyu.maptodemo.aop.MapTo;
import java.util.List;
import java.util.StringJoiner;
/**
* @author victor
* @desc 说明
* @date 2022/5/23
*/
public class SysRole {
@TableId
private Long id;
private Long userId;
private String name;
@MapTo(targetClass = SysPermission.class
, sql = "SELECT p.* FROM sys_permission p " +
"LEFT JOIN sys_role_permission rp ON p.id = rp.perm_id " +
"WHERE rp.role_id = ${id}")
@TableField(exist = false)
private List
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public List
return permissionList;
}
public void setPermissionList(List
this.permissionList = permissionList;
}
@Override
public String toString() {
return new StringJoiner(", ", SysRole.class.getSimpleName() + "[", "]")
.add("id=" + id)
.add("userId=" + userId)
.add("name='" + name + "'")
.toString();
}
}
SysPermission
package sushengbuyu.maptodemo.sys.po;
import java.util.StringJoiner;
/**
* @author victor
* @desc 说明
* @date 2022/5/25
*/
public class SysPermission {
private Long id;
private String name;
private Integer type;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
@Override
public String toString() {
return new StringJoiner(", ", SysPermission.class.getSimpleName() + "[", "]")
.add("id=" + id)
.add("name='" + name + "'")
.add("type=" + type)
.toString();
}
}
SysUserService
测试用例就常见的三种, 查单个,查列表,查分页
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.io.Serializable;
import java.util.List;
/**
* @author victor
* @desc 说明
* @date 2022/5/17
*/
@Service
public class SysUserService extends ServiceImpl
@DoMap(targetClass = SysUser.class)
@Override
public SysUser getById(Serializable id) {
return super.getById(id);
}
@DoMap(targetClass = SysUser.class)
public List
QueryWrapper
return baseMapper.selectList(wrapper);
}
/**
* 从Page中取records作为处理对象
*/
@DoMap(targetClass = SysUser.class, spel = "records")
public Page
QueryWrapper
Page
return baseMapper.selectPage(p, wrapper);
}
}
DoMapTests
import cn.hutool.json.JSONUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
//@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class DoMapTests {
@Autowired
private SysUserService service;
@Test
void single() {
System.out.println(JSONUtil.toJsonPrettyStr(service.getById(1)));
}
@Test
void list() {
System.out.println(JSONUtil.toJsonPrettyStr(service.listAll()));
}
@Test
void page() {
System.out.println(JSONUtil.toJsonPrettyStr(service.page()));
}
}
测试数据
测试结果
single
{
"nickName": "aa11",
"roleList": [
{
"permissionList": [
{
"type": 0,
"name": "add",
"id": 1
},
{
"type": 0,
"name": "query",
"id": 2
}
],
"userId": 1,
"name": "r1",
"id": 11
},
{
"permissionList": [
{
"type": 0,
"name": "del",
"id": 3
}
],
"userId": 1,
"name": "r2",
"id": 12
}
],
"password": "123456",
"id": 1,
"username": "a1"
}
list
[
{
"nickName": "aa11",
"roleList": [
{
"permissionList": [
{
"type": 0,
"name": "add",
"id": 1
},
{
"type": 0,
"name": "query",
"id": 2
}
],
"userId": 1,
"name": "r1",
"id": 11
},
{
"permissionList": [
{
"type": 0,
"name": "del",
"id": 3
}
],
"userId": 1,
"name": "r2",
"id": 12
}
],
"password": "123456",
"id": 1,
"username": "a1"
},
{
"sysRole": {
"userId": 2,
"name": "r3",
"id": 13
},
"nickName": "aa22",
"roleList": [
{
"userId": 2,
"name": "r3",
"id": 13
}
],
"password": "123456",
"id": 2,
"username": "a2"
}
]
page
{
"optimizeCountSql": true,
"records": [
{
"nickName": "aa11",
"roleList": [
{
"permissionList": [
{
"type": 0,
"name": "add",
"id": 1
},
{
"type": 0,
"name": "query",
"id": 2
}
],
"userId": 1,
"name": "r1",
"id": 11
},
{
"permissionList": [
{
"type": 0,
"name": "del",
"id": 3
}
],
"userId": 1,
"name": "r2",
"id": 12
}
],
"password": "123456",
"id": 1,
"username": "a1"
},
{
"sysRole": {
"userId": 2,
"name": "r3",
"id": 13
},
"nickName": "aa22",
"roleList": [
{
"userId": 2,
"name": "r3",
"id": 13
}
],
"password": "123456",
"id": 2,
"username": "a2"
}
],
"searchCount": true,
"total": 0,
"current": 1,
"size": 10,
"orders": [
]
}
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~