欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用AOP+反射實(shí)現(xiàn)自定義Mybatis多表關(guān)聯(lián)查詢

 更新時(shí)間:2022年05月25日 15:12:51   作者:sushengbuyu  
這篇文章主要介紹了使用AOP+反射實(shí)現(xiàn)自定義Mybatis多表關(guān)聯(lián),目前的需求是增強(qiáng)現(xiàn)有的查詢,使用簡單的注解即可實(shí)現(xiàn)多表關(guān)聯(lián),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下

一、需求

目前使用的ORM框架是Mybatis Plus,是Mybatis的增強(qiáng)框架,基礎(chǔ)的CRUD的方法都集成了,開發(fā)起來很是方便。但是項(xiàng)目中總是需要多表關(guān)聯(lián)查詢。

Mybatis的多表關(guān)聯(lián)有兩種

一、在Mapper中使用@Result @One @Many注解

二、在xml文件中配置對應(yīng)的resultMap和關(guān)聯(lián)標(biāo)簽

使用起來很不方便。JPA倒是有多表關(guān)聯(lián)的注解實(shí)現(xiàn),但是不想再引入另一個(gè)ORM框架。

目前的需求是增強(qiáng)現(xiàn)有的查詢,使用簡單的注解即可實(shí)現(xiàn)多表關(guān)聯(lián)。

二、核心代碼

GitHub:https://github.com/sushengbuyu/mybatis-mapping-demo

實(shí)現(xiàn)該功能總共需要四個(gè)文件

兩個(gè)自定義注解,一個(gè)虛擬Mapper,一個(gè)切面處理類

源碼

MapTo

自定義映射注解,標(biāo)注需要映射處理的字段

import java.lang.annotation.*;
/**
 * @author victor
 * @desc 自定義多表關(guān)聯(lián)映射注解
 * @date 2022/5/23
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MapTo {
    /**
     * 映射目標(biāo)
     */
    Class<?> targetClass();

    /**
     * 執(zhí)行SQL
     */
    String sql();

    /**
     * 嵌套處理
     * 為true時(shí),如果映射的對象類中有映射字段,也執(zhí)行映射操作
     */
    boolean doDeep() default false;
}

DoMap

自定義映射處理注解,標(biāo)注需要執(zhí)行映射的方法

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * @author victor
 * @desc 標(biāo)注該需要執(zhí)行映射處理的方法
 * @date 2022/5/23
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DoMap {
    /**
     * 需要處理映射的類
     * @return Class
     */
    Class<?> targetClass();

    /**
     * spel表達(dá)式
     * 默認(rèn)為空
     * @return String
     */
    String spel() default "";
}

IDualMapper

虛擬Mapper,用來執(zhí)行自定義SQL

import java.util.List;
import java.util.Map;
/**
 * @author victor
 * @desc 虛擬Mapper
 * @date 2022/5/23
 */
@Mapper
public interface IDualMapper {

    /**
     * 執(zhí)行自定義SQL
     * @param sql sql
     * @return List<Map<String, Object>>
     */
    List<Map<String, Object>> executeSql(String sql);
}

DualMapper

使用者自行實(shí)現(xiàn)DualMapper,解除mybatis強(qiáng)依賴

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 {
    /**
     * 執(zhí)行自定義SQL
     * @param sql sql
     * @return List<Map<String, Object>>
     */
    @Select("${sql}")
    List<Map<String, Object>> executeSql(@Param("sql") String sql);
}

DoMapAspect

切面處理類,核心代碼,執(zhí)行映射操作

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
 * @desc 自定義關(guān)聯(lián)映射切面
 * @date 2022/5/23
 */
