mybatisPlus beforeGetBoundSql和setProperties使用场景介绍及代码示例

2024-01-03 15:50:18

beforeGetBoundSql 和 setProperties 都是 InnerInterceptor 接口中定义的默认方法,可以用于自定义拦截器实现。

beforeGetBoundSql 方法会在 MyBatis 获取 BoundSql 对象之前被调用,可以通过该方法来修改或扩展 SQL 语句,例如添加或删除条件、修改表名等。常见的使用场景包括:

数据权限控制:在查询数据时,自动添加当前用户可访问的数据范围的 WHERE 条件。
多租户支持:在查询数据时,自动添加租户 ID 的 WHERE 条件,以区分不同租户的数据。
动态表名:根据不同的请求参数,动态修改 SQL 语句中的表名,以实现数据分片或数据隔离等功能。

需要注意的是,修改 BoundSql 对象时,需要使用 MetaObject 对象来操作属性,以保证 MyBatis 的内部状态正确。

setProperties 方法会在创建拦截器实例时被调用,可以用于初始化拦截器的属性或资源,例如加密算法、缓存对象等。常见的使用场景包括:

加密解密:对数据库中的敏感数据进行加密,查询数据时进行解密。
缓存优化:通过缓存某些查询结果来提高系统性能,可以将缓存对象作为拦截器的属性来管理。
数据库连接池:创建数据库连接池对象并缓存起来,减少每次请求时创建连接池的开销。

需要注意的是,setProperties 方法中的参数 Properties 是一个键值对集合,可以通过该对象获取配置文件中定义的属性值。在编写拦截器时,需要定义对应的属性,并在 setProperties 方法中将其赋值给拦截器的成员变量。

代码示例:
beforeGetBoundSql 和 setProperties 两个方法都是 InnerInterceptor 接口中定义的默认方法,下面分别介绍其作用和示例代码。

在 MyBatis 中,BoundSql 对象表示了一个 SQL 语句的信息,包括 SQL 语句本身和它所需要的参数。在执行 SQL 之前,MyBatis 会通过解析 XML 配置文件和 Mapper 接口,生成对应的 BoundSql 对象。

beforeGetBoundSql 方法允许开发者在获取 BoundSql 对象之前进行必要的操作,例如设置参数、修改 SQL 等。下面是一个示例代码:

public class MyInterceptor implements InnerInterceptor {

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        // 获取原始的 SQL 语句
        String sql = boundSql.getSql();
        // 对 SQL 语句进行修改,添加 WHERE 条件
        if (!sql.contains("WHERE")) {
            sql += " WHERE deleted = 0";
        }
        // 将修改后的 SQL 语句设置回去
        MetaObject metaObject = SystemMetaObject.forObject(boundSql);
        metaObject.setValue("sql", sql);
    }

    @Override
    public void setProperties(Properties properties) {
        // 设置拦截器属性,例如将 deleted 字段从逻辑删除修改为物理删除
    }
}

在上面的代码中,beforeQuery 方法首先获取原始的 SQL 语句,然后判断是否已经包含了 WHERE 条件,如果没有,就添加一个 WHERE 条件来控制查询的数据范围。最后,再将修改后的 SQL 语句设置回去。

需要注意的是,在修改 BoundSql 对象时,不能直接调用其方法,而是需要通过 MetaObject 对象来操作属性,这样才能保证 MyBatis 的内部状态正确。

setProperties 方法允许开发者在创建拦截器实例时对其进行配置,例如设置一些参数、初始化一些资源等。下面是一个示例代码:

public class MyInterceptor implements InnerInterceptor {
    private String algorithm; // 加密算法

    @Override
    public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        // 在更新之前对参数进行加密
        if (parameter instanceof BaseEntity) {
            BaseEntity entity = (BaseEntity) parameter;
            entity.encrypt(algorithm);
        }
    }

    @Override
    public void setProperties(Properties properties) {
        // 设置加密算法
        algorithm = properties.getProperty("algorithm");
    }
}

在上面的代码中,我们定义了一个成员变量 algorithm,它表示加密算法。在 beforeUpdate 方法中,我们首先判断参数是否为 BaseEntity 类型,如果是,则调用其 encrypt 方法对数据进行加密。在 setProperties 方法中,我们获取配置文件中的 algorithm 属性,并将其设置到拦截器实例的成员变量中,以便在 beforeUpdate 方法中使用。

使用场景一:加密解密

在数据库存储敏感数据时,对数据进行加密,在查询数据时进行解密,保护数据的安全性。

public class EncryptionInterceptor implements InnerInterceptor {
    private String algorithm; // 加密算法

