Monthly Archives: March 2014

一台电脑使用多个github账号时如何免密登录

1. 生成各自的ssh key

ssh-keygen -t rsa -C "foo@test.com"  #选择key文件为~/.ssh/id_rsa_foo
ssh-keygen -t rsa -C "bar@test.com"  #选择key文件为~/.ssh/id_rsa_bar

2. 将这两个ssh key加入到系统中

ssh-add ~/.ssh/id_rsa_foo
ssh-add ~/.ssh/id_rsa_bar
ssh-add -l

3. 配置两个虚拟ssh host,分别对应每个ssh key.  这样在进行ssh登录github时,系统知道应该选择哪一个ssh key

引用
#~/.ssh/config

Host github-foo

HostName github.com

User git

IdentityFile ~/.ssh/id_rsa_foo

Host github-bar

HostName github.com

User git

IdentityFile ~/.ssh/id_rsa_bar

4. 将这两个ssh key的公钥部分分别粘贴到github的账号设定中,这样才能免密登录

请参考:
https://help.github.com/articles/generating-ssh-keys 中的Step3 . 把 ~/.ssh/id_rsa.pub 分别替换成 ~/.ssh/id_rsa_foo 和 ~/.ssh/id_rsa_bar

5. 测试登录

ssh -T github-foo  #不是github.com,这里要用虚拟host
ssh -T github-bar  

6. Clone时选择ssh clone url,并使用虚拟host替换url中的”github.com”

git clone git@[color=red]github-foo[/color]:SomeUser1/SomeProject1.git
git clone git@[color=red]github-bar[/color]:SomeUser2/SomeProject2.git

7. 修改一下你作为代码作者时的名字和email, 否则github在显示某个改动提交者的信息时可能会张冠李戴

请见 这里

8. 改个文件提交一下(commit + push),你应该不会被提示用户名、密码

注:这里介绍的处理办法不仅限于github, 它适用于任何git repository,或者github + 其他git-repository混合使用的情况,唯一不同的地方可能在于上述的第4步,你需要咨询你的git repository管理员,了解粘贴公钥的办法。

改变当前git repo的author name和author email

如果你原有一个git账号,它是 UserA/UserA@test.com, 并且你用它工作过

现在你又搞了一个git账号,用这个新账号提交代码后,github(或其他git中心)页面上的日志会说刚刚的代码是由“UserA/UserA@test.com" 提交的。这就很搞笑了。

为什么会这样? 因为你可能已经把UserA设置为global username了

可以这样检查一下

git config --global --get user.name
git config --global --get user.email

解决方案是在当前repo中,修改git客户端的username/email:

  cd your-working-dir
  git config user.name "UserB"
  git config user.email UserB@test.com

注: 这里的username/email只用于git commit中的author记录,跟用于登录github的用户名/密码没有关系

二叉查找树与平衡二叉树

内容编译自网络

Binary Search Tree

什么情况下会出现O(n) ? 如果树的根结点没选好,一棵树变成一个列表:

平衡二叉查找树 –  可以自动旋转

参考:

http://en.wikipedia.org/wiki/Binary_search_tree
http://www.cnblogs.com/huangxincheng/archive/2012/07/21/2602375.html
http://www.cnblogs.com/huangxincheng/archive/2012/07/22/2603956.html

代码片断:远程接口返回数据中的属性过滤

从网络流量考虑,我们的远程接口有时并不需要返回所有数据。为此,可以通过反射去掉不要的属性。


	/**
	 * 过滤属性,省流量
	 * 
	 *  
	 * @param bean
	 * @param filter
	 */
	public static void filterProps(Object bean,
			ResultPropsFilter filter) {
		if (filter == null) {
			return;
		}
		PropertyDescriptor[] pdArray = PropertyUtils
				.getPropertyDescriptors(bean);
		if (pdArray == null) {
			return;
		}

		for (PropertyDescriptor pd : pdArray) {
			String propName = pd.getName();
			if(propName.equals("class")){
				continue;
			}
			Class<?> type = pd.getPropertyType();
			// 原子类型不支持null, 无法过滤。这也意味着应该尽量使用封装类型,而不是原子类型
			if (type.isPrimitive()) {
				continue;
			}
			if (!filter.isPropIncluded(propName)) {
				// 不需要去掉
				continue;
			}
			String errIfFail = "cannot set property " + propName + " to null";
			try {
				PropertyUtils.setProperty(bean, propName, null);
			} catch (IllegalAccessException e) {
				logger.error(errIfFail, e);
			} catch (InvocationTargetException e) {
				logger.error(errIfFail, e);
			} catch (NoSuchMethodException e) {
				logger.error(errIfFail, e);
			}
		}

	}

public abstract class ResultPropsFilter {

	public static final String FILTER_MODE_INCLUDE = "INCL";
	public static final String FILTER_MODE_EXCLUDE = "EXCL";

	/**
	 * 返回结果属性过滤模式。如果为INCL,则服务端会忽略excludeProps; 如果为EXCL,则服务端会忽略includeProps;
	 * 默认情况下为EXCL模式且excludeProps为空,意味着服务端返回所有值
	 */
	private String filterMode = FILTER_MODE_EXCLUDE;

	/**
	 * 需要包含的属性。
	 */
	private List<String> includeProps;

	/**
	 * 需要排除的属性。
	 */
	private List<String> excludeProps;

	public boolean isPropIncluded(String prop) {
		if (FILTER_MODE_INCLUDE.equals(filterMode)) {
			return getIncludeProps().contains(prop);
		}

		if (FILTER_MODE_EXCLUDE.equals(filterMode)) {
			return !getExcludeProps().contains(prop);
		}
		return false;

	}
    ...
}

