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

 更新时间:2021年11月30日 10:26:52   作者:daodfs1  
这篇文章主要介绍了mybatis注解动态sql中foreach工具方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

实现目标

由于在注解版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 <C> ParamObject<C> createParamObject(C obj) {
		ParamObject<C> paramObject = new ParamObject<C>();
		paramObject.setObj(obj);
		return paramObject;
	}
	
	public static <C> StringBuilder foreach(ParamObject<C> paramObject) {
		return foreach(paramObject, null);
	}
	
	@SuppressWarnings("rawtypes")
	public static <C> StringBuilder foreach(ParamObject<C> 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 <C> 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 <C> 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 <C> 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 <C> 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<Entry> 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<C, K, T> {
		
		/**
		 * 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 <C> 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 <C> 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<C> {
		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<C> setObj(C obj) {
			this.obj = obj;
			return this;
		}
		public String getCollection() {
			return collection;
		}
		public ParamObject<C> setCollection(String collection) {
			this.collection = collection;
			return this;
		}
		public String getItem() {
			return item;
		}
		public ParamObject<C> setItem(String item) {
			this.item = item;
			return this;
		}
		public String getIndex() {
			return index;
		}
		public ParamObject<C> setIndex(String index) {
			this.index = index;
			return this;
		}
		public String getItemFormatter() {
			return itemFormatter;
		}
		public ParamObject<C> setItemFormatter(String itemFormatter) {
			this.itemFormatter = itemFormatter;
			return this;
		}
		public String getSeparator() {
			return separator;
		}
		public ParamObject<C> setSeparator(String separator) {
			this.separator = separator;
			return this;
		}
		public String getOpen() {
			return open;
		}
		public ParamObject<C> setOpen(String open) {
			this.open = open;
			return this;
		}
		public String getClose() {
			return close;
		}
		public ParamObject<C> 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<C, K> {
		CharSequence foreach(C innerObj, K index);
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static <C> 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<Entry> 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<String[]> list = Arrays.asList(strs);
	Map<String, Object> 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<Map<String, Object>, String, String[]>() {
			@Override
			public boolean preBreakIntercept(Map<String, Object> collection, String key, String[] item) {
				// TODO Auto-generated method stub
				return false;
			}
			@Override
			public boolean postBreakIntercept(Map<String, Object> collection, String key, String[] item) {
				// TODO Auto-generated method stub
				return false;
			}
			@Override
			public boolean continueIntercept(Map<String, Object> 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<String[][], Integer, String[]>() {
			@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<String[][], Integer, String[]>() {
			@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<String[][], Integer, String[]>() {
			@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<List<Integer>> lists = new ArrayList<List<Integer>>();
		List<Integer> list1 = new ArrayList<>();
		list1.add(1);
		list1.add(2);
		list1.add(3);
		lists.add(list1);
		List<Integer> list2 = new ArrayList<>();
		list2.add(11);
		list2.add(12);
		list2.add(13);
		list2.add(14);
		list2.add(19);
		lists.add(list2);
		List<Integer> 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<List<Integer>, Integer>() {
					@Override
					public CharSequence foreach(List<Integer> 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<Map<String, Object>> queryDevices(@Param("devicetypeno") String devicetypeno, @Param("list") List<Object> list);
	
	@SelectProvider(type = ManageProvider.class, method = "queryMap")
	List<Map<String, Object>> queryMap(@Param("map") Map<String, List<Integer>> map);
	@SelectProvider(type = ManageProvider.class, method = "queryList")
	List<Map<String, Object>> queryList(@Param("list") List<Map<String, Object>> 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<String, Object> params) {
		@SuppressWarnings("unchecked")
		List<Object> list = (List<Object>) 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<String, Object> params) {
		@SuppressWarnings("unchecked")
		Map<String, List<Integer>> map = (Map<String, List<Integer>>) params.get("map");
		StringBuilder sqlBuilder = ForeachMybatisUtils.nestedForeach(map,
				" union all ", "select b.id, b.name from (", ") b",
				new ForeachMybatisUtils.InnerForeach<List<Integer>, String>() {
					@Override
					public CharSequence foreach(List<Integer> 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<String, Object> params) {
		@SuppressWarnings("unchecked")
		List<Map<String, Object>> list = (List<Map<String, Object>>) params.get("list");
		StringBuilder sqlBuilder = ForeachMybatisUtils.nestedForeach(list,
				" union all ", "select b.id, b.name from (", ") b",
				new ForeachMybatisUtils.InnerForeach<Map<String, Object>, Integer>() {
					@Override
					public CharSequence foreach(Map<String, Object> 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 <C> ParamObject<C> createParamObject(C obj) {
		ParamObject<C> paramObject = new ParamObject<C>();
		paramObject.setObj(obj);
		return paramObject;
	}
	
	public static <C> StringBuilder foreach(ParamObject<C> paramObject) {
		return foreach(paramObject, null);
	}
	
	@SuppressWarnings("rawtypes")
	public static <C> StringBuilder foreach(ParamObject<C> 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 <C> 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 <C> 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 <C> 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 <C> 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<Entry> 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<C, K, T> {
		
		/**
		 * 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 <C> 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 <C> 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<C> {
		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<C> setObj(C obj) {
			this.obj = obj;
			return this;
		}
		public String getCollection() {
			return collection;
		}
		public ParamObject<C> setCollection(String collection) {
			this.collection = collection;
			return this;
		}
		public String getItem() {
			return item;
		}
		public ParamObject<C> setItem(String item) {
			this.item = item;
			return this;
		}
		public String getIndex() {
			return index;
		}
		public ParamObject<C> setIndex(String index) {
			this.index = index;
			return this;
		}
		public String getItemFormatter() {
			return itemFormatter;
		}
		public ParamObject<C> setItemFormatter(String itemFormatter) {
			this.itemFormatter = itemFormatter;
			return this;
		}
		public String getSeparator() {
			return separator;
		}
		public ParamObject<C> setSeparator(String separator) {
			this.separator = separator;
			return this;
		}
		public String getOpen() {
			return open;
		}
		public ParamObject<C> setOpen(String open) {
			this.open = open;
			return this;
		}
		public String getClose() {
			return close;
		}
		public ParamObject<C> 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<C, K> {
		CharSequence foreach(C innerObj, K index);
	}	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static <C> 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<Entry> 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;
	}
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java实现SSH模式加密

    Java实现SSH模式加密

    这篇文章主要介绍了Java实现SSH模式加密的相关资料,需要的朋友可以参考下
    2016-01-01
  • mybatis中返回主键一直为1的问题

    mybatis中返回主键一直为1的问题

    这篇文章主要介绍了mybatis中返回主键一直为1的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • 关于SpringBoot微服务发布与部署的三种方式

    关于SpringBoot微服务发布与部署的三种方式

    SpringBoot 框架只提供了一套基于可执行 jar 包(executable jar)格式的标准发布形式,但并没有对部署做过多的界定,而且为了简化可执行 jar 包的生成,SpringBoot 提供了相应的 Maven 项目插件,需要的朋友可以参考下
    2023-05-05
  • SpringCloud中的Hystrix保护机制详解

    SpringCloud中的Hystrix保护机制详解

    这篇文章主要介绍了SpringCloud中的Hystrix保护机制详解,Hystrix,英文意思是豪猪,全身是刺,看起来就不好惹,是一种保护机制,Hystrix也是Netflix公司的一款组件,需要的朋友可以参考下
    2023-12-12
  • java中Filter过滤器处理中文乱码的方法

    java中Filter过滤器处理中文乱码的方法

    java中Filter过滤器处理中文乱码的方法,需要的朋友可以参考一下
    2013-05-05
  • Java实现InputStream的任意拷贝方式

    Java实现InputStream的任意拷贝方式

    这篇文章主要介绍了Java实现InputStream的任意拷贝方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • 二种jar包制作方法讲解(dos打包jar eclipse打包jar文件)

    二种jar包制作方法讲解(dos打包jar eclipse打包jar文件)

    这篇文章主要介绍了二种jar包制作方法讲解:dos打包jar和eclipse打包jar文件,大家参考使用吧
    2013-11-11
  • MyBatis中常见的SQL执行方式及其使用方法

    MyBatis中常见的SQL执行方式及其使用方法

    MyBatis可能很多人都一直在用,但是MyBatis的SQL执行流程可能并不是所有人都清楚了,下面这篇文章主要给大家介绍了关于MyBatis中常见的SQL执行方式及其使用的相关资料,需要的朋友可以参考下
    2023-09-09
  • java使用itext导出PDF文本绝对定位(实现方法)

    java使用itext导出PDF文本绝对定位(实现方法)

    下面小编就为大家带来一篇java使用itext导出PDF文本绝对定位(实现方法)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • 详解SpringCloud微服务架构之Hystrix断路器

    详解SpringCloud微服务架构之Hystrix断路器

    本篇文章主要介绍了详解SpringCloud微服务架构之Hystrix断路器,Hystrix是一个库,通过添加延迟容差和容错逻辑来帮助您控制这些分布式服务之间的交互,有兴趣的可以了解一下
    2018-01-01

最新评论