background
The current project leader resigned at the end of last year, which has led to the data filtering function of the preliminary planning being not implemented.
Now the project is about to enter the trial operation period, and the data needs to be filtered according to the configuration of user data permissions.
If it is too much work to modify SQL manually in files, I will consider processing the query SQL through Mybatis later.
Basic knowledge
Introduction to Mybatis Interceptor
Interceptor interface source code analysis
package ; import ; public interface Interceptor { Object intercept(Invocation invocation) throws Throwable; default Object plugin(Object target) { return (target, this); } default void setProperties(Properties properties) { } }
-
intercept
method
This method is core and will be executed when intercepted to the call. The Invocation object contains all the information of the intercepted method, including the method itself, parameters, target object, etc. In this method, you can do any preprocessing or postprocessing logic, and then continue to execute the original method by calling () or directly return the custom result. -
plugin
method
This method is used to decide whether to apply an interceptor to an object. If the target is returned, it means that no intercept is performed; if a new object is returned, it means that the new object will be used instead of the original object, usually a proxy object is returned here. -
setProperties
method
Used to set the properties of the interceptor, which can be defined in the MyBatis configuration file.
Signature annotation source code analysis
@Documented @Retention() @Target({}) public @interface Signature { Class<?> type(); String method(); Class<?>[] args(); }
-
type
: represents the type of the target object, -
method
: The name of the target method to be intercepted. -
args
: A list of parameter types representing the target method. Different @Signature annotations may have different parameter types lists, depending on the specific method signature.
Code combat
Implement a similarPageHelper
A tool class that stores data permission-related information in local thread variables
public class DataAccessMethod { private static final ThreadLocal<DataAccessType[]> ACCESS_LOCAL = new ThreadLocal<>(); public DataAccessMethod() { } public static void setLocalAccess(DataAccessType... accessType) { ACCESS_LOCAL.set(accessType); } public static DataAccessType[] getLocalAccess() { return ACCESS_LOCAL.get(); } public static void clearLocalAccess() { ACCESS_LOCAL.remove(); } public static void accessData(DataAccessType... accessType) { setLocalAccess(accessType); } }
accomplishInterceptor
The interface enhances SQL processing
@Intercepts({@Signature( type = , method = "query", args = {, , , } ), @Signature( type = , method = "query", args = {, , , , , } )}) @Slf4j @Component public class DataAccessFilterInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { if (skip()) { return (); } MappedStatement statement = (MappedStatement) ()[0]; Object parameter = ()[1]; BoundSql boundSql = (parameter); String originalSql = (); Object parameterObject = (); String sql = addTenantCondition(originalSql, "1", "222"); ("OriginalSQL:{}, After data permission replacementSQL:{}", originalSql, sql); BoundSql newBoundSql = new BoundSql((), sql, (), parameterObject); MappedStatement newStatement = copyFromMappedStatement(statement, new BoundSqlSqlSource(newBoundSql)); ()[0] = newStatement; return (); } /** * Determine whether to skip * * @return Whether to skip it */ private boolean skip() { DataAccessType[] localAccess = (); return localAccess == null; } private String addTenantCondition(String originalSql, String depId, String alias) { String field = "id"; if ((alias)) { field = alias + "." + field; } StringBuilder sb = new StringBuilder(()); int index = ("where"); sb = new StringBuilder(originalSql); if (index < 0) { (" where ").append(field).append(" = ").append(depId); } else { (index + 5, " " + field + " = " + depId + " and "); } return (); } private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) { builder = new ((), (), newSqlSource, ()); (()); (()); (()); (()); (()); (()); (()); (()); (()); return (); } public static class BoundSqlSqlSource implements SqlSource { private final BoundSql boundSql; public BoundSqlSqlSource(BoundSql boundSql) { = boundSql; } @Override public BoundSql getBoundSql(Object parameterObject) { return boundSql; } } }
Summarize
The above code is just an example. In actual production, multi-table query, SQL injection and other related issues need to be considered.
These are just personal experience. I hope you can give me a reference and I hope you can support me more.