    @Override
    public void beforeGetBoundSql(StatementHandler sh) {
        // 获取原始的 SQL 语句
        BoundSql boundSql = sh.getBoundSql();
        String sql = boundSql.getSql();
        
        // 解密 SQL 语句
        String decryptedSql = decryptSql(sql);
        
        // 将解密后的 SQL 语句设置回去
        MetaObject metaObject = SystemMetaObject.forObject(boundSql);
        metaObject.setValue("sql", decryptedSql);
    }

    @Override
    public void setProperties(Properties properties) {
        // 设置加密算法
        algorithm = properties.getProperty("algorithm");
    }
    
    private String decryptSql(String encryptedSql) {
        // 解密 SQL 语句
        // ...
    }
}

在上述代码中,EncryptionInterceptor 实现了 beforeGetBoundSql 方法,在获取 BoundSql 对象之前,对 SQL 语句进行解密操作。通过 setProperties 方法设置加密算法的属性值。

需要注意的是,这只是示例代码,实际的加密解密操作需要根据具体的加密算法和业务逻辑来编写。

当需要根据条件动态修改 SQL 语句时,可以使用 beforeGetBoundSql 方法。下面是一个场景示例:

使用场景二:
假设有一个用户表 User,包含 id、name、age 等字段。现在需要实现一个分页查询功能,只返回年龄大于 18 岁的用户记录,并且查询结果按照姓名升序排序。但是,由于数据库中存储的是加密后的数据,所以需要在查询之前解密数据,再进行条件过滤和排序操作。

public class DecryptInterceptor implements InnerInterceptor {

    @Override
    public void beforeGetBoundSql(StatementHandler sh) {
        BoundSql boundSql = sh.getBoundSql();
        String sql = boundSql.getSql();

        // 解密 SQL 语句
        String decryptedSql = decryptSql(sql);
        
        // 将解密后的 SQL 语句设置回去
        MetaObject metaObject = SystemMetaObject.forObject(boundSql);
        metaObject.setValue("sql", decryptedSql);
        
        // 设置查询条件
        Map<String, Object> parameterObject = (Map<String, Object>) boundSql.getParameterObject();
        parameterObject.put("minAge", 18);
        
        // 修改排序方式
        String originalOrderByClause = boundSql.getOrderByClause();
        StringBuilder newOrderByClause = new StringBuilder();
        if (StringUtils.isNotEmpty(originalOrderByClause)) {
            newOrderByClause.append(originalOrderByClause).append(", ");
        }
        newOrderByClause.append("name ASC");
        metaObject.setValue("orderByClause", newOrderByClause.toString());
    }

    private String decryptSql(String sql) {
        // 解密 SQL 语句
        // ...
    }
}

在上述代码中,DecryptInterceptor 实现了 beforeGetBoundSql 方法,在获取 BoundSql 对象之前解密 SQL 语句,并设置查询条件和排序方式。使用 MetaObject 对象操作 BoundSql 对象的属性来实现修改。

需要注意的是,这只是示例代码,实际的加密解密操作需要根据具体的加密算法和业务逻辑来编写。

使用场景三:

当需要在拦截器中使用一些配置信息时,可以使用 setProperties 方法。下面是一个场景示例:

假设有一个需求:需要记录 SQL 执行时间,并根据执行时间判断 SQL 是否过慢。如果 SQL 过慢,需要记录日志并发出告警。为了方便配置告警阈值,可以将告警阈值作为拦截器的属性,在创建拦截器实例时通过配置文件进行配置。

public class SqlExecutionTimeInterceptor implements InnerInterceptor {
    private long threshold; // 告警阈值,单位毫秒

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();

        Object result = invocation.proceed();

        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;

        if (executionTime >= threshold) {
            log.warn("SQL 执行时间过长,耗时:{}ms", executionTime);
            sendAlarm(executionTime);
        }

        return result;
    }

    @Override
    public void setProperties(Properties properties) {
        String thresholdStr = properties.getProperty("threshold");
        threshold = Long.parseLong(thresholdStr);
    }

    private void sendAlarm(long executionTime) {
        // 发送告警
        // ...
    }
}

在上述代码中,SqlExecutionTimeInterceptor 实现了 setProperties 方法,在创建拦截器实例时从配置文件中读取告警阈值,并保存到拦截器的属性中。在拦截器的 intercept 方法中,计算 SQL 执行时间,并判断是否超过告警阈值,如果超过,则发送告警。

需要注意的是,这只是示例代码,实际的告警操作需要根据具体的业务逻辑来编写。

文章来源:https://blog.csdn.net/weixin_44060488/article/details/135363293
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。