好好学习 天天向上

Java

EasyEAO 注释详解 2009/02/28

Filed under: EasyEAO — gzyangfan @ 11:38 下午

花了一整个晚上的时间,摸索着 google code wiki 的用法,终于磕磕碰碰地把这份文档写完了。期间还试过写了一半浏览器没有响应,我真的连杀人的心都有了。 

最后,终于皇天不负有心人,总算把他给弄出来了。以后有什么功能修正,或者加什么新的 Annotation,我就直接在 wiki 上改了。
文档地址:http://code.google.com/p/easy-eao/wiki/Reference

这下谁再问过 EasyEAO 怎么用,偶就发 URL 给他,嘻嘻

 

EasyEAO 入门介绍 2009/02/26

Filed under: EasyEAO — gzyangfan @ 11:02 下午

这段时间好不容易没有课要讲,可以歇一下了。于是就把一年多前的一个项目重新翻了出来重构了一下,做了个小小的开源项目(EasyEAO) 放到 Google Code 上。呵呵,所以这段时间一直都没有更新日志就是在忙这个事情咯。不过万恶的资本主义是不会让偶一直闲着的,接下来又是一个忙碌的时间段了,555…

还是说一下 EasyEAO 是做啥用的吧,相信只要做过分层结构应用开发的人,应该没有人会不知道 DAO 是什么东西的吧?DAO 这东西最初出现时是为了解决两个很重要的任务,一个就是解决应用与数据库的无关性,另一个就是把数据记录封装成 DTO 形式返回给应用。不过两这个责任在 ORM 框架日盛的今日,已经不需要 DAO 负责了。直接使用实体(Entity)进行跨层的数据传递,比使用 DTO 更为敏捷、优雅。数据库无关性的问题也不再需要开发人员考虑,ORM 框架都能很好地帮我们解决问题。这个年代的 DAO 已经变成了对实体进行存取操作,完成增、删、改、查方法的简单接口。这个时候,你不觉得他的名字不太恰当吗?因此他有了个新的名字叫 EAO (Entity Access Object)。嘻嘻,扫盲完成。

EasyEAO 的目的就是让你写 EAO 更快更好。不,应该说,让你不再需要写EAO,只需要设计EAO。他没有打算做成一个会改变你开发习惯的大个子,他很小非常小,只是一个工具,可以加入到你的开发框架当中,简化你的开发过程,让你更敏捷更高效。你可以在主流的开发框架(例如:Spring、EJB3)内使用他(目前已经完成对Spring的支持),他会提供对主流开发框架的支持。不过就算他没有提供支持也没有关系,你可以自己完成扩展。EAO 常用的存取方法,他都会支持,不过就算他没有提供支持也无所谓,你也可以自己动手添加支持。

技术的东西说是说不清楚的,还是让代码来告诉我们,EasyEAO 是什么,他可以干啥。下面就以 Spring 环境为例,示范一个实体的 EAO 使用 EasyEAO 进行开发的全过程。

创建实体类

   1: public class Exam {
   2:  
   3:     private Integer id;
   4:     private String subject;
   5:     private String student;
   6:     private int grade;
   7:  
   8:     // Getter and Setter ...
   9: }

完成Mapping

   1: <hibernate-mapping package="org.easy.eao.example">
   2:  
   3:     <class name="Exam" table="exam">
   4:         <id name="id">
   5:             <generator class="identity" />
   6:         </id>
   7:         <property name="subject" not-null="true" />
   8:         <property name="student" not-null="true" />
   9:         <property name="grade" not-null="true" />
  10:     </class>
  11:  
  12:     <query name="Exam.listByStudent"><![CDATA[
  13:         from Exam e where e.student = :student order by e.grade
  14:     ]]></query>
  15:  
  16: </hibernate-mapping>

设计 EAO 接口

