Monthly Archives: April 2014

mysql – 只对一个字段做distinct

要在所有用户记录中做一次普通的查询,要求返回的结果中,对某一个“姓”只有一条记录。

比如,你的数据是这样

引用

ln  fn  

陈  东

胡  西

陈  南

刘  北

我希望查出的是

引用

ln  fn

陈  东   // 或  “陈  南”,总之只返回一个姓陈的

胡  西

刘  北

sql要这样写:

select * from user  group by ln

mysql – 让in查询的结果按in参数列表排序

select * from t where id in (3,1,2) 

结果可能是

1, xxx

2, xxx

3, xxx

或者其它,反正不确定

select * from t where id in (3,1,2)  order by field(t.id 3, 1, 2)

就可以确定得到这样的排序结果

3, xxx

1, xxx

2, xxx

在ibatis里这样写:

 	<select id="someSql" parameterClass="list" resultMap="resultMap">		 
			select *
			from t
			where  id in 
		  <iterate open="(" close=")" conjunction=",">
  			 #[]#
  		  </iterate>										
			order by FIELD(t.id, 			
		  <iterate conjunction=",">
  			 #[]#
  		  </iterate>		
			)		 
	</select>


代码片断:安全的subList()方法

	/**
	 * 从类集中找到前几个元素,组成新的类集再返回。 <br/>
	 * 1. 如果想要的元素个数超过了类集的大小,则返回该类集中的所有元素组成的类集 <br/>
	 * 2. 原类集对象不会被修改<br/>
	 * 3. 返回ArrayList,它可能为empty,但不会是null<br/>
	 * 
	 * @param list
	 * @param elementCount
	 *            想要的元素个数; 如果个数不是正数,则返回空list
	 * @return
	 */
	public static <T> ArrayList<T> selectTopElements(List<T> list,
			int elementCount) {
		ArrayList<T> resultList = new ArrayList<T>();
		if (list == null) {
			return resultList;
		}

		if (elementCount <= 0) {
			return resultList;
		}

		List<T> subList = list.subList(0, Math.min(list.size(), elementCount));
		resultList.addAll(subList);
		return resultList;
	}

	/**
	 * 从类集的指定下标开始取一定个数的元素<br/>
	 * 0. 如果取10个,但只剩5个,则返回5个元素组成的类集<br/>
	 * 1. 原类集对象不会被修改<br/>
	 * 2. 返回ArrayList,它可能为empty,但不会是null<br/>
	 * 
	 * @author 集安 2013年10月9日下午2:21:19
	 * @param list
	 * @param startIndex
	 *            0-based,inclusive
	 * @param elementCount
	 * @return
	 */
	public static <T> ArrayList<T> selectElementsFrom(List<T> list,
			int startIndex, int elementCount) {
		ArrayList<T> resultList = new ArrayList<T>();
		if (list == null) {
			return resultList;
		}
		if (startIndex < 0) {
			return resultList;
		}
		if (elementCount <= 0) {
			return resultList;
		}
		if (startIndex >= list.size()) {
			return resultList;
		}
		List<T> subList = list.subList(startIndex,
				Math.min(list.size(), startIndex + elementCount));
		resultList.addAll(subList);
		return resultList;
	}

	/**
	 * 获取类集的某下标前的一定个数的元素<br/>
	 * 
	 * 1. 原类集对象不会被修改<br/>
	 * 2. 返回ArrayList,它可能为empty,但不会是null<br/>
	 * 
	 * @author 集安 2013年10月9日下午2:21:19
	 * @param list
	 * @param endIndex
	 *            0-based,exclusive
	 * @param elementCount
	 * @return
	 */
	public static <T> ArrayList<T> selectElementsUpTo(List<T> list,
			int endIndex, int elementCount) {
		ArrayList<T> resultList = new ArrayList<T>();
		if (list == null) {
			return resultList;
		}
		if (endIndex <= 0) {
			return resultList;
		}
		if (elementCount <= 0) {
			return resultList;
		}

		int startIndex = Math.max(0, endIndex - elementCount);
		if (startIndex > list.size() - 1) {
			return resultList;
		}

		elementCount = endIndex - startIndex;
		
		List<T> subList = list.subList(startIndex,
				Math.min(list.size(), startIndex + elementCount));
		resultList.addAll(subList);
		return resultList;
	}




逻辑删除一条记录时,最好删除相关的关系表中的记录

逻辑删除一条记录时,最好删除相关的关系表中的记录。

如果不这样做,会导致没必要的连接,在极端情况下会出现极其糟糕的性能。

今天就遇到这样一个实例。 有两个表,分别叫a表和b表,它们之间有一张a_b关系表, a可以逻辑删除。要查找b=123关联的前10个a记录,需要这样关联:

select a.a_id from a_b 
       join a on a_b.a_id = a.a_id 
       where a_b.b_id = 123 
       and a.deleted = 0 
       order by a.a_id limit 0, 10  -- a.deleted = 0  代表未删除

一般情况下,我们预期它会先根据a_b.b_id = 123找出相关的a_id,然后再根据a_id从a表中过滤中deleted = 0的记录。

