基于mybatis注解动态sql中foreach工具的方法

网友投稿 1311 2022-11-18

基于mybatis注解动态sql中foreach工具的方法

基于mybatis注解动态sql中foreach工具的方法

目录实现目标工具类测试集成进spring后的使用方法Mapper层Provider层Ognl问题解决方案创建一个DefaultMemberAccess.java文件改造ForeachMybatisUtils.java类

实现目标

由于在注解版mybatis中手动循环拼接动态sql容易出错

请看mybatis注解动态sql注入map和list(防sql注入攻击),所以封装了这个类似于foreach标签的工具方法。

由于mybatis(3.5.6、3.5.7 or earlier)的bug(mybatis can not parse #{list[0][0]} correctly),不支持数组/List直接嵌套数组/List,但是可以与Map嵌套。不过 mybatis can parse ${list[0][0]} correctly

工具类

package com.xxx.common.util;

import java.util.Arrays;

import java.util.List;

import java.util.Map;

import java.util.Map.Entry;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

import org.apache.ibatis.binding.BindingException;

import org.apache.ibatis.ognl.Ognl;

import org.apache.ibatis.ognl.OgnlException;

import java.util.Set;

public class ForeachMybatisUtils {

private ForeachMybatisUtils() {}

public static ParamObject createParamObject(C obj) {

ParamObject paramObject = new ParamObject();

paramObject.setObj(obj);

return paramObject;

}

public static StringBuilder foreach(ParamObject paramObject) {

return foreach(paramObject, null);

}

@SuppressWarnings("rawtypes")

public static StringBuilder foreach(ParamObject paramObject, Interceptor interceptor) {

return foreach(paramObject.getObj(), paramObject.getCollection(), paramObject.getItem(), paramObject.getIndex(),

paramObject.getItemFormatter(), paramObject.getSeparator(), paramObject.getOpen(), paramObject.getClose(), interceptor);

}

/**

* itemFormatter部分用法:#{item,jdbcType=VARCHAR},#{item.3345,jdbcType=VARCHAR}其中3345为map的key, ${item}, ${item['3345']}其中3345为map的key

* @param List.class、Map.class、Array

* @param obj list、map、数组对象

* @param collection 对应xml foreach标签的collection属性

* @param item 对应xml foreach标签的item属性

* @param index 对应xml foreach标签的index属性,但是在itemFormatter中只匹配 ${ } 格式

* @param itemFormatter 对应xml foreach标签内 #{item}

* @param separator 对应xml foreach标签的separator属性

* @param open 对应xml foreach标签的open属性

* @param close 对应xml foreach标签的close属性

* @return 拼接后的动态sql

*/

public static StringBuilder foreach(C obj, String collection, String item, String itemFormatter,

String separator, String open, String close) {

return foreach(obj, collection, item, null, itemFormatter, separator, open, close, null);

}

public static StringBuilder foreach(C obj, String collection, String item, String index, String itemFormatter,

String separator, String open, String close) {

return foreach(obj, collection, item, index, itemFormatter, separator, open, close, null);

}

@SuppressWarnings({ "rawtypes", "unchecked" })

public static StringBuilder foreach(C obj, String collection, String item, String index, String itemFormatter, String separator,

String open, String close, ForeachMybatisUtils.Interceptor interceptor) {

if (obj == null) {

throw new NullPointerException("object cannot be null");

}

if (collection == null || collection.trim().equals("")) {

throw new NullPointerException("collection cannot be blank");

}

if (item == null || item.trim().isEmpty()) {

throw new NullPointerException("item cannot be blank");

}

if (itemFormatter == null) {

throw new NullPointerException("itemFormatter cannot be null, and you can fill #{item},please");

}

collection = collection.trim();

item = item.trim();

if (index != null && item.equals(index.trim())) {

throw new IllegalArgumentException("index cannot be equal to item");

}

Pattern itemDynamicPattern = Pattern.compile("(?<=#\\{)" + item + "(?=[\\s\\S]*\\})");

Pattern itemBindingPattern = Pattern.compile("\\$\\{" + item + "(?:(?:\\.|\\[)\\S+)?\\s*\\}");

Pattern indexStaticPattern = null;

if (index != null && !index.trim().isEmpty() && itemFormatter.contains("${")) {

indexStaticPattern = Pattern.compile("\\$\\{" + index.trim() + "\\s*\\}");

}

if (separator == null) {

separator = "";

}

StringBuilder sqlBuilder = new StringBuilder();

if (open != null) {

sqlBuilder.append(open);

}

String prod = "";

int n = 0;

try {

if (obj instanceof Map) {

Set set = ((Map) obj).entrySet();

for (Entry entry : set) {

String key = (String) entry.getKey();

if (interceptor != null && interceptor.preBreakIntercept(obj, key, entry.getValue())) {

break;

}

if (interceptor != null && interceptor.continueIntercept(obj, key, entry.getValue())) {

continue;

}

if (key.contains(".") || key.contains(" ")) {

throw new IllegalStateException("the Key of map can not contains '.' or ' '");

}

if (n > 0) {

sqlBuilder.append(separator);

}

prod = replaceAll(itemDynamicPattern, itemFormatter, collection + "." + key);

if (indexStaticPattern != null) {

prod = replaceAll(indexStaticPattern, prod, key);

}

prod = replaceBindingMap(itemBindingPattern, item, prod, key, obj);

sqlBuilder.append(prod);

n++;

if (interceptor != null && interceptor.postBreakIntercept(obj, key, entry.getValue())) {

break;

}

}

} else if (obj instanceof List) {

List list = (List) obj;

for (int i = 0, size = list.size(); i < size; i++) {

if (interceptor != null && interceptor.preBreakIntercept(obj, i, list.get(i))) {

break;

}

if (interceptor != null && interceptor.continueIntercept(obj, i, list.get(i))) {

continue;

}

if (n > 0) {

sqlBuilder.append(separator);

}

prod = replaceAll(itemDynamicPattern, itemFormatter, collection + "[" + i + "]");

if (indexStaticPattern != null) {

prod = replaceAll(indexStaticPattern, prod, "" + i);

}

prod = replaceBindingList(itemBindingPattern, item, prod, "" + i, obj);

sqlBuilder.append(prod);

n++;

if (interceptor != null && interceptor.postBreakIntercept(obj, i, list.get(i))) {

break;

}

}

} else if (obj.getClass().isArray()) {

List list = Arrays.asList((Object[]) obj);

for (int i = 0, size = list.size(); i < size; i++) {

if (interceptor != null && interceptor.preBreakIntercept(obj, i, list.get(i))) {

break;

}

if (interceptor != null && interceptor.continueIntercept(obj, i, list.get(i))) {

continue;

}

if (n > 0) {

sqlBuilder.append(separator);

}

prod = replaceAll(itemDynamicPattern, itemFormatter, collection + "[" + i + "]");

if (indexStaticPattern != null) {

prod = replaceAll(indexStaticPattern, prod, "" + i);

}

prod = replaceBindingList(itemBindingPattern, item, prod, "" + i, obj);

sqlBuilder.append(prod);

n++;

if (interceptor != null && interceptor.postBreakIntercept(obj, i, list.get(i))) {

break;

}

}

} else {

throw new IllegalArgumentException("the Type of collection support only Array,List,Map");

}

} catch (OgnlException e) {

throw new BindingException("ognl exception", e);

}

if (n < 1) {

sqlBuilder.delete(0, sqlBuilder.length());

} else {

if (close != null) {

sqlBuilder.append(close);

}

}

return sqlBuilder;

}

public static interface Interceptor {

/**

* for循环内是否执行break语句, break语句在循环内第一行

* @param collection 集合

* @param item 集合元素

* @param key 集合key或下标

* @return 返回true,则执行break语句

*/

boolean preBreakIntercept(C collection, K key, T item);

/**

* for循环内是否执行break语句, break语句在循环内最后一行

* @param collection 集合

* @param item 集合元素

* @param key 集合key或下标

* @return 返回true,则执行break语句

*/

boolean postBreakIntercept(C collection, K key, T item);

/**

* for循环内是否执行continue语句

* @param collection 集合

* @param item 集合元素

* @param key 集合key或下标

* @return 返回true,则执行continue语句

*/

boolean continueIntercept(C collection, K key, T item);

}

private static String replaceAll(Pattern pattern, String itemFormatter, String collection) {

itemFormatter = pattern.matcher(itemFormatter).replaceAll(collection);

return itemFormatter;

}

private static String replaceBindingMap(Pattern pattern, String item, String itemFormatter, String index, C obj) throws OgnlException {

Matcher matcher = pattern.matcher(itemFormatter);

StringBuffer buffer = new StringBuffer();

matcher.reset();

String group = "";

while (matcher.find()) {

group = matcher.group();

group = group.replaceFirst("\\$\\{" + item, "#root['" + index + "']");

group = group.substring(0, group.length() - 1).trim();

group = String.valueOf(Ognl.getValue(group, obj));

matcher.appendReplacement(buffer, group);

}

matcher.appendTail(buffer);

return buffer.toString();

}

private static String replaceBindingList(Pattern pattern, String item, String itemFormatter, String index, C obj) throws OgnlException {

Matcher matcher = pattern.matcher(itemFormatter);

StringBuffer buffer = new StringBuffer();

matcher.reset();

String group = "";

while (matcher.find()) {

group = matcher.group();

group = group.replaceFirst("\\$\\{" + item, "#root[" + index + "]");

group = group.substring(0, group.length() - 1).trim();

group = String.valueOf(Ognl.getValue(group, obj));

matcher.appendReplacement(buffer, group);

}

matcher.appendTail(buffer);

return buffer.toString();

}

public static class ParamObject {

private C obj;

private String collection;

private String item = "item";

private String index;

private String itemFormatter;

private String separator;

private String open;

private String close;

public C getObj() {

return obj;

}

public ParamObject setObj(C obj) {

this.obj = obj;

return this;

}

public String getCollection() {

return collection;

}

public ParamObject setCollection(String collection) {

this.collection = collection;

return this;

}

public String getItem() {

return item;

}

public ParamObject setItem(String item) {

this.item = item;

return this;

}

public String getIndex() {

return index;

}

public ParamObject setIndex(String index) {

this.index = index;

return this;

}

public String getItemFormatter() {

return itemFormatter;

}

public ParamObject setItemFormatter(String itemFormatter) {

this.itemFormatter = itemFormatter;

return this;

}

public String getSeparator() {

return separator;

}

public ParamObject setSeparator(String separator) {

this.separator = separator;

return this;

}

public String getOpen() {

return open;

}

public ParamObject setOpen(String open) {

this.open = open;

return this;

}

public String getClose() {

return close;

}

public ParamObject setClose(String close) {

this.close = close;

return this;

}

public StringBuilder foreach() {

return this.foreach(null);

}

@SuppressWarnings("rawtypes")

public StringBuilder foreach(Interceptor interceptor) {

return ForeachMybatisUtils.foreach(this, interceptor);

}

}

public interface InnerForeach {

CharSequence foreach(C innerObj, K index);

}

@SuppressWarnings({ "rawtypes", "unchecked" })

public static StringBuilder nestedForeach(C obj, String separator, String open, String close, InnerForeach innerForeach) {

if (obj == null) {

throw new NullPointerException("object can not is null");

}

if (separator == null) {

separator = "";

}

StringBuilder sqlBuilder = new StringBuilder();

if (open != null) {

sqlBuilder.append(open);

}

int n = 0;

int i = 0;

CharSequence sqlItem = null;

if (obj instanceof Map) {

Set set = ((Map) obj).entrySet();

for (Entry entry : set) {

sqlItem = innerForeach.foreach(entry.getValue(), entry.getKey());

if (sqlItem != null && !sqlItem.toString().trim().isEmpty()) {

if (n > 0) {

sqlBuilder.append(separator);

}

sqlBuilder.append(sqlItem);

n++;

}

i++;

}

} else if (obj instanceof List) {

List list = (List) obj;

for (Object element : list) {

sqlItem = innerForeach.foreach(element, i);

if (sqlItem != null && !sqlItem.toString().trim().isEmpty()) {

if (n > 0) {

sqlBuilder.append(separator);

}

sqlBuilder.append(sqlItem);

n++;

}

i++;

}

} else if (obj.getClass().isArray()) {

List list = Arrays.asList((Object[]) obj);

for (Object element : list) {

sqlItem = innerForeach.foreach(element, i);

if (sqlItem != null && !sqlItem.toString().trim().isEmpty()) {

if (n > 0) {

sqlBuilder.append(separator);

}

sqlBuilder.append(sqlItem);

n++;

}

i++;

}

} else {

throw new IllegalArgumentException("the Type of collection support only Array,List,Map");

}

if (n < 1) {

sqlBuilder.delete(0, sqlBuilder.length());

} else {

if (close != null) {

sqlBuilder.append(close);

}

}

return sqlBuilder;

}

测试

public static void main(String[] args) {

String[][] strs = {{"ddd","jfhd","uyijn"}, {"ddd","jgwhd","uyijn"}, {"ddd","kyugkfd","uyijn"}};

// List list = Arrays.asList(strs);

Map map = new HashMap<>();

map.put("fwgsss", new String[] {"ddd","jfhd","uyijn"});

map.put("uytr", new String[] {"ddd","jgwhd","uyijn"});

map.put("2", new String[] {"ddd","kyugkfd","uyijn"});

StringBuilder foreach = foreach(map, "wea.arr", "item", "index", "SELECT #{item[1], jdbcType=VARCHAR, javaType=java.lang.String} AS SFF, #{item[2], jdbcType=VARCHAR} AS AT, '${index }' AS FV FROM dual", " UNION ALL ", " (", ")",

new Interceptor, String, String[]>() {

@Override

public boolean preBreakIntercept(Map collection, String key, String[] item) {

// TODO Auto-generated method stub

return false;

}

@Override

public boolean postBreakIntercept(Map collection, String key, String[] item) {

// TODO Auto-generated method stub

return false;

}

@Override

public boolean continueIntercept(Map collection, String key, String[] item) {

// TODO Auto-generated method stub

return false;

}

});

System.out.println(foreach);

StringBuilder foreach1 = foreach(strs, "wea.arr", "item", "index", "SELECT #{item[1], jdbcType=VARCHAR, javaType=java.lang.String} AS SFF, #{item[2], jdbcType=VARCHAR} AS AT FROM dual", " UNION ALL ", " (", ")",

new Interceptor() {

@Override

public boolean preBreakIntercept(String[][] collection, Integer key, String[] item) {

return false;

}

@Override

public boolean postBreakIntercept(String[][] collection, Integer key, String[] item) {

return false;

}

@Override

public boolean continueIntercept(String[][] collection, Integer key, String[] item) {

return false;

}

});

System.out.println(foreach1);

StringBuilder foreach2 = ForeachMybatisUtils.createParamObject(strs)

.setCollection("wea.arr")

.setItem("item")

.setIndex("index")

.setItemFormatter("SELECT #{item[1], jdbcType=VARCHAR, javaType=java.lang.String} AS SFF, #{item[2], jdbcType=VARCHAR} AS AT FROM dual")

.setSeparator(" UNION ALL ")

.setOpen(" (")

.setClose(")")

.foreach(new ForeachMybatisUtils.Interceptor() {

@Override

public boolean preBreakIntercept(String[][] collection, Integer key, String[] item) {

return false;

}

@Override

public boolean postBreakIntercept(String[][] collection, Integer key, String[] item) {

return false;

}

@Override

public boolean continueIntercept(String[][] collection, Integer key, String[] item) {

return false;

}

});

System.out.println(foreach2);

StringBuilder foreach3 = ForeachMybatisUtils.foreach(ForeachMybatisUtils.createParamObject(strs)

.setCollection("wea.arr")

.setItem("item")

.setIndex("index")

.setItemFormatter("SELECT #{item[1], jdbcType=VARCHAR, javaType=java.lang.String} AS SFF, #{item[2], jdbcType=VARCHAR} AS AT FROM dual")

.setSeparator(" UNION ALL ")

.setOpen(" (")

.setClose(")"),

new ForeachMybatisUtils.Interceptor() {

@Override

public boolean preBreakIntercept(String[][] collection, Integer key, String[] item) {

return false;

}

@Override

public boolean postBreakIntercept(String[][] collection, Integer key, String[] item) {

return false;

}

@Override

public boolean continueIntercept(String[][] collection, Integer key, String[] item) {

return false;

}

}

);

System.out.println(foreach3);

}

public static void main(String[] args) {

// @Param("list")

List> lists = new ArrayList>();

List list1 = new ArrayList<>();

list1.add(1);

list1.add(2);

list1.add(3);

lists.add(list1);

List list2 = new ArrayList<>();

list2.add(11);

list2.add(12);

list2.add(13);

list2.add(14);

list2.add(19);

lists.add(list2);

List list3 = new ArrayList<>();

list3.add(31);

list3.add(32);

list3.add(35);

list3.add(38);

lists.add(list3);

StringBuilder sql = ForeachMybatisUtils.nestedForeach(lists,

" union all ", "select b.id, b.name from (", ") b",

new ForeachMybatisUtils.InnerForeach, Integer>() {

@Override

public CharSequence foreach(List innerObj, Integer index) {

return ForeachMybatisUtils.createParamObject(innerObj)

.setCollection("list[" + index + "]")

.setItem("item")

.setItemFormatter("#{item}")

.setSeparator(",")

.setOpen("select id, name from table_demo where id in (")

.setClose(")")

.foreach();

}

});

System.out.println(sql);

StringBuilder sql2 = ForeachMybatisUtils.createParamObject(lists)

.setCollection("list")

.setItem("item")

.setItemFormatter("'${item[1]}'")

.setSeparator(",")

.setOpen("select id, name from table_demo where id in (")

.setClose(")")

.foreach();

System.out.println(sql2.toString());

集成进spring后的使用方法

Mapper层

package com.xxx.manage.mapper;

import java.util.List;

import java.util.Map;

import org.apache.ibatis.annotations.Param;

import org.apache.ibatis.annotations.SelectProvider;

import org.springframework.stereotype.Repository;

import com.xxx.manage.bo.DeviceBO;

import com.xxx.manage.provider.ManageProvider;

@Repository

public interface ManageMapper {

@SelectProvider(type = ManageProvider.class, method = "queryDevices")

List> queryDevices(@Param("devicetypeno") String devicetypeno, @Param("list") List list);

@SelectProvider(type = ManageProvider.class, method = "queryMap")

List> queryMap(@Param("map") Map> map);

@SelectProvider(type = ManageProvider.class, method = "queryList")

List> queryList(@Param("list") List> lists);

}

Provider层

package com.xxx.manage.provider;

import java.util.List;

import java.util.Map;

import org.apache.ibatis.jdbc.SQL;

import com.xxx.common.util.ForeachMybatisUtils;

public class ManageProvider {

public String queryDevices(Map params) {

@SuppressWarnings("unchecked")

List list = (List) params.get("list");

SQL sql = new SQL()

.SELECT("TERMINALNUM, ORGCODE, DEVICETYPENO, DEVICENAME")

.FROM("S_DEVICE_INFO")

.WHERE("DEVICETYPENO = #{devicetypeno}");

StringBuilder inBuilder = ForeachMybatisUtils.foreach(list, "list", "item", "#{item}", ", ", "ORGCODE IN (", ")");

if (inBuilder.length() > 0) {

sql.WHERE(inBuilder.toString());

}

return sql.toString();

}

public String queryMap(Map params) {

@SuppressWarnings("unchecked")

Map> map = (Map>) params.get("map");

StringBuilder sqlBuilder = ForeachMybatisUtils.nestedForeach(map,

" union all ", "select b.id, b.name from (", ") b",

new ForeachMybatisUtils.InnerForeach, String>() {

@Override

public CharSequence foreach(List innerObj, String index) {

return ForeachMybatisUtils.createParamObject(innerObj)

.setCollection("map." + index + "")

.setItem("item")

.setItemFormatter("#{item, jdbcType=NUMERIC}")

.setSeparator(",")

.setOpen("select id, name from table_demo where id in (")

.setClose(")")

.foreach();

}

});

System.out.println(sqlBuilder.toString());

return sqlBuilder.toString();

}

public String queryList(Map params) {

@SuppressWarnings("unchecked")

List> list = (List>) params.get("list");

StringBuilder sqlBuilder = ForeachMybatisUtils.nestedForeach(list,

" union all ", "select b.id, b.name from (", ") b",

new ForeachMybatisUtils.InnerForeach, Integer>() {

@Override

public CharSequence foreach(Map innerObj, Integer index) {

return ForeachMybatisUtils.createParamObject(innerObj)

.setCollection("list[" + index + "]")

.setItem("item")

.setItemFormatter("#{item, jdbcType=NUMERIC}")

.setSeparator(",")

.setOpen("select id, name from table_demo where id in (")

.setClose(")")

.foreach();

}

});

System.out.println(sqlBuilder.toString());

return sqlBuilder.toString();

}

}

Ognl问题

版本的mybatis使用${}注入时,可能会抛出异常

MemberAccess implementation must be provided - null not permitted!

解决方案

创建一个DefaultMemberAccess.java文件

package com.xxx.common.util;

import java.lang.reflect.*;

import java.util.*;

import org.apache.ibatis.ognl.MemberAccess;

public class DefaultMemberAccess implements MemberAccess {

private boolean allowPrivateAccess = false;

private boolean allowProtectedAccess = false;

private boolean allowPackageProtectedAccess = false;

/*===================================================================

Constructors

===================================================================*/

public DefaultMemberAccess(boolean allowAllAccess) {

this(allowAllAccess, allowAllAccess, allowAllAccess);

}

public DefaultMemberAccess(boolean allowPrivateAccess, boolean allowProtectedAccess, boolean allowPackageProtectedAccess) {

super();

this.allowPrivateAccess = allowPrivateAccess;

this.allowProtectedAccess = allowProtectedAccess;

this.allowPackageProtectedAccess = allowPackageProtectedAccess;

}

/*===================================================================

Public methods

===================================================================*/

public boolean getAllowPrivateAccess() {

return allowPrivateAccess;

}

public void setAllowPrivateAccess(boolean value) {

allowPrivateAccess = value;

}

public boolean getAllowProtectedAccess() {

return allowProtectedAccess;

}

public void setAllowProtectedAccess(boolean value) {

allowProtectedAccess = value;

}

public boolean getAllowPackageProtectedAccess() {

return allowPackageProtectedAccess;

}

public void setAllowPackageProtectedAccess(boolean value) {

allowPackageProtectedAccess = value;

}

/*===================================================================

MemberAccess interface

===================================================================*/

@Override

public Object setup(Map context, Object target, Member member, String propertyName) {

Object result = null;

if (isAccessible(context, target, member, propertyName)) {

AccessibleObject accessible = (AccessibleObject)member;

if (!accessible.isAccessible()) {

result = Boolean.FALSE;

accessible.setAccessible(true);

}

}

return result;

}

@Override

public void restore(Map context, Object target, Member member, String propertyName, Object state) {

if (state != null) {

((AccessibleObject)member).setAccessible(((Boolean)state).booleanValue());

}

}

/**

Returns true if the given member is accessible or can be made accessible

by this object.

*/

@Override

public boolean isAccessible(Map context, Object target, Member member, String propertyName) {

int modifiers = member.getModifiers();

boolean result = Modifier.isPublic(modifiers);

if (!result) {

if (Modifier.isPrivate(modifiers)) {

result = getAllowPrivateAccess();

} else {

if (Modifier.isProtected(modifiers)) {

result = getAllowProtectedAccess();

} else {

result = getAllowPackageProtectedAccess();

}

}

}

return result;

}

}

改造ForeachMybatisUtils.java类

添加一个静态字段context,

将context插入,Ognl.getValue和Ognl.setValue方法的第二个参数

private static final OgnlContext context = new OgnlContext(null,null,new DefaultMemberAccess(true));

Ognl.getValue(group, context, obj);

Ognl.setValue(group, context, obj, value);

完整的ForeachMybatisUtils.java类

package com.xxx.common.util;

import java.util.Arrays;

import java.util.List;

import java.util.Map;

import java.util.Map.Entry;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

import org.apache.ibatis.binding.BindingException;

import org.apache.ibatis.ognl.Ognl;

import org.apache.ibatis.ognl.OgnlException;

import java.util.Set;

public class ForeachMybatisUtils {

private static final OgnlContext context = new OgnlContext(null,null,new DefaultMemberAccess(true));

private ForeachMybatisUtils() {}

public static ParamObject createParamObject(C obj) {

ParamObject paramObject = new ParamObject();

paramObject.setObj(obj);

return paramObject;

}

public static StringBuilder foreach(ParamObject paramObject) {

return foreach(paramObject, null);

}

@SuppressWarnings("rawtypes")

public static StringBuilder foreach(ParamObject paramObject, Interceptor interceptor) {

return foreach(paramObject.getObj(), paramObject.getCollection(), paramObject.getItem(), paramObject.getIndex(),

paramObject.getItemFormatter(), paramObject.getSeparator(), paramObject.getOpen(), paramObject.getClose(), interceptor);

}

/**

* itemFormatter部分用法:#{item,jdbcType=VARCHAR},#{item.3345,jdbcType=VARCHAR}其中3345为map的key, ${item}, ${item['3345']}其中3345为map的key

* @param List.class、Map.class、Array

* @param obj list、map、数组对象

* @param collection 对应xml foreach标签的collection属性

* @param item 对应xml foreach标签的item属性

* @param index 对应xml foreach标签的index属性,但是在itemFormatter中只匹配 ${ } 格式

* @param itemFormatter 对应xml foreach标签内 #{item}

* @param separator 对应xml foreach标签的separator属性

* @param open 对应xml foreach标签的open属性

* @param close 对应xml foreach标签的close属性

* @return 拼接后的动态sql

*/

public static StringBuilder foreach(C obj, String collection, String item, String itemFormatter,

String separator, String open, String close) {

return foreach(obj, collection, item, null, itemFormatter, separator, open, close, null);

}

public static StringBuilder foreach(C obj, String collection, String item, String index, String itemFormatter,

String separator, String open, String close) {

return foreach(obj, collection, item, index, itemFormatter, separator, open, close, null);

}

@SuppressWarnings({ "rawtypes", "unchecked" })

public static StringBuilder foreach(C obj, String collection, String item, String index, String itemFormatter, String separator,

String open, String close, ForeachMybatisUtils.Interceptor interceptor) {

if (obj == null) {

throw new NullPointerException("object cannot be null");

}

if (collection == null || collection.trim().equals("")) {

throw new NullPointerException("collection cannot be blank");

}

if (item == null || item.trim().isEmpty()) {

throw new NullPointerException("item cannot be blank");

}

if (itemFormatter == null) {

throw new NullPointerException("itemFormatter cannot be null, and you can fill #{item},please");

}

collection = collection.trim();

item = item.trim();

if (index != null && item.equals(index.trim())) {

throw new IllegalArgumentException("index cannot be equal to item");

}

Pattern itemDynamicPattern = Pattern.compile("(?<=#\\{)" + item + "(?=[\\s\\S]*\\})");

Pattern itemBindingPattern = Pattern.compile("\\$\\{" + item + "(?:(?:\\.|\\[)\\S+)?\\s*\\}");

Pattern indexStaticPattern = null;

if (index != null && !index.trim().isEmpty() && itemFormatter.contains("${")) {

indexStaticPattern = Pattern.compile("\\$\\{" + index.trim() + "\\s*\\}");

}

if (separator == null) {

separator = "";

}

StringBuilder sqlBuilder = new StringBuilder();

if (open != null) {

sqlBuilder.append(open);

}

String prod = "";

int n = 0;

try {

if (obj instanceof Map) {

Set set = ((Map) obj).entrySet();

for (Entry entry : set) {

String key = (String) entry.getKey();

if (interceptor != null && interceptor.preBreakIntercept(obj, key, entry.getValue())) {

break;

}

if (interceptor != null && interceptor.continueIntercept(obj, key, entry.getValue())) {

continue;

}

if (key.contains(".") || key.contains(" ")) {

throw new IllegalStateException("the Key of map can not contains '.' or ' '");

}

if (n > 0) {

sqlBuilder.append(separator);

}

prod = replaceAll(itemDynamicPattern, itemFormatter, collection + "." + key);

if (indexStaticPattern != null) {

prod = replaceAll(indexStaticPattern, prod, key);

}

prod = replaceBindingMap(itemBindingPattern, item, prod, key, obj);

sqlBuilder.append(prod);

n++;

if (interceptor != null && interceptor.postBreakIntercept(obj, key, entry.getValue())) {

break;

}

}

} else if (obj instanceof List) {

List list = (List) obj;

for (int i = 0, size = list.size(); i < size; i++) {

if (interceptor != null && interceptor.preBreakIntercept(obj, i, list.get(i))) {

break;

}

if (interceptor != null && interceptor.continueIntercept(obj, i, list.get(i))) {

continue;

}

if (n > 0) {

sqlBuilder.append(separator);

}

prod = replaceAll(itemDynamicPattern, itemFormatter, collection + "[" + i + "]");

if (indexStaticPattern != null) {

prod = replaceAll(indexStaticPattern, prod, "" + i);

}

prod = replaceBindingList(itemBindingPattern, item, prod, "" + i, obj);

sqlBuilder.append(prod);

n++;

if (interceptor != null && interceptor.postBreakIntercept(obj, i, list.get(i))) {

break;

}

}

} else if (obj.getClass().isArray()) {

List list = Arrays.asList((Object[]pMwhFoz) obj);

for (int i = 0, size = list.size(); i < size; i++) {

if (interceptor != null && interceptor.preBreakIntercept(obj, i, list.get(i))) {

break;

}

if (interceptor != null && interceptor.continueIntercept(obj, i, list.get(i))) {

continue;

}

if (n > 0) {

sqlBuilder.append(separator);

}

prod = replaceAll(itemDynamicPattern, itemFormatter, collection + "[" + i + "]");

if (indexStaticPattern != null) {

prod = replaceAll(indexStaticPattern, prod, "" + i);

}

prod = replaceBindingList(itemBindingPattern, item, prod, "" + i, obj);

sqlBuilder.append(prod);

n++;

if (interceptor != null && interceptor.postBreakIntercept(obj, i, list.get(i))) {

break;

}

}

} else {

throw new IllegalArgumentException("the Type of collection support only Array,List,Map");

}

} catch (OgnlException e) {

throw new BindingException("ognl exception", e);

}

if (n < 1) {

sqlBuilder.delete(0, sqlBuilder.length());

} else {

if (close != null) {

sqlBuilder.append(close);

}

}

return sqlBuilder;

}

public static interface Interceptor {

/**

* for循环内是否执行break语句, break语句在循环内第一行

* @param collection 集合

* @param item 集合元素

* @param key 集合key或下标

* @return 返回true,则执行break语句

*/

boolean preBreakIntercept(C collection, K key, T item);

/**

* for循环内是否执行break语句, break语句在循环内最后一行

* @param collection 集合

* @param item 集合元素

* @param key 集合key或下标

* @return 返回true,则执行break语句

*/

boolean postBreakIntercept(C collection, K key, T item);

/**

* for循环内是否执行continue语句

* @param collection 集合

* @param item 集合元素

* @param key 集合key或下标

* @return 返回true,则执行continue语句

*/

boolean continueIntercept(C collection, K key, T item);

}

private static String replaceAll(Pattern pattern, String itemFormatter, String collection) {

itemFormatter = pattern.matcher(itemFormatter).replaceAll(collection);

return itemFormatter;

}

private static String replaceBindingMap(Pattern pattern, String item, String itemFormatter, String index, C obj) throws OgnlException {

Matcher matcher = pattern.matcher(itemFormatter);

StringBuffer buffer = new StringBuffer();

matcher.reset();

String group = "";

while (matcher.find()) {

group = matcher.group();

group = group.replaceFirst("\\$\\{" + item, "#root['" + index + "']");

group = group.substring(0, group.length() - 1).trim();

group = String.valueOf(Ognl.getValue(group, context, obj));

matcher.appendReplacement(buffer, group);

}

matcher.appendTail(buffer);

return buffer.toString();

}

private static String replaceBindingList(Pattern pattern, String item, String itemFormatter, String index, C obj) throws OgnlException {

Matcher matcher = pattern.matcher(itemFormatter);

StringBuffer buffer = new StringBuffer();

matcher.reset();

String group = "";

while (matcher.find()) {

group = matcher.group();

group = group.replaceFirst("\\$\\{" + item, "#root[" + index + "]");

group = group.substring(0, group.length() - 1).trim();

group = String.valueOf(Ognl.getValue(group, context, obj));

matcher.appendReplacement(buffer, group);

}

matcher.appendTail(buffer);

return buffer.toString();

}

public static class ParamObject {

private C obj;

private String collection;

private String item = "item";

private String index;

private String itemFormatter;

private String separator;

private String open;

private String close;

public C getObj() {

return obj;

}

public ParamObject setObj(C obj) {

this.obj = obj;

return this;

}

public String getCollection() {

return collection;

}

public ParamObject setCollection(String collection) {

this.collection = collection;

return this;

}

public String getItem() {

return item;

}

public ParamObject setItem(String item) {

this.item = item;

return this;

}

public String getIndex() {

return index;

}

public ParamObject setIndex(String index) {

this.index = index;

return this;

}

public String getItemFormatter() {

return itemFormatter;

}

public ParamObject setItemFormatter(String itemFormatter) {

this.itemFormatter = itemFormatter;

return this;

}

public String getSeparator() {

return separator;

}

public ParamObject setSeparator(String separator) {

this.separator = separator;

return this;

}

public String getOpen() {

return open;

}

public ParamObject setOpen(String open) {

this.open = open;

return this;

}

public String getClose() {

return close;

}

public ParamObject setClose(String close) {

this.close = close;

return this;

}

public StringBuilder foreach() {

return this.foreach(null);

}

@SuppressWarnings("rawtypes")

public StringBuilder foreach(Interceptor interceptor) {

return ForeachMybatisUtils.foreach(this, interceptor);

}

}

public interface InnerForeach {

CharSequence foreach(C innerObj, K index);

}

@SuppressWarnings({ "rawtypes", "unchecked" })

public static StringBuilder nestedForeach(C obj, String separator, String open, String close, InnerForeach innerForeach) {

if (obj == null) {

throw new NullPointerException("object can not is null");

}

if (separator == null) {

separator = "";

}

StringBuilder sqlBuilder = new StringBuilder();

if (open != null) {

sqlBuilder.append(open);

}

int n = 0;

int i = 0;

CharSequence sqlItem = null;

if (obj instanceof Map) {

Set set = ((Map) obj).entrySet();

for (Entry entry : set) {

sqlItem = innerForeach.foreach(entry.getValue(), entry.getKey());

if (sqlItem != null && !sqlItem.toString().trim().isEmpty()) {

if (n > 0) {

sqlBuilder.append(separator);

}

sqlBuilder.append(sqlItem);

n++;

}

i++;

}

} else if (obj instanceof List) {

List list = (List) obj;

for (Object element : list) {

sqlItem = innerForeach.foreach(element, i);

if (sqlItem != null && !sqlItem.toString().trim().isEmpty()) {

if (n > 0) {

sqlBuilder.append(separator);

}

sqlBuilder.append(sqlItem);

n++;

}

i++;

}

} else if (obj.getClass().isArray()) {

List list = Arrays.asList((Object[]) obj);

for (Object element : list) {

sqlItem = innerForeach.foreach(element, i);

if (sqlItem != null && !sqlItem.toString().trim().isEmpty()) {

if (n > 0) {

sqlBuilder.append(separator);

}

sqlBuilder.append(sqlItem);

n++;

}

i++;

}

} else {

throw new IllegalArgumentException("the Type of collection support only Array,List,Map");

}

if (n < 1) {

sqlBuilder.delete(0, sqlBuilder.length());

} else {

if (close != null) {

sqlBuilder.append(close);

}

}

return sqlBuilder;

}

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:3 Linux文件和目录管理
下一篇:11 破解Centos7.5系统root密码
相关文章

 发表评论

暂时没有评论,来抢沙发吧~