你只需要考虑你的应用需要些什么方法来存取实体,完全不需要考虑如何实现,应该是根本不需要实现。只需要在定义完方法后,给方法加上特定的 @Annotation ,并对查询方法设置正确的查询语句即可。

   1: public interface ExamEao {
   2:  
   3:     @Persist
   4:     void create(Exam... exams);
   5:     
   6:     @Remove
   7:     void remove(Exam... exams);
   8:     
   9:     @Merge
  10:     void update(Exam... exams);
  11:     
  12:     @Retrieve
  13:     Exam get(Integer id);
  14:     
  15:     @Retrieve(lazy = true)
  16:     Exam load(Integer id);
  17:     
  18:     @Query(query = "from Exam e where e.subject = ? order by e.grade")
  19:     List<Exam> listBySubject(String subject);
  20:  
  21:     @Query(query = "from Exam e where e.subject = :s1 and e.student = :s2")
  22:     Exam getByStudentAndSubject(
  23:             @Param(name="s2") String Student, 
  24:             @Param(name="s1") String subject);
  25:     
  26:     @Paging(size = 5,
  27:         query = @Query(query = "from Exam e order by e.grade"))
  28:     Page<Exam> pagingAll(@Number int page);
  29:     
  30:     @Query(query = "select new org.easy.eao.example.ExamInfo(" +
  31:         "e.student, max(e.grade), min(e.grade), sum(e.grade), avg(e.grade)" +
  32:         ") from Exam e where e.student = ? group by e.student")
  33:     ExamInfo getStudentExamInfo(String student);
  34:     
  35:     @Query(named = "Exam.listByStudent")
  36:     List<Exam> listByStudent(@Param(name="student") String student);
  37: }

将 EAO 接口配置为 Spring 环境中的 Bean

除了完成必须的持久化环境配置外,只需要在 Spring Context 中加入下面的3个Bean的配置,你就可以使用你所定义的 EAO 接口了。而这3个Bean中 eaoFactory 和 abstractEao 都只需定义一次就可反复使用,如果你还有其他的 EAO Bean 就只需不断重复使用 abstractEao 进行定义即可。

   1:     <!-- EAO创建工厂 -->
   2:     <bean id="eaoFactory" class="org.easy.eao.spring.SpringEaoFactory">
   3:         <property name="actionBuilder">
   4:             <bean class="org.easy.eao.spring.SpringActionBuilder" />
   5:         </property>
   6:     </bean>
   7:  
   8:     <!-- 配置模板定义 -->
   9:     <bean id="abstractEao" class="org.easy.eao.spring.hibernate.HibernateEaoBean"
  10:         abstract="true">
  11:         <property name="eaoFactory" ref="eaoFactory" />
  12:     </bean>
  13:  
  14:     <!-- 定义一个 EAO Bean -->
  15:     <bean id="examEao" parent="abstractEao">
  16:         <property name="eaoInterface" value="org.easy.eao.example.ExamEao" />
  17:     </bean>

测试并 Enjoy 你的 EAO

测试的代码我就不列出了,如有兴趣可以下载该例子的项目源码自行测试。

项目源码

http://cid-6ac933e7557ed503.skydrive.live.com/embedrow.aspx/.Public/%e6%95%99%e7%a8%8b%e4%bb%a3%e7%a0%81/EasyEao/example.zip

依赖类库

http://cid-6ac933e7557ed503.skydrive.live.com/embedrow.aspx/.Public/%e6%95%99%e7%a8%8b%e4%bb%a3%e7%a0%81/EasyEao/libraries.zip

 

JSF 系列教程 – 写个小程序,找找感觉先 2009/02/18

Filed under: JSF — gzyangfan @ 10:39 下午

在上一篇教程里,我们已经把一个 JSF 1.2 的项目创建出来了。那个项目已经有了最基本的 JSF 项目配置信息,在 WEB-INF 目录下的 web.xml (部署描述文件)内已经帮我们加入了 JSF 的配置信息,还在同一目录下帮我们创建了名为 faces-config.xml 的配置文件。

声明 Faces Servlet 和映射
image在使用 JSF 时,请求通过 Faces Servlet 来处理。这个 Faces Servlet 的配置和普通的 Servlet 配置没有什么差别。再看下面的 mapping 设置,设置让容器把以 jsf 结尾的请求都交给 JSF 处理,JSF 会将请求转发给相应的 jsp 页面。这就是说,当我们访问 faces/hello.jsf 这个地址的时候,实际上是在访问 faces/hello.jsp 这个页面。那 JSF 在这中间,起到什么作用呢?它在显示页面之前初始化 Faces 上下文和视图根。视图根包含 JSF 组件树。Faces 上下文是与 JSF 进行交互的方式。

创建 HelloBean 类并声明为托管Bean
imageimage现在,创建一个名为 HelloBean 的POJO,然后在 faces-config.xml 文件将它声明为一个托管Bean。托管Bean的存活范围(作用域),你可以设定为none、request、session或application。设定为none时,会在需要使用的时候创建新的实例。剩下那几个作用域的含义与 jsp 内的几个作用域的含义并无不同。(在完成应用后,你可以修改该托管Bean的存活范围,观察变化)