然而今天发现一个奇怪的情况,如果b关联的a记录很多时,它会先按 deleted = 0扫描整个a表(deleted上有索引),然后再与a_b关联。性能极差。

数据的分布对执行计划的影响是很难捉摸的,最后的办法就是不做关联。 所以我们可以这样,当逻辑删除a记录时,清除a_b中的关系记录。这样就不用连接了:

select a_b.a_id from a_b  where a_b.b_id = 123  order by a.a_id limit 0, 10 

如果需要保留a_b表的物理记录,可以在a_b表中也加一个逻辑删除字段。

ibatis里存取enum类型

enum类型不是标准的jdbcType, 所以你要使用
type handler配置一下映射。数据库里使用varchar类型,存储enum字段的name .

/**
 * ibatis内置了一个EnumTypeHandler,
 * 但它没有无参构造函数,直接使用它会导致java.lang.InstantiationException, 所以你要另建一个类,提供一个无参构造函数,并利用装饰器模式集成EnumTypeHandler的功能。

 * ibatis内置的EnumTypeHandler还有一个缺点: 对于数据库里存储的脏数据即不在指定enum
 * name范围之内的数据,EnumTypeHandler在运行时会直接报异常; 但在一般的应用场景下,我们希望把这些脏数据解析成null就可以了。
 * 所以,我们的新类里应该在这方面提供健壮性。
 * 
 * 用法:对每个具体的枚举,请继承一下本类
 * 
 * @author kent
 * 
 */
@SuppressWarnings("rawtypes")
public abstract class MyEnumTypeHandler extends BaseTypeHandler implements TypeHandler {

	private EnumTypeHandler delegate;

	public MyEnumTypeHandler() {
		Class type = this.getEnumType();
		delegate = new EnumTypeHandler(type);
	}

	public abstract Class getEnumType();
	
	
	public void setParameter(PreparedStatement ps, int i, Object parameter,
			String jdbcType) throws SQLException {
		try {
			delegate.setParameter(ps, i, parameter, jdbcType);
		} catch (Exception e) {
		}
	}

	public Object getResult(ResultSet rs, String columnName)
			throws SQLException {
		try {
			return delegate.getResult(rs, columnName);
		} catch (Exception e) {
			return null;
		}
	}

	public Object getResult(ResultSet rs, int columnIndex) throws SQLException {
		try {
			return delegate.getResult(rs, columnIndex);
		} catch (Exception e) {
			return null;
		}
	}

	public Object getResult(CallableStatement cs, int columnIndex)
			throws SQLException {
		try {
			return delegate.getResult(cs, columnIndex);
		} catch (Exception e) {
			return null;
		}
	}

	public Object valueOf(String s) {
		try {
			return delegate.valueOf(s);
		} catch (Exception e) {
			return null;
		}
	}

	

}

关于unicode中的bmp

最早版本的unicode所允许的值是 U+0000 to U+FFFF

后来unicode扩充了,取值范围变成了U+0000 to U+10FFFF .

原先的
U+0000 to U+FFFF这个集合就称作BMP (Basic Multilingual Plane)

这个集合也是mysql utf8_general字符集所能支持的集合(三个字节); 所以,java在插入文字之前应该过滤掉non-bmp字符。

	public static String removeNonBmpUnicode(String str) {
		if (str == null) {
			return null;
		}

		str = str.replaceAll("[^\\u0000-\\uFFFF]", "");
		return str;
	}

BMP之外的字符集合称为"supplementary characters". 由于Java里每个char都是用16位来表示的,一个supplementary character在java里就要用两个特殊的bmp字符来表示。这类特殊的bmp字符分别称作high-surrogates(\uD800-\uDBFF) 和 low-surrogates(\uDC00-\uDFFF).

要注意的是,把surrogates用在java6的正则中时,得到的结果会让人很意外:

//\ud83d\udd12是一个unicode单字(”锁“的形状)
"\ud83d\udd12".matches("\ud83d\udd12")  == true //这个好理解
"\ud83d\udd12".matches("\\ud83d\\udd12")  == false //为什么?java.util.regex.Pattern的javadoc不是说 \uxxxx和\\uxxxx是等价的吗?
"\ud83d\udd12".matches("\ud83d[\udd12]")  == false //不可思议

"\ud83d".matches("[\\u0000-\\uFFFF]") == true  //符合预期
"\ud83d\udd12".matches("[\\u0000-\\uFFFF]+") == false //为什么?
"\ud83d\udd12".matches("[^\\u0000-\\uFFFF]+") == true // 无语了。。。

说白了,surrogate不遵守普通的正则律。

转:为spring mvc加上csrf防范的简单方案

官方文档建议你引入spring security,然后用一行简单的xml配置就可以搞定csrf防范。

但是引入spring security意味着你必须使用它的一套authentication & authorization机制,而且这玩意儿也不轻量。

网上搜了下,下面这个方案简单又可靠,你抄一些它的代码即可:

http://blog.eyallupu.com/2012/04/csrf-defense-in-spring-mvc-31.html

=======

以上只对普通form提交方式有效

对于ajax方式,可以在页面任何一处写一个<form:form/>,就可以在页面源码中生成这样一个token; ajax在提交时,把这个token作为post的内容提交就可以了