@Component
@Aspect
public class DoMapAspect {
    private final static Logger log = LoggerFactory.getLogger(DoMapAspect.class);
    /**
     * 保存MapTo映射關(guān)系
     * key 映射字段所在類
     * value 映射字段集合
     */
    private static final Map<Class<?>, Set<String>> MAPPING = new HashMap<>(8);
    private final IDualMapper dualMapper;
    public DoMapAspect(DualMapper dualMapper) {
        this.dualMapper = dualMapper;
    }
    /**
     * 初始化映射關(guān)系
     * 掃描指定包下所有類,找出帶有MapTo注解的字段
     * 存儲映射數(shù)據(jù)
     */
    @PostConstruct
    public void initMap() {
        // 初始化所有MapTo對象
        // 掃描所有類
        Set<Class<?>> classes = ClassUtil.scanPackage("sushengbuyu.maptodemo");
        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());
                    // 保存映射關(guān)系
                    Set<String> set;
                    if (MAPPING.containsKey(c)) {
                        set = MAPPING.get(c);
                    } else {
                        set = new HashSet<>();
                    }
                    set.add(f.getName());
                    MAPPING.put(c, set);
                    totalField++;
                }
            }
        }
        log.info("總計(jì){}個(gè)映射類,{}個(gè)映射字段", MAPPING.size(), totalField);
    }
    /**
     * 切點(diǎn)
     * @param doMap 執(zhí)行映射注解
     */
    @Pointcut("@annotation(doMap)")
    public void point(DoMap doMap){}
    /**
     * 處理關(guān)聯(lián)映射
     * @param point 切點(diǎn)
     * @param doMap 映射處理配置
     * @return Object
     * @throws Throwable 異常
     */
    @Around(value = "@annotation(doMap)")
    public Object doMap(ProceedingJoinPoint point, DoMap doMap) throws Throwable {
        // 執(zhí)行切面方法
        Object obj = point.proceed();
        try {
            Object relObj = obj;
            if (StringUtils.hasLength(doMap.spel())) {
                // 如果使用了SPEL表達(dá)式,則從返回值中獲取處理對象
                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 {
            // 單個(gè)對象
            mapping(c, obj);
        }
    }
    private void mapping(Class<?> c, Object obj) throws Exception {
        // 判斷是否有映射關(guān)系
        if (MAPPING.containsKey(c)) {
            log.info("處理映射類:{}", c.getName());
            // 從緩存中獲取映射字段名稱
            Set<String> filedNames = MAPPING.get(c);
            for (String fieldName : filedNames) {
                Field f = c.getDeclaredField(fieldName);
                log.info("處理映射字段:{}", f.getName());
                // 獲取映射注解
                MapTo mapTo = f.getAnnotation(MapTo.class);
                log.info("映射配置:{}", mapTo);
                // 設(shè)置私有字段訪問權(quán)限
                f.setAccessible(true);
                // 執(zhí)行SQL
                String sql = mapTo.sql();
                // 處理SQL變量
                List<String> res = ReUtil.findAll("\$\{(.*?)}", sql, 0);
                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<Map<String, Object>> results = dualMapper.executeSql(sql);
                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 {
                    // 單個(gè)對象
                    if (results.size() > 1) {
                        log.error("預(yù)計(jì)返回一條數(shù)據(jù),實(shí)際返回多條數(shù)據(jù)。執(zhí)行SQL: {}", sql);
                    } else if (results.size() == 1) {
                        // 轉(zhuǎn)換結(jié)果,賦值
                        v = mapToBean(results.get(0), mapTo.targetClass());
                    }
                }
                if (v != null && mapTo.doDeep()) {
                    doMapping(mapTo.targetClass(), v);
                }
                f.set(obj, v);
            }
        }
    }
    /**
     * Map對象轉(zhuǎn)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("實(shí)例化異常", 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 系統(tǒng)用戶
 * @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<SysRole> roleList;

    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<SysRole> getRoleList() {
        return roleList;
    }
    public void setRoleList(List<SysRole> roleList) {
        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<SysPermission> permissionList;
    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<SysPermission> getPermissionList() {
        return permissionList;
    }
    public void setPermissionList(List<SysPermission> permissionList) {
        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

測試用例就常見的三種, 查單個(gè),查列表,查分頁

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<SysUserMapper, SysUser> {
    @DoMap(targetClass = SysUser.class)
    @Override
    public SysUser getById(Serializable id) {
        return super.getById(id);
    }
    @DoMap(targetClass = SysUser.class)
    public List<SysUser> listAll() {
        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        return baseMapper.selectList(wrapper);
    }
    /**
     * 從Page中取records作為處理對象
     */
    @DoMap(targetClass = SysUser.class, spel = "records")
    public Page<SysUser> page() {
        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        Page<SysUser> p = new Page<>(1, 10);
        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()));
    }
}