编写 JSF 页面 
imageimage接下来我们就可以开始编写 JSF 页面了,JSF 页面其实就是 JSP 页面,只是里面使用了 JSF 特有的 Taglib 而已。我们可以使用 Eclipse 的创建向导帮助我们创建该页面,注意最后在选择页面模板的时候,我们应该选择 JSF 的页面模板。这样创建出来的页面就已经帮我们导入了必要的 JSF 标记库了。
JSF 标记库分为 html 和 core 两部分,html 标记库包含用来处理表单和其他 HTML 相关元素的所有标记。core 标记库包含 JSF 特有的所有逻辑、检验、控制器和其他标记。一个JSF 页面最重要的就是要有<f:view>标记,这个标记告诉容器管理在该标记内的组件,如果没有<f:view>,JSF 就无法构建组件树,以后也无法搜索已经创建的组件树。
接下来,我们就可以在页面内完成我们的界面布局了。这就要用到大量的 html 标记库的标记来完成布局,并与托管Bean绑定。JSF 内的所有 html 标记都有一个名为 rendered 的属性,该属性可以决定是否显示该标记及其子内容。还有标记属性的赋值,我们可以通过 JSF EL(JavaServer Faces Expression Language) 进行。这是一种非常像 JSTL EL 的表达式,它可以协助我们完成界面控件与托管Bean的绑定,或使用托管Bean的属性完成赋值。

编写 HTML 页面
image最后再创建一个index.html页面,使访问该应用时可以出现默认页面。该页面只需提供一个链接,以便用户可直接点击测试 JSF 页面是否工作正常。接下来就可以部署该应用,准备进行测试了。

部署与测试
imageimage打开服务器视图,添加应用到 Tomcat 服务器,然后点击启动按钮。如果 Console 窗口没有出现异常的错误信息,那么恭喜你,你的应用已经顺利启动了。
接下来,你就可以打开浏览器,输入该地址 http://127.0.0.1:8080/first 访问你的第一个 JSF 应用。
你会发现,当你第一次点击默认页面的链接进入 JSF 页面时,应用会不知道你的身份而让你输入你的名称。之后,你再次进入该 JSF 页面,应用会记住你的身份,无需你再次输入名称(这是因为托管Bean的存活范围是session)。

项目代码下载
http://cid-6ac933e7557ed503.skydrive.live.com/embedrow.aspx/.Public/%e6%95%99%e7%a8%8b%e4%bb%a3%e7%a0%81/JSF/first.zip

ps:
我的妈呀,写这种手把手的教程真是不是人干的活!太花时间了!!!
因此,后面不玩 Step by Step 了,还是解说代码比较适合偶的风格。

 

JSF 系列教程 – Eclipse JEE 开发环境搭建 2009/02/17

Filed under: JSF — gzyangfan @ 10:37 下午

Java™Server Faces(JSF)技术是一种服务器端框架,它提供一种基于组件的 Web 用户界面开发方式。er…好烦,本来想写一下 JSF 的简介的,不过发现 IBM developerWorks 上的说明已经非常好了,我又不想把它们直接“借过来”,因此这里省略掉N字。(IBM developerWorks 上的 JSF 入门级文章)

那么,我们就从如何搭建 JSF 开发环境开始咯。首先当然先是去下载 JSF 1.2 的实现啦。这一步,我们可以凭个人喜好下载 Sun 的参考实现(下载地址),或 Apache 的 MyFaces (下载地址)。在解压后就可以进入 Eclipse JEE 配置 JSF Libraries 。
jsf_1_1 
然后,把 JSF 的参考实现的库和 JSTL 的库都设置进去。
 jsf_1_2

这样开发环境的准备工作就完成了,我们就可以正式开始开发一个 JSF 项目了。首先,先创建一个动态的 Web 项目。这个时候不要忘记设置你的部属服务器设置(Target Runtime),不过最重要的还是把项目的配置设为 "JavaServer Faces v1.2 Project” 。
jsf_1_3 jsf_1_4 jsf_1_5
在接下去就是配置 JSF Libraries 和部署文件(web.xml)中的配置信息了,然后再点击 "Finish” 一个 JSF 项目就搭建好了。
jsf_1_6 

截图好累呀,下次还是直接开说代码算了。

 

再谈八皇后问题 2009/02/06

Filed under: 算法 — gzyangfan @ 9:57 下午

今天有学生问到我“如何写出高性能的程序?”我就跟他说:“高性能的程序一开始的性能也不一定高的,但必须是一个可以完成功能且地球人看得懂的程序,性能是不断重构后逐步提高的。”他还是对这个软件重构提高性能的过程充满迷惑,我只好继续说:“提高性能无非就是找出问题的潜在规律……(这里省略掉N个字),然后以一种适合计算机工作的方式……(这里又省略掉N个字)对程序进行改造而已。”听完我这段话,我感觉那位同学更加迷茫了。纯理论的东西听不懂,又要老子深入浅出举例说明了。

image张口就回到了前些天的“八皇后问题”的程序上了,前些日子的那个程序能工作吗?当然可以啦。性能呢?呵呵,这就不敢恭维了。那个程序就像我们为完成某一功能或解决某一问题,所写程序的第一个版本。那时候我们对问题的了解并不深入,只是不断把大问题分解成小问题,然后写出解决小问题的代码,最后再把这些代码组织起来把大问题给解决掉。随着对问题的深入认识,我们就能发现问题的更多潜在规律。仔细观察“八皇后问题”,我们就会发现其中一些潜在的规律。就拿棋盘上的对角线来说,就有公式:ROW + COL 和 ROW – COL + 7 ,分别对应左右两个方向的对角线。那么,我们就可以利用这些规律对我们的程序进行重构,让他的性能更佳。(下面就给出调整后的程序)

package com.dc.queen8;

public class EightQueenDiagonal {
	
	private static int limit = 8; // 边界
	private static int sum = 0; // 找到的方案数
	
	// 棋盘数组,数组元素为 true 表示在该位置放入了皇后
	private static boolean[][] chessboard = new boolean[limit][limit];
	// 列冲突检测数组
	private static boolean[] chkColumn = new boolean[limit];
	// 左对角线冲突检测数组
	private static boolean[] chkLeftDiagonal = new boolean[limit * 2 - 1];
	// 右对角线冲突检测数组
	private static boolean[] chkRightDiagonal = new boolean[limit * 2 - 1];
	
	/**
	 * 坐标点对象
	 * @author Frank
	 */
	static class Point {
		private int row; // 行坐标
		private int col; // 列坐标
		public Point(int row, int col) {
			this.row = row;
			this.col = col;
		}
	}
	
	public static void main(String[] args) {
		putLine(0);
	}
	
	/**
	 * 显示棋盘的情况
	 */
	private static void show() {
		System.out.println("方案:" + ++sum);
		for (int x = 0; x < limit; x++) {
			for (int y = 0; y < limit; y++) {
				System.out.print((chessboard[x][y] ? "" : "") + " ");
			}
			System.out.println();
		}		
	}
	
	/**
	 * 放入指定行的皇后
	 * @param row 行号
	 */
	private static void putLine(int row) {
		// 从第一列开始尝试放入皇后
		for (int column = 0; column < limit; column++) {
			Point p = new Point(row, column);
			if (check(p)) { // 检查指定点是否有效
				setPoint(p);
				if (p.row < limit - 1) {
					putLine(p.row + 1); // 放入下一行的皇后
				} else {
					show(); // 显示结果
				}
				clearPoint(p); // 回溯并清除放入的皇后
			}
		}
	}
	
	/**
	 * 向指定点放入皇后
	 * @param p
	 */
	private static void setPoint(Point p) {
		chessboard[p.row][p.col] = true;
		chkColumn[p.col] = true;
		chkLeftDiagonal[p.row + p.col] = true;
		chkRightDiagonal[p.row - p.col + limit - 1] = true;
	}
	
	/**
	 * 清除指定点的皇后
	 * @param p
	 */
	private static void clearPoint(Point p) {
		chessboard[p.row][p.col] = false;
		chkColumn[p.col] = false;
		chkLeftDiagonal[p.row + p.col] = false;
		chkRightDiagonal[p.row - p.col + limit - 1] = false;
	}
	
	/**
	 * 检查新放入的皇后坐标,是否会给棋盘上的其它皇后吃掉
	 * @param p
	 * @return true 新坐标有效,false 新坐标无效
	 */
	private static boolean check(Point p) {
		if (chkColumn[p.col] || chkLeftDiagonal[p.row + p.col] 
		    || chkRightDiagonal[p.row - p.col + limit - 1])
			return false;
		else
			return true;
	}

}

程序经过这样的重构后,性能就有了更大的提高。找出规律并用计算机的方式思考问题(计算机是用二进制思考的)然后重构,我们就可以得到性能更优的程序。

 

