目录

一、DataScopeAspect使用场景二、ew.customSqlSegment${ew.customSqlSegment}build:this.normal : queryWrapper where 条件不为空的时候,才有normalget第二次 进来add(), 已经拼接完 ew.customSqlSegment 了, 因为@DataPermission 注解进来的 动态拼接

Mybatis-plus 在自定义的sql语句中 调用QueryWrapper 的查询条件需求解决分析注意这里,如果没有where 条件,不拼接 customSqlSegment

扩展end 查看源码中:是根据所有的get方法,经过处理保存到:getMethods中

使用方法:

ew是mapper方法里的@Param(Constants.WRAPPER) Wrapper queryWrapper对象

首先判断ew.emptyOfWhere是否存在where条件,有的话再拼接上去,ew.customSqlSegment是WHERE + sql语句 没有where的时候加上 == false

使用${ew.sqlSegment} 如果是连表查询且查询条件是连表的字段则需在service层拼接查询条件时字段前指定别名

sqlSegment:

${ew.sqlSegment}

customSqlSegment:

${ew.customSqlSegment} 则省略

eg: select * from a ${ew.sqlSegment}

select * from a ${ew.customSqlSegment}

注:在对应的dao方法中 一样要指定 @Param(Constants.WRAPPER) Wrapper ew,其中Constants.WRAPPER="ew"源码中可以看到

————————————————

一、DataScopeAspect

从这里可以看出:表sys_role_dept 的用途,之前一起不清楚 role_dept关联表的含义。 自定义数据权限时(DATA_SCOPE_CUSTOM),通过角色 可以管理哪几个部门,来实现的。

@Aspect

@Component

public class DataScopeAspect

{

/**

* 全部数据权限

*/

public static final String DATA_SCOPE_ALL = "1";

/**

* 自定数据权限

*/

public static final String DATA_SCOPE_CUSTOM = "2";

/**

* 部门数据权限

*/

public static final String DATA_SCOPE_DEPT = "3";

/**

* 部门及以下数据权限

*/

public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";

/**

* 仅本人数据权限

*/

public static final String DATA_SCOPE_SELF = "5";

/**

* 数据权限过滤关键字

*/

public static final String DATA_SCOPE = "dataScope";

@Before("@annotation(controllerDataScope)")

public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable

{

clearDataScope(point);

handleDataScope(point, controllerDataScope);

}

protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope)

{

// 获取当前的用户

LoginUser loginUser = SecurityUtils.getLoginUser();

if (StringUtils.isNotNull(loginUser))

{

SysUser currentUser = loginUser.getUser();

// 如果是超级管理员,则不过滤数据

if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())

{

dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),

controllerDataScope.userAlias());

}

}

}

/**

* 数据范围过滤

*

* @param joinPoint 切点

* @param user 用户

* @param userAlias 别名

*/

public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias)

{

StringBuilder sqlString = new StringBuilder();

for (SysRole role : user.getRoles())

{

String dataScope = role.getDataScope();

if (DATA_SCOPE_ALL.equals(dataScope))

{

sqlString = new StringBuilder();

break;

}

else if (DATA_SCOPE_CUSTOM.equals(dataScope))

{

// 自定义数据权限,通过 role_dept, roleid可以查看哪些部门deptid的数据

// 关于 OR 关键字,大胆猜测是考虑 有多个角色时(因为是for),用OR 连接。当然第一个OR 肯定得去掉,(看后面验证)

sqlString.append(StringUtils.format(

" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,

role.getRoleId()));

}

else if (DATA_SCOPE_DEPT.equals(dataScope))

{

sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));

}

else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))

{

sqlString.append(StringUtils.format(

" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",

deptAlias, user.getDeptId(), user.getDeptId()));

}

else if (DATA_SCOPE_SELF.equals(dataScope))

{

if (StringUtils.isNotBlank(userAlias))

{

sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));

}

else

{

// 数据权限为仅本人且没有userAlias别名不查询任何数据

sqlString.append(" OR 1=0 ");

}

}

}

if (StringUtils.isNotBlank(sqlString.toString()))

{

Object params = joinPoint.getArgs()[0];

if (StringUtils.isNotNull(params) && params instanceof BaseEntity)

{

BaseEntity baseEntity = (BaseEntity) params;

// 验证了前面的猜测:一个user多个role时,会拼接多个OR 部门的数据, 猜测第一个OR ,按sql 逻辑,理应去掉。

// 这里substring(4) 去掉第一个" OR "

baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");

}

}

}

上面拼接后: and dept_id in (), 放到 baseEntity的 params里 。

/**

* 拼接权限sql前先清空params.dataScope参数防止注入

*/

private void clearDataScope(final JoinPoint joinPoint)