測試數(shù)據(jù)

測試結(jié)果

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": [
    ]
}

到此這篇關(guān)于使用AOP+反射實(shí)現(xiàn)自定義Mybatis多表關(guān)聯(lián)的文章就介紹到這了,更多相關(guān)Mybatis多表關(guān)聯(lián)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 深入理解Java中@Accessors和@Builder

    深入理解Java中@Accessors和@Builder

    本文主要介紹了@Accessors和@Builder使用,區(qū)別與坑,對使用這兩個(gè)注解的有一定的幫助,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-08-08
  • Java設(shè)計(jì)模式之策略模式定義與用法詳解

    Java設(shè)計(jì)模式之策略模式定義與用法詳解

    這篇文章主要介紹了Java設(shè)計(jì)模式之策略模式定義與用法,結(jié)合具體實(shí)例形式詳細(xì)分析了Java策略模式的概念、原理、定義及相關(guān)操作技巧,需要的朋友可以參考下
    2018-02-02
  • springboot項(xiàng)目打包成jar包的圖文教程

    springboot項(xiàng)目打包成jar包的圖文教程

    有時(shí)候我們會用IDEA來開發(fā)一些小工具,需要打成可運(yùn)行的JAR包,這篇文章主要給大家介紹了關(guān)于springboot項(xiàng)目打包成jar包的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • Mybatis-Plus的多數(shù)據(jù)源你了解嗎

    Mybatis-Plus的多數(shù)據(jù)源你了解嗎

    這篇文章主要為大家詳細(xì)介紹了Mybatis-Plus的多數(shù)據(jù)源,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • Spring?Boot統(tǒng)一接口返回及全局異常處理

    Spring?Boot統(tǒng)一接口返回及全局異常處理

    這篇文章主要介紹了Spring?Boot統(tǒng)一接口返回及全局異常處理,文章圍繞主題展開相關(guān)資料,具有一定的參考價(jià)值需要的小伙伴可以參考一下
    2022-04-04
  • java 實(shí)現(xiàn)MD5加密算法的簡單實(shí)例

    java 實(shí)現(xiàn)MD5加密算法的簡單實(shí)例

    這篇文章主要介紹了java 實(shí)現(xiàn)MD5加密算法的簡單實(shí)例的相關(guān)資料,這里提供實(shí)例幫助大家應(yīng)用這樣的加密算法,需要的朋友可以參考下
    2017-09-09
  • Jmeter參數(shù)化實(shí)現(xiàn)原理及過程解析

    Jmeter參數(shù)化實(shí)現(xiàn)原理及過程解析

    這篇文章主要介紹了Jmeter參數(shù)化實(shí)現(xiàn)原理及過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • SpringBoot+Jpa項(xiàng)目配置雙數(shù)據(jù)源的實(shí)現(xiàn)

    SpringBoot+Jpa項(xiàng)目配置雙數(shù)據(jù)源的實(shí)現(xiàn)

    本文主要介紹了SpringBoot+Jpa項(xiàng)目配置雙數(shù)據(jù)庫源的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Java基于API接口爬取商品數(shù)據(jù)的示例代碼

    Java基于API接口爬取商品數(shù)據(jù)的示例代碼

    Java作為一種流行的編程語言,可以用于編寫程序來調(diào)用這些API接口,從而獲取商品數(shù)據(jù),本文將介紹如何使用Java基于API接口爬取商品數(shù)據(jù),包括請求API、解析JSON數(shù)據(jù)、存儲數(shù)據(jù)等步驟,并提供相應(yīng)的代碼示例,感興趣的朋友跟隨小編一起看看吧
    2023-10-10
  • Java線程等待用法實(shí)例分析

    Java線程等待用法實(shí)例分析

    這篇文章主要介紹了Java線程等待用法,結(jié)合實(shí)例形式分析了obj.wait()實(shí)現(xiàn)線程等待相關(guān)原理與操作技巧,需要的朋友可以參考下
    2018-09-09

最新評論