八皇后问题 2009/02/03

Filed under: 算法 — gzyangfan @ 1:23 下午

受“新年病毒”的影响,偶的学生脑袋变笨了。为了让他们尽快从“疾病”中恢复过来,今天第一天上课就给他们来了道算法题——八皇后问题。

八皇后问题
这是是一个古老而著名的问题,是回溯算法的典型例题。该问题是十九世纪著名的数学家高斯1850年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

这群家伙也真行,一个春节就把全部知识还回来了。半个多小时过去,也没有多少人的代码有点像样的感觉。没法子,只能自己DIY一份给他们参考。

package com.dc.queen8;

public class EightQueen {
	static int limit = 8; // 边界
	// 棋盘数组,数组元素为 true 表示在该位置放入了皇后
	private static boolean[][] chessboard = new boolean[limit][limit];

	/**
	 * 坐标点对象
	 * @author Frank
	 */
	static class Point {
		private int x; // 行坐标
		private int y; // 列坐标
		public Point() {}
		public Point(int x, int y) {
			this.x = x;
			this.y = y;
		}
	}
	
	public static void main(String[] args) {
		int i = 1;
		Point p = new Point();
		while (find(p)) {
			System.out.println("方案:" + i++);
			show(); // 显示结果
			p = next(prev(last())); // 找后续可用的位置(穷举的关键)
			if (p == null)
				break;
		}
	}
	
	/**
	 * 显示棋盘的情况
	 */
	private static void show() {
		for (int x = 0; x < limit; x++) {
			for (int y = 0; y < limit; y++) {
				System.out.print((chessboard[x][y] ? "" : "") + " ");
			}
			System.out.println();
		}		
	}
	
	/**
	 * 获取最后一个皇后的位置
	 * @return
	 */
	public static Point last() {
		int row = limit - 1;
		int y = 0;
		while (true) {
			if (chessboard[row][y])
				break;
			else
				y++;
		}
		chessboard[row][y] = false;
		return new Point(row, y);
	}
	
	/**
	 * 重指定点开始向下找解决方案
	 * @param p 开始
	 * @return true 找到解决方案,false 找不到解决方案
	 */
	private static boolean find(Point p) {
		while (check(p)) {
			p = next(p);
			if (p == null)
				return false;
		}
		chessboard[p.x][p.y] = true;
		if (p.x < (limit - 1))
			return find(new Point(p.x + 1, 0));
		return true;
	}
	
	/**
	 * 找下一个点
	 * @param p
	 * @return null 表示找不到可用点
	 */
	private static Point next(Point p) {
		if (p.x == 0 && p.y == limit - 1)
			return null;
		if (p.y < (limit - 1)) {
			p.y++;
			return p;
		} else {
			return next(prev(p));
		}
	}
	
	/**
	 * 回溯上一个点
	 * @param p
	 * @return
	 */
	private static Point prev(Point p) {
		int row = p.x - 1;
		for (int i = 0; i < limit; i++) {
			if (chessboard[row][i]) {
				chessboard[row][i] = false; // 清除回溯点的值
				return new Point(row, i);
			}
		}
		return null;
	}

	/**
	 * 检查新放入的皇后坐标,是否会给棋盘上的其它皇后吃掉
	 * @param p
	 * @return false 新坐标有效,true 新坐标无效
	 */
	private static boolean check(Point p) {
		// 检查同列(使用Point.y作为检查边界,节省时间)
		for (int i = 0; i < p.x; i++) {
			if (chessboard[i][p.y])
				return true;
		}
		// 检查左上方对角线,只检查上方以节省时间
		int x = p.x - 1;
		int y = p.y - 1;
		while (x >= 0 && y >= 0) {
			if (chessboard[x--][y--])
				return true;
		}
		// 检查右上方对角线,只检查上方以节省时间
		x = p.x - 1;
		y = p.y + 1;
		while (x >= 0 && y < limit) {
			if (chessboard[x--][y++])
				return true;
		}
		return false;
	}
}

本来也不想把这东西放上来的,不过Google了一下其他经典的八皇后代码,总是觉得那些代码太为算法而算法了。虽然那些经典,都比偶这份简练很多,但根本不像一边思考一边写出来的,总是给我感觉是为了拼命简化代码行数,拼命利用特定规律和计算机的特性再三优化后代码。让初学者不好入手,无法看到如何把想法变成代码的过程。