基于比较的分页机制的注意事项

分页机制有两种: 一种是根据偏移量(或页码)+页大小获取数据,另一种是根据上一次看过的最后一条记录+页大小获取本页记录。

后一种机制常见于weibo/twitter这种feed流页面:记住当前页最后一条feed的发表时间,然后找出比这个时间更早的feed.   这种情况下一般不需要知道feed总数。

姑且把这种机制称为“基于比较的分页”。 使用这种机制时,需要注意的地方有:

1. 用作比较的维度应该是唯一的,在做大小比较时才不会遗漏数据。如果按ID来比较,则比id=1更大的记录是(2,3,…),不会遗漏;  如果按发表时间来比较,则比100毫秒更大的记录是(101, 102…), 而同在第100毫秒发表的其他记录就会被遗漏掉。

2. 除了往下翻页,还可能要往上翻页; 系统应该同时支持。

3. “首页”应该传什么值作为比较基准? 如果id为Long型,应该传-1还是传一个Long.maxValue()? 这种地方最容易埋坑,引发程序错误和沟通困难。 最好的办法是再另传一个参数:firstPage = true, 简单明了。

4. 服务端回传记录时,应该同时回传本页记录第一条和最后一条的比较值,比如第一个feed的id和最后一个 feed的id; 客户端/浏览器 再根据这两个记录决定上一页和下一页的URL。你会说不传也可以,客户端自己从feed列表中找出这两个id; 然而用于比较分页的维度未必是feed的属性,从feed列表中是找不出来的,比如分页查询“我评论过的feed”时,要通过评论ID来做比较,评论ID显然并不是feed的属性。

5. 第4点帮助你得出一个结论:分页参数可以跟业务无关,客户端不必知道分页维度的业务意义; 同理,也不必知道下一页的记录是比某个属性更“大”,还是更“小”,客户端只需要传“下一页”,或是“上一页”,让服务端自己决定取更大或是更小的记录。

6. 有了这个结论,我们可以做一个通用的、适用于任何业务分页参数类。示范代码:

分页参数类:

public class PageByCompareParam {

	 * 服务端将跟这个值比较。日期时间类型请传毫秒数时间戳,数字类型将转成字符串后再传过来
	 */
	private String value;

	/**
	 * 页大小
	 */
	private int pageSize;

	/**
	 * true = next page, false = previous page. 默认为true
	 */
	private boolean next = true;


	/**
	 * 访问的是否是第一页,如果是的话,value将被忽略。默认为true
	 */
	private boolean firstPage = true;
  
         ...
}

结果参数类:

public class PageQueryResult {
	/**
	 * 本页结果中的第一条记录的相关值。
	 */
	private String resultFirstValue;

	/**
	 * 本页结果中的最后一条记录的相关值。
	 */
	private String resultLastValue;
}

7. 上述PageQueryResult.resultFirstValue和PageQueryResult.resultLastValue从哪里来? 为了解决这个问题,最好为你的每一条查询记录配一个sortOrder属性;当sortOrder不是记录本身的属性时,这一点尤其必要。所以,你内部使用的查询结果可以这样写:


public class PageByCompareQueryResult<D, S> {

	/**
	 * 本页结果
	 */
	private List<ResultEntry<D, S>> resultList; 

	public S getFirstSortOrder() {
		return resultList.get(0).getSortOrder();
	}

	public S getLastSortOrder() {
		return resultList.get(resultList.size() - 1).getSortOrder();
	}

	public List<D> getDataList() {
		......
	}

	/**
	 * 结果列表中的一个元素,由数据本身和用于比较用的排序号组成
	 * 
	
	 * 
	 * @param <D>
	 * @param <S>
	 */
	public static class ResultEntry<D, S> {
		private D data;
		private S sortOrder;
		......
	}
}

7. 最后附一段ibatis代码:

	public List<Post> selectPosts(long idToCompareWith, int pageSize,
			NumCompareFlag numCompareFlag) {
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("id", idToCompareWith);
		params.put("rowCount", pageSize);
		params.put("numCompareFlag", numCompareFlag.name());
		return getSqlMapClientTemplate().queryForList("selectPosts",
				params);
	}

	public static enum NumCompareFlag {

		/**
		 * 更大。 
		 */
		GREATER,
		/**
		 * 更小。 
		 */
		LESS;

	}


	<select id="selectPosts" parameterClass="map" resultMap="resultMap">	
			select  *
			from post
			where  
			<include refid="id_compare"/>								
			order by id desc
			LIMIT 0, #rowCount#	 
	</select>	


	<sql id="id_compare">
			<isEqual prepend="and"  property="numCompareFlag"  compareValue="LESS">
				id &lt; #id#
			</isEqual> 
			<isEqual prepend="and"  property="numCompareFlag"  compareValue="GREATER">
				id &gt; #id#
			</isEqual> 					
	</sql>	




Jdbc代码片断:使sql in子句中的记录数恒定

	private List<Long> toNormalizedList(List<Long> idList) {
		List<Long> newList = new ArrayList<Long>();
		newList.addAll(idList);
		for (int i = idList.size() + 1; i <= 100; i++) { // 填满100个,使得每个prepared
															// statement都有100参数,以充分利用sql缓存
			newList.add(idList.get(0));
		}
		return newList;
	}

相应的ibatis sql map:


	<select id="batchGet" parameterClass="list" resultClass="java.lang.Long">
		select distinct *
		from some_table
		where id in
		  <iterate open="(" close=")" conjunction=",">
  			 #[]#
  		  </iterate>
	</select>