{

Object params = joinPoint.getArgs()[0];

if (StringUtils.isNotNull(params) && params instanceof BaseEntity)

{

BaseEntity baseEntity = (BaseEntity) params;

baseEntity.getParams().put(DATA_SCOPE, "");

}

}

}

-----------------------------------

使用场景

二、ew.customSqlSegment

${ew.customSqlSegment}

build:

this.normal : queryWrapper where 条件不为空的时候,才有normal

get

第二次 进来add(), 已经拼接完 ew.customSqlSegment 了, 因为@DataPermission 注解进来的 动态拼接

Mybatis-plus 在自定义的sql语句中 调用QueryWrapper 的查询条件

需求

项目中需要实现自定义的查询,但是仍然想用QueryWrapper对象里面的那些查询,

解决

在接口中方法中:@Param(Constants.WRAPPER) QueryWrapper queryWrapper; 里面的Constants.WRAPPER其实就是:“ew”

在我们的Mapper.xml文件中:

直接使用:${ew.customSqlSegment} 就可以调用QueryWrapper对象的条件sql

分析

就上面就可以直接用了,下面来分析怎么调用的

首先${ew.customSqlSegment} 这里,我们首先猜测Query Wrapper里面是不是有这个属性,因为我们用过mybaits都知道, 传入一个对象之后,直接对象.属性就可以获取值 ————————————————

附上Query Wrapper的关系图

我在Wrapper类中找到了getCustomSqlSegment()方法,所以我猜测肯定是调用了这个get方法,

但是奇怪的是这个类里面并没有customSqlSegment属性字段

public String getCustomSqlSegment() {

MergeSegments expression = this.getExpression();

if (Objects.nonNull(expression)) {

NormalSegmentList normal = expression.getNormal();

String sqlSegment = this.getSqlSegment();

if (StringUtils.isNotBlank(sqlSegment)) {

// 注意这里,如果没有where 条件,不拼接 customSqlSegment

if (normal.isEmpty()) {

return sqlSegment;

}

return "WHERE " + sqlSegment;

}

}

return "";

}

注意这里,如果没有where 条件,不拼接 customSqlSegment

所以猜测是直接调用的这个get方法,根据我们传入的属性名称,拼接成get+属性名称,这样来调用的吗?

扩展

所以我就写了一个demo

1、没有属性值

2、但是有一个getUserForId()的方法,

3、在mapper.xml文件中 :对象.userForId 但是注意:我的类里面并没有这个属性

试一下是否可以成功查询出数据!

实体类:

xml文件

调用:居然真的查询成功了!!!!!!!!!!!!!

end 查看源码中:

Reflector类中: 构造方法里面,根据该类名获取所有的get方法

到这里大家应该就明白了,

是根据所有的get方法,经过处理

private void addSetMethods(Method[] methods) {

Map> conflictingSetters = new HashMap<>();

Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 1 && PropertyNamer.isSetter(m.getName()))

.forEach(m -> addMethodConflict(conflictingSetters, PropertyNamer.methodToProperty(m.getName()), m));

resolveSetterConflicts(conflictingSetters);

}

private void addMethodConflict(Map> conflictingMethods, String name, Method method) {

if (isValidPropertyName(name)) {

List list = MapUtil.computeIfAbsent(conflictingMethods, name, k -> new ArrayList<>());

list.add(method);

}

}

private void resolveSetterConflicts(Map> conflictingSetters) {

for (Entry> entry : conflictingSetters.entrySet()) {

String propName = entry.getKey();

List setters = entry.getValue();

Class getterType = getTypes.get(propName);

boolean isGetterAmbiguous = getMethods.get(propName) instanceof AmbiguousMethodInvoker;

boolean isSetterAmbiguous = false;

Method match = null;

for (Method setter : setters) {

if (!isGetterAmbiguous && setter.getParameterTypes()[0].equals(getterType)) {

// should be the best match

match = setter;

break;

}

if (!isSetterAmbiguous) {

match = pickBetterSetter(match, setter, propName);

isSetterAmbiguous = match == null;

}

}

if (match != null) {

addSetMethod(propName, match);

}

}

}

保存到:getMethods中

private void addGetMethod(String name, Method method, boolean isAmbiguous) {

MethodInvoker invoker = isAmbiguous

? new AmbiguousMethodInvoker(method, MessageFormat.format(

"Illegal overloaded getter method with ambiguous type for property ''{0}'' in class ''{1}''. This breaks the JavaBeans specification and can cause unpredictable results.",

name, method.getDeclaringClass().getName()))

: new MethodInvoker(method);

getMethods.put(name, invoker);

Type returnType = TypeParameterResolver.resolveReturnType(method, type);

getTypes.put(name, typeToClass(returnType));

}

调用的时候:根据xml里面的属性名称,在我们上面初始化的getMethods的map中找到对应的方法,执行方法的调用

总结 所以这就是为啥 ${ew.customSqlSegment} 可以调用的原因了

相关链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: