2005年10月25日
帕金森定律
美国著名历史学家诺斯古德-帕金森通过长期调查研究,写了一本名叫《帕金森定律》的书,他在书中阐述了机构人员膨胀的原因及后果:一个不称职的官员,可能有三条出路。第一是申请退职,把位子让给能干的人;第二是让一位能干的人来协助自己工作;第三是任用两个水平比自己更低的人当助手。
这第一条路是万万走不得的,因为那样会丧失许多权力;第二条路也不能走,因为那个能干的人会成为自己的对手;看来只有第三条路最适宜。于是,两个平庸的助手分担了他的工作,他自己则高高在上发号施令。两个助手既无能,也就上行下效,再为自己找两个无能的助手。如此类推,就形成了一个机构臃肿、人浮于事、相互扯皮、效率低下的领导体系。
苛希纳定律
西方管理学中有一条著名的苛希纳定律:如果实际管理人员比最佳人数多两倍,工作时间就要多两倍,工作成本就要多四倍;如果实际管理人员比最佳人数多三倍,工作时间就要多三倍,工作成本就要多六倍。
苛希纳定律告诉我们,在管理上并不是人多力量大,管理人员越多,工作效率未必就会越高。苛希纳定律要求我们,要认真研究并找到一个最佳人数,以最大限度地减少工作时间,降低工作成本。
马蝇效应
林肯少年时和他的兄弟在肯塔基老家的一个农场里犁玉米地,林肯吆马,他兄弟扶犁,而那匹马很懒,慢慢腾腾,走走停停。可是有一段时间马走得飞快。林肯感到奇怪,到了地头,他发现有一只很大的马蝇叮在马身上,他就把马蝇打落了。看到马蝇被打落了,他兄弟就抱怨说:“哎呀,你为什么要打掉它,正是那家伙使马跑起来的嘛!”
没有马蝇叮咬,马慢慢腾腾,走走停停;有马蝇叮咬,马不敢怠慢,跑得飞快。这就是马蝇效应。马蝇效应给我们的启示是:一个人只有被叮着咬着,他才不敢松懈,才会努力拼搏,不断进步。
“南风”法则
“南风”法则也称“温暖”法则,源于法国作家拉封丹写过的一则寓言:北风和南风比威力,看谁能把行人身上的大衣脱掉。北风首先来一个冷风凛冽寒冷刺骨,结果行人为了抵御北风的侵袭,便把大衣裹得紧紧的。南风则徐徐吹动,顿时风和日丽,行人因为觉得春暖上身,始而解开纽扣,继而脱掉大衣,南风获得了胜利。
这则寓言形象地说明了一个道理:温暖胜于严寒。领导者在管理中运用“南风”法则,就是要尊重和关心下属,以下属为本,多点“人情味”,尽力解决下属日常生活中的实际困难,使下属真正感受到领导者给予的温暖,从而激发出工作的积极性。
酒与污水定律
酒与污水定律是指,如果把一匙酒倒进一桶污水中,你得到的是一桶污水;如果把一匙污水倒进一桶酒中,你得到的还是一桶污水。
在任何组织里,都存在几个难弄的人物,他们存在的目的似乎就是为了把事情搞糟。最糟糕的是,他们像果箱里的烂苹果一样,如果你不及时处理,它会迅速传染,把果箱里其他的苹果也弄烂。“烂苹果”的可怕之处在于它那惊人的破坏力。一个正直能干的人进入一个混乱的部门可能被吞没,而一个无德无才者能很快将一个高效的部门变成一盘散沙。一个能工巧匠花费时日精心制作的陶瓷品,一头驴子一秒钟就能将它毁坏掉。
posted @
2007-11-27 14:18 surffish 阅读(369) |
评论 (0) |
编辑 收藏
1、WINKEY+D:这是高手最常用的第一快捷组合键(木头按:也是我最喜欢用的哦,好像有点自诩为高手)。这个快捷键组合可以将桌面上的所有窗口瞬间最小化,无论是聊天的窗口还是游戏的窗口……只要再次按下这个组合键,刚才的所有窗口都回来了,而且激活的也正是你最小化之前在使用的窗口!
2、WINKEY + F:不用再去移动鼠标点“开始→搜索→文件和文件夹”了,在任何状态下,只要一按WINKEY+F就会弹出搜索窗口。
3、WINKEY + R:在我们的文章中,你经常会看到这样的操作提示:“点击‘开始→运行’,打开‘运行’对话框……”。其实,还有一个更简单的办法,就是按WINKEY + R!
4、ALT + TAB :如果打开的窗口太多,这个组合键就非常有用了,它可以在一个窗口中显示当前打开的所有窗口的名称和图标,选中自己希望要打开的窗口,松开这个组合键就可以了。而ALT+TAB+SHIFT键则可以反向显示当前打开的窗口。
5、WINKEY + E:当你需要打开资源管理器找文件的时候,这个快捷键会让你感觉非常“爽”!再也不用腾出一只手去摸鼠标了!
6、WINKEY+L,锁定计算机
小提示:
WINKEY指的是键盘上刻有Windows徽标的键。WINKEY主要出现在104键和107键的键盘中。104键盘又称Win95键盘,这种键盘在原来101键盘的左右两边、Ctrl和Alt键之间增加了两个Windwos键和一个属性关联键。107键盘又称为Win98键盘,比104键多了睡眠、唤醒、开机等电源管理键,这3个键大部分位于键盘的右上方。
posted @
2007-02-15 15:30 surffish 阅读(165) |
评论 (0) |
编辑 收藏
不论你是单一团队的领导者还是多个团队的管理人,团队管理工作都是你职权范围内一个重要的组成部分。在今日,集多重技术于一身的工作方法已逐渐取代阶层式的、缺乏弹性的传统工作体制,团队合作因而很快就成为了一种很受欢迎的工作方式。对于每一位参与团队管理工作的人而言,《团队管理》是一本不可或缺的重要读物。它向你提供了达成计划所需的技巧、建立团队成员间的信任、激发团队最大的潜能等方面的知识,为你能专业化地管理好你的团队创造了有利条件。另外,全书还散布了101条简明提示,为你提供重要而实用的讯息。后半部分有个自我评估练习,使你能正确地评估自己的领导能力,并针对自己的不足加以改进。
了解团队运作
团队合作是所有成功管理的根基。无论你是新手还是资深管理人,对你而言,管理好团队都是重要且具激励性的挑战。
1.切记:每位成员都能为团队作出一些贡献。
2.谨慎地设定团队目标,且认真严肃地对待它们。
3.切记成员间要彼此扶持。
4.将长程目标打散成许多短程计划。
5.为每个计划设定明确的期限。
6.尽早决定何种形态的团队适合你的目标。
7.努力与其它团队的成员建立强有力的紧密关系。
8.找一位可提升团队工作士气的重量级人物。
9.时时提醒团队成员:他们都是团队的一份子。
10.将团队的注意力集中在固定可衡量的目标上。
11.利用友谊的强大力量强化团队。
12.选择领导者时要把握用人唯才原则。
13.领导者需具备强烈的团队使命感。
14.奖赏优异的表现,但绝不姑息错误。
15.记住每位团队成员看事情的角度都不一样。
16.征召团队成员时,应注重他们的成长潜能。
17.密切注意团队成员缺少的相关经验。
18.应使不适任的成员退出团队。
19.找到能将人际关系处理得很好的人,并培养他们。
设立一支团队
成立一支团队是领导者的主要工作。确保你的团队有清楚明确的目的和足够达成目标的资源。要以开放和公正无私的态度对待团队成员。
20.设定具挑战性的目标须根据限期来考量是否合理。
21.设定目标时,考量个别成员的工作目标。
22.计划的失败危及整体计划的成功。
23.坚持得到信息技术支持,它能为你提供确实需要的东西。
24.对待团队外的顾问要如同对待团队成员一般。
25.让团队的赞助者随时知道工作进展情形。
26.除非你确定没有人能够胜任,否则应避免“事必躬亲”。
27.不要委托不必要的工作,最好将其去除掉。
28.赋予团队自己作决策的权力。
29.鼓励团队成员正面积极的贡献。
30.肯定、宣扬和庆祝团队每次的成功。
31.找到易于让成员及团队了解每日工作进度的展现方式。
32.鼓励成员之间建立工作上的伙伴关系。
33.鼓励天生具有领导才能的人,并引导和培养他们的领导技巧。
34.绝对不能没有解释就驳回团队的意见,与此相反,解释要坦白,理由要充分。
35.确定团队和客户经常保持联系。
36.以自信肯定的态度让团队知道谁当家,但要预防予人来势汹汹的感觉。
37.想办法给新团队留下一个实时的好印象,但切忌操之过急。
38.倘若你要求别人的建议,抱持的心态不能只是欢迎就行了,也要依循建议有所行动。
提升团队效率
团队要达到应有的效率,唯一的条件是每个成员都要学会集中力量。你必须了解团队的能力,以确保团队的成功。
39.协助团队找出方法以改变有碍任务推展的团体行为。
40.找出可建设性地利用冲突的方法。
41.记住要在工作中穿插安排娱乐调剂身心──这是每个人应得的福利。
42.若有计划出错,一定要作全面性、公开化的分析。
43.如果你希望团队成员有问题时能毫不犹疑地找你谈,就要实施“开门政策”。
44.要求提出问题的人解决问题。
45.安排正式的和非正式的会面,讨论团队的工作进展。
46.使用不带感情只问事实的态度,是化解纷争的最好方法。
47.保持团队成员间的熟稔,以易于沟通。
48.设立交谊场所,让团队成员可作非正式的碰面交谈。
49.鼓励同事间自由的沟通活动。
50.建立最适合的通讯科技系统,并经常更新。
51.实施会议主席轮流制,让每个人都有机会主持会议。
52.尽可能多地授权给团队成员。
53.事先于会前发出议程,预留时间给与会者准备。
54.培养所有对团队有益的关系。
55.努力保持团队内外关系的均衡与平稳。
56.确定所有相关人士都能听到、了解好消息。
57.倘有麻烦在团队关系中发酵蕴酿,要尽快处理。
58.安排团队与机构的其它部门作社交联谊。
59.找出你与“大佬”保持联系的最佳通讯科技。
60.要对你在团队或办公室外接触过的重要人士作联系记录。
61.谨慎分派角色以避免任务重复。
62.找寻建议中的精华,且绝不在公开场合批评任何建议。
63.一定要找有经验的人解决问题。
64.分析团队成员每个人所扮演的角色。
65.脑力激发出的意见,就算不采用,亦不得轻视。否则,会打击人的积极性,创意的流动也会因此停止。
66.公平对待每个成员才能避免怨恨。
67.确定团队成员真正有错之前,都须视他们没有错。
68.告诉同事他们做得很好,这有助于激励团队士气。
69.尊重每一位成员,包括那些给你制造麻烦的人。
70.避免和团队成员有直接的冲突。
71.记住采用对事不对人的处事态度。
72.确定整个团队都能够从解决问题中学习经验。
73.先选择完成一些规模大的、可快速达成及有成就感的任务,以激励成员再接再励。
74.确信团队成员皆了解团队中的其它角色。
75.计算品质的成本之前,先计算失败的成本。
76.针对每笔预算及每项团队行动计划,设定重大的改进目标。
为未来努力
为团队设定新的、更高的挑战目标是团队工作中最令人兴奋的事情之一。可运用一些适当的技巧,推动团队向更大、更好的目标前进。
77.告知团队每位成员,在设定的标准中有哪些评量的项目。
78.确定所有改善措施及新订目标都持续进行着。
79.召开检讨会议前传阅所有相关资料及资料。
80.开检讨会时一定要避讳人身攻击。
81.记住关系会随时间改变。
82.避开低估或忽视坏消息的陷井。
83.每天结束时自问团队今天是否又向前跨出了一步。
84.倾听受训者关于训练课程的回馈意见。
85.找到有最好设备的最佳训练场所。
86.聘请顾问设立公司内部的训练课程。
87.利用移地训练时的用餐时间作非正式的计划。
88.每位团队成员都必须参与设定目标的工作,以促进团队合作及达成共识。
89.允许团队自行决定达成目标的方法,可激励团队努力工作。
90.确定目标能激发团队的斗志,如果不行,请改变目标。
91.一支没有“严峻”目标的团队,工作表现将不如接受过此类考验的团队。
92.设定奖励标准时,允许团队成员有发言权。
93.避免使用名次表,因为落后的团队成员将会感到自尊心受创。
94.指定某人监视市场上每一个相关变化。
95.随时准备作改变,甚至计划的根本要素亦包含在改变的范围内。
96.记住有某些人很害怕变革。
97.寻找能推动改革的团队成员。
98.每隔一段时间作一次生涯发展的评量。
99.记住:鼓励团队成员即是在帮助团队。
100.与团队同事就生涯规划达成一致意见,并给他们提供必要的协助。
101.团队解散后仍旧要与团队成员保持联系,因为你可能还会与他们再次合作
posted @
2006-09-21 08:05 surffish 阅读(201) |
评论 (0) |
编辑 收藏
LEFT JOIN 和 RIGHT JOIN 运算
用于 FROM 子句时,把源表记录组合起来。
语法
FROM表1 [ LEFT | RIGHT ] JOIN表2
ON 表1.字段1compopr 表2. 字段2
LEFT JOIN 及 RIGHT JOIN 运算可分为以下几个部分:
部分 |
说明 |
table1, table2 |
记录被组合的表的名称。 |
field1, field2 |
被联接的字段的名称。且这些字段必须有相同的数据类型及包含相同类型的数据,但它们不需要有相同的名称。 |
compopr
|
任何的关系比较运算子:"=," "<," ">," "<=," ">=," 或 "<>." |
说明
用 LEFT JOIN 运算 创建左边外部联接.左边外部联接将包含了从第一个(左边)开始的两个表中的全部记录,即使在第二个(右边)表中并没有相符值的记录。
用RIGHT JOIN 运算 创建 右边外部联接.右边外部联接将包含了从第二个(右边)开始的两个表中的全部记录,即使在第一个(左边)表中并没有匹配值的记录。
例如,可以使用 LEFT JOIN 与部门(左边)及员工(右边)表来选择所有的部门,包含了没有分配到员工的部门。可以使用 RIGHT JOIN 选择所有的员工,包含了没有分配到部门的员工。
下列示例显示如何在类标识符字段中联接类表及产品表。查询将会列出所有种类的列表,包含那些没有产品在其中的种类:
SELECT CategoryName,
ProductName
FROM Categories LEFT JOIN Products
ON Categories.CategoryID = Products.CategoryID;
在本例中,CategoryID 是联接的字段,但由于它不包含在 SELECT 语句中,因此,也不包含在查询结果中。要包含联接的字段,请在 SELECT 语句中输入字段名 — 在这个示例中为 Categories.CategoryID。
注意
欲创建只包含联接字段中数据相同的记录的查询,请用 INNER JOIN 运算。
- 在 INNER JOIN 之中可以写一个嵌套的 LEFT JOIN 或一个 RIGHT JOIN,但是在一个 LEFT JOIN 或一个 RIGHT JOIN 之中不能写嵌套的 INNER JOIN。请参阅 INNER JOIN 主题中有关使用嵌套的讨论,从其中可获知如何在其它联接中编写嵌套联接的信息。
- 可以链接多个 ON 子句。若需更多信息,请参阅在 INNER JOIN 主题中的子句链接的讨论。
若试图联接包含 Memo或 OLE Object数据的字段,会导致错误。
请参阅
示例
LEFT JOIN 和 RIGHT JOIN 运算示例
posted @
2006-09-03 11:52 surffish 阅读(238) |
评论 (0) |
编辑 收藏
关闭msn,在开始->运行中分别输入
regsvr32 softpub.dll
regsvr32 mssip32.dll
regsvr32 intipki.dll
regsvr32 MSXML3.dll
11221399B
再重新启动msn即可
posted @
2006-08-21 08:03 surffish 阅读(215) |
评论 (0) |
编辑 收藏
1. 在业务层使用JDBC直接操作数据库-最简单,最直接的操作
1)数据库url,username,password写死在代码中
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
String url="jdbc:oracle:thin:@localhost:1521:orcl";
String user="scott";
String password="tiger";
Connection conn= DriverManager.getConnection(url,user,password);
Statement stmt=conn.createStatement(
ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
String sql="select * from test";
ResultSet rs=stmt.executeQuery(sql);
2)采用Facade和Command模式,使用DBUtil类封装JDBC操作;
数据库url,username,password可以放在配置文件中(如xml,properties,ini等)。
这种方法在小程序中应用较多。
2.DAO(Data Accessor Object)模式-松耦合的开始
DAO = data + accessor + domain object
例如User类-domain object (javabean)
UserDAO类-accessor ,提供的方法getUser(int id),save(User user)内包含了JDBC操作
在业务逻辑中使用这两个类来完成数据操作。
使用Factory模式可以方便不同数据库连接之间的移植。
3.数据库资源管理模式
3.1 数据库连接池技术
资源重用,避免频繁创建,释放连接引起大大量性能开销;
更快的系统响应速度;
通过实现JDBC的部分资源对象接口( Connection, Statement, ResultSet ),可以使用Decorator设计模式分别产生三种逻辑资源对象: PooledConnection, PooledStatement和 PooledResultSet。
一个最简单地数据库连接池实现:
public class ConnectionPool {
private static Vector pools;
private final int POOL_MAXSIZE = 25;
/**
* 获取数据库连接
* 如果当前池中有可用连接,则将池中最后一个返回;若没有,则创建一个新的返回
*/
public synchronized Connection getConnection() {
Connection conn = null;
if (pools == null) {
pools = new Vector();
}
if (pools.isEmpty()) {
conn = createConnection();
} else {
int last_idx = pools.size() - 1;
conn = (Connection) pools.get(last_idx);
pools.remove(last_idx);
}
return conn;
}
/**
* 将使用完毕的数据库连接放回池中
* 若池中连接已经超过阈值,则关闭该连接;否则放回池中下次再使用
*/
public synchronized void releaseConnection(Connection conn) {
if (pools.size() >= POOL_MAXSIZE)
try {
conn.close();
} catch (SQLException e) {
// TODO自动生成 catch 块
e.printStackTrace();
} else
pools.add(conn);
}
public static Connection createConnection() {
Connection conn = null;
try {
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
String url = "jdbc:oracle:thin:@localhost:1521:orcl";
String user = "scott";
String password = "tiger";
conn = DriverManager.getConnection(url, user, password);
} catch (InstantiationException e) {
// TODO自动生成 catch 块
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO自动生成 catch 块
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO自动生成 catch 块
e.printStackTrace();
} catch (SQLException e) {
// TODO自动生成 catch 块
e.printStackTrace();
}
return conn;
}
}
注意:利用getConnection()方法得到的Connection,程序员很习惯地调用conn.close()方法关闭了数据库连接,那么上述的数据库连接机制便形同虚设。在调用conn.close()方法方法时如何调用releaseConnection()方法?这是关键。这里,我们使用Proxy模式和java反射机制。
public synchronized Connection getConnection() {
Connection conn = null;
if (pools == null) {
pools = new Vector();
}
if (pools.isEmpty()) {
conn = createConnection();
} else {
int last_idx = pools.size() - 1;
conn = (Connection) pools.get(last_idx);
pools.remove(last_idx);
}
ConnectionHandler handler=new ConnectionHandler(this);
return handler.bind(con);
}
public class ConnectionHandler implements InvocationHandler {
private Connection conn;
private ConnectionPool pool;
public ConnectionHandler(ConnectionPool pool){
this.pool=pool;
}
/**
* 将动态代理绑定到指定Connection
* @param conn
* @return
*/
public Connection bind(Connection conn){
this.conn=conn;
Connection proxyConn=(Connection)Proxy.newProxyInstance(
conn.getClass().getClassLoader(), conn.getClass().getInterfaces(),this);
return proxyConn;
}
/* (非 Javadoc)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO自动生成方法存根
Object obj=null;
if("close".equals(method.getName())){
this.pool.releaseConnection(this.conn);
}
else{
obj=method.invoke(this.conn, args);
}
return obj;
}
}
在实际项目中,并不需要你来从头开始来设计数据库连接池机制,现在成熟的开源项目,如C3P0,dbcp,Proxool等提供了良好的实现。一般推荐使用Apache dbcp,基本使用实例:
DataSource ds = null;
try{
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
ds = (DataSource)envCtx.lookup("jdbc/myoracle");
if(ds!=null){
out.println("Connection is OK!");
Connection cn=ds.getConnection();
if(cn!=null){
out.println("cn is Ok!");
Statement stmt = cn.createStatement();
ResultSet rst = stmt.executeQuery("select * from BOOK");
out.println("
rst is Ok!" + rst.next());
while(rst.next()){
out.println("
BOOK_CODE:" + rst.getString(1));
}
cn.close();
}else{
out.println("rst Fail!");
}
}
else
out.println("Fail!");
}catch(Exception ne){ out.println(ne);
}
3.2 Statement Pool
普通预编译代码:
String strSQL=”select name from items where id=?”;
PreparedStatement ps=conn.prepareStatement(strSQL);
ps.setString(1, “2”);
ResultSet rs=ps.executeQuery();
但是PreparedStatement 是与特定的Connection关联的,一旦Connection关闭,则相关的PreparedStatement 也会关闭。
为了创建PreparedStatement 缓冲池,可以在invoke方法中通过sql语句判断池中还有没有可用实例。
4. 持久层设计与O/R mapping 技术
1) Hernate:适合对新产品的开发,进行封闭化的设计
Hibernate 2003年被Jboss接管,通过把java pojo对象映射到数据库的table中,采用了xml/javareflection技术等。3.0提供了对存储过程和手写sql的支持,本身提供了hql语言。
开发所需要的文件:
hibernate配置文件: hibernate.cfg.xml 或 hibernate.properties
hibernate 映射文件: a.hbm.xml
pojo类源文件: a.java
导出表与表之间的关系:
a. 从java对象到hbm文件:xdoclet
b. 从hbm文件到java对象:hibernate extension
c. 从数据库到hbm文件:middlegen
d. 从hbm文件到数据库:SchemaExport
2)Iatis :适合对遗留系统的改造和对既有数据库的复用,有很强的灵活性 3) Apache OJB:优势在于对标准的全面支持 4)EJB:适合集群服务器,其性能也不象某些人所诟病的那么差劲 5) JDO (java data object)
设置一个Properties对象,从而获取一个JDO的PersistenceManagerFactory(相当于JDBC连接池中的DataSource),进而获得一个PersistenceManager对象(相当于JDBC中的Connection对象),之后,你可以用这个PersistenceManager对象来增加、更新、删除、查询对象。
JDOQL是JDO的查询语言;它有点象SQL,但却是依照Java的语法的。
5. 基于开源框架的Struts+Spring+Hibernate实现方案
示例:这是一个3层架构的web 程序,通过一个Action 来调用业务代理,再通过它来回调 DAO类。下面的流程图表示了MyUsers是如何工作的。数字表明了流程的先后顺序,从web层(UserAction)到中间层(UserManager),再到数据层(UserDAO),然后返回。
Spring是AOP, UserManager和UserDAO都是接口.
1) web层(UserAction) :调用中间层的接口方法,将UserManager作为属性注入。
采用流行的Struts框架,虽然有很多人不屑一顾,但是这项技术在业界用的比较普遍,能满足基本的功能,可以减少培训学习成本。
2) 中间层(UserManager):将UserDAO作为属性注入,其实现主要是调用数据层接口的一些方法;它处于事务控制中。
采用Spring框架实现,IOC与AOP是它的代名词,功能齐全,非常棒的一个架构。
3) 数据层(UserDAO):实现类继承HibernateDaoSupport类,在该类中可以调用getHibernateTemplate()的一些方法执行具体的数据操作。
采用Hibernate做O/R mapping,从种种迹象可以看出,Hibernate就是EJB3.0的beta版。 (转载文章请保留出处:Java家(www.javajia.com))
posted @
2006-04-18 11:05 surffish 阅读(258) |
评论 (0) |
编辑 收藏
【品名】民间俗称老婆,正式场合可称妻子或夫人;古称内人,现亦可叫爱人。
【成分】水、血液和脂肪类碳水化合物。
【性状】本品为细长条块状糖衣片,表面涂层一般为粉底、口红等化妆物,除去後呈浅黄色,外观与除去前略有差异;本品随时间推移,形状会有所改变,出现发胖、起皱等现象,但不影响继续使用。
【功能主治】主治单身恐惧症及传宗接代等顽疾,对失恋和相思病也有明显效果。
【用法用量】口服兼外用,一次限一片。对无效者,可暂时停用并向婚姻专家咨询,或间隔一段时间後重复使用。
【注意事项】本品仅适用于单身之成年男性。服用时 小心谨慎,细心观察有无不良反应,如有必须马上停止服用,没有则可继续使用。若忌烟酒等不良习惯,会使疗效更佳。
【规格】通常为45千克至55千克,特殊情况下亦有例外。
【贮藏】常温下妥善保存,室内阴凉处最佳;如在室外,则 避免强烈阳光直射。 贮藏期间,尤忌随意弃置一旁不管
posted @
2006-03-23 12:00 surffish 阅读(266) |
评论 (0) |
编辑 收藏
用java写服务端代码
import java.io.*;
import java.net.*;
public class test {
public test() {
}
public static final int PORT = 8080;
public static final String ip = "10.194.111.222";
public static void main(String[] args) throws IOException {
ServerSocket s = new ServerSocket(51,2,InetAddress.getByName(ip));
System.out.println("Started: " + s);
try {
// Blocks until a connection occurs:
Socket socket = s.accept();
try {
System.out.println(
"Connection accepted: "+ socket);
BufferedReader in =
new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
// Output is automatically flushed
// by PrintWriter:
PrintWriter out =
new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream())),true);
while (true) {
String str = in.readLine();
if (str.equals("END")) break;
System.out.println("Echoing: " + str);
out.println(str);
}
// Always close the two sockets...
} finally {
System.out.println("closing...");
socket.close();
}
} finally {
s.close();
}
}
}
.net写客户端代码
private void button2_Click(object sender, System.EventArgs e)
{
try
{
stSend = new Socket ( AddressFamily.InterNetwork ,
SocketType.Stream , ProtocolType.Tcp ) ;
//初始化一个Socket实例
IPEndPoint tempRemoteIP = new IPEndPoint(IPAddress.Parse("10.194.111.222"),51);
//根据IP地址和端口号创建远程终结点
EndPoint epTemp = ( EndPoint ) tempRemoteIP;
stSend.Connect ( epTemp ) ;
}
catch ( Exception err)
{
string s = err.ToString();
}
}
posted @
2005-12-09 16:36 surffish 阅读(1178) |
评论 (0) |
编辑 收藏
1.工作区: (显隐)
项目面板:ctrl + Alt + p (Project)
设计面板: ctrl + Alt + c (content)
结构面板: ctrl + Alt + s (Structure)
信息面板: ctrl + Alt + M (Message)
状态面板: ctrl + Alt + Z
2.主面板:(代码面板和设计面板)
激活代码模块: ctrl + J (@1)
参数提示信息的激活: ctrl + shift + H
打开查询、替换窗口: ctrl + F
类的查询: ctrl + -
3.F 键的用法
F1: 帮助快捷
F4: 运行多行
F5: 加入断点
F7: 当遇到方法时会运行方法内的代码
F8: 逐步运行代码
F12: 代码面板和设计面板切换
4. Shift 键的用法
添加多个相同组件: 按shift键在选项上选取组件,把组件添加到窗口即可
调整组件间间隔和对齐: 假设有组件JPanel 1/2/3;(要达到3个组件宽度相同,组件间隔相等,并且都是依据JPanel1左对齐),按shift键,用鼠标选中需要调整的组件,(第一个选中的组件是其他的基准)然后右键。
5: codeInsight 和 Codetemplates
MemberInsight -- 根据当前的代码提示可用的类成员或方法(说明)
ParameterInsight -- 提示当前的方法需要使用的参数
SymbolInsigh -- 查看当前的变量、方法或者类的愿代码。
MemberInsight: ctrl + space 或 ctrl + H
ParameterInsight: ctrl + shift + space 或 ctrl + shift + H
SymbolInsight: ctrl + Enter 或 Alt + shift + H
ClassInsight : ctrl + alt + space 或 ctrl + alt + H
注: (@1):使用代码功能:(ctrl + J)
1、 在想要输入代码的位置输入代码摸板名,然后按 ctrl + J(开发人员可以用主菜单上的Tools/Editor/Templates命令来查看代码摸板的名字)
2、把光标定位于想要输入代码的位置,然后按ctrl + J
posted @
2005-12-09 11:40 surffish 阅读(289) |
评论 (0) |
编辑 收藏
http://blog.csdn.net/javamxj/archive/2004/10/11/131935.aspx
我是一名java的爱好者,理所当然装了不少java方面的软件,大部分是开放源码的,而且多数是绿色软件,只要解压,设置一下环境变量即可使用。
由于软件本身升级比较频繁,经常需要重新设置使用的环境变量,而且我常常同时安装同一软件的不同版本(甚至是相同版本)。如eclipse我就分别在F,G,H三个盘上装了不同的版本,一个是中文版,一个是英文的Latest Release,一个是Stream Stable,反正是绿色安装,也不会发生冲突,这样通过对比便于了解和测试最新版本的先进之处。
但是假如把JDK版本从1.3升级到1.4,即JDK目录名可能要从“j2sdk1.3”改为“j2sdk1.4”,如果是这样的话,那么eclipse可能会无法再启动了(它要从环境变量中寻找JAVA_HOME变量,而JAVA_HOME变量值已经从“j2sdk1.3”改为“j2sdk1.4”了)。
在谈谈jakarta-tomcat,这个软件升级比较频繁,如果你是结合eclipse来使用Tomcat的,那么每次Tomcat升级,可能都要在eclipse中重新设置一下Tomcat的安装目录,是不是有些麻烦?
对于此类问题,解决的方法很简单,只要把默认的软件安装目录名去掉版本号即可(如果担心忘记版本号,只要在目录中添加一个readme文件加以说明即可)。
如上图,我的j2sdk,ant,jakata-tomcat等都没有版本号。
同样,在环境变量设置中也没有出现版本号。
这样,如果再需要升级软件时,仅仅把新版软件安装在旧版软件目录即可。其它一般都不需要再改动了。
另外,在环境变量设置中,可以用%变量%代替变量值,如Path变量值可以这样设置: %JAVA_HOME%\bin;%JBOSS_HOME%\bin;%ANT_HOME%\bin;
posted @
2005-12-07 16:31 surffish 阅读(473) |
评论 (0) |
编辑 收藏
我们要开发一个java类:其内容只有一句,输出"hello ant"字符串。并使用ant完成编译和运行工作,这个例子只是为了跑通ant,不附加多余的东西。
下图为文件组织,请建立相应的目录,并编写HelloAnt.java
按照人家老外的文件组织规则咱也照搬。
在项目根目录(hello-ant\)写1个文件:ant执行配置文件build.xml
ok,一切大功告成,哦,不,还没有运行它。
dos下进入hello-ant的目录,即build.xml所在的目录,我们要用ant工具执行它 ,
执行: %ant_home%/bin/ant -file build.xml 用ant工具执行当前目录下的配置文件build.xml
或 :ant -file build.xml 你如果设置%ant_home%/bin到path中
这次ok了,这是答案:
命令提示符窗口 |
D:\temp\hello-ant>ant -file build.xml Buildfile: build.xml
main: [javac] Compiling 1 source file to D:\temp\hello-ant\build\classes [java] hello ant,ant 的第一次接触,好棒!
BUILD SUCCESSFUL Total time: 2 seconds D:\temp\hello-ant> |
检查一下build/classes目录,哦,看到编译过的文件就在这里:
build/classes/hello/ant/HelloAnt.class.
hello ant 进级
(此段比较废话,可以略过)
你也许会说:这末简单的工作写个批处理不就得了,又xml又ant的,把我的时间都浪费完了,我用jbuild或
webShpere不就得了,怎末说你才明白呢?反正网上开源项目大多数都用ant,你总不能给人家个*.jpx吧,
而且这样的工具太贵,受不了(当然用D的兄弟不怕^_^ ),而且ant可以让你明确的管理和自动化所有的东西:
编译-实施-测试...,哎,稍微麻烦一点点,但节约你以前花在零碎的copy,paste上的时间.而且我发现管理
代码的质量有所提高.
我们要改进build.xml,让它做更多的事情:
- 定义全局变量
- 初始化,主要是建立目录
- 编译 (已有)
- 打包为jar
- 建立API documentation
- 生成distribution产品
凡事都讲究平衡,你要ant给你做更多事,当然要累一点点,不过只用累一次,以后的代码修改后的构建都是"一键式"完成,我们制作一个hello的简单例子,你可以自己做j2ee的练习。
我们要扩充目录结构,使它更像回事:
ant处理编译之前的目录:
ant处理之后的目录:
图中:\src,\docs,\lib是自己组织的文件结构,\build,\dist是ant动态生成的成品。
\src 源文件:java源,script源,jsp源,xml配置.....
\src\main java源
\src\script window,unix,liunx的执行script,我们的简单只有一个:
run.bat: java hello.ant.HelloAnt
\docs 手写说明文档
\lib 程序所需类库的jar,比如j2ee.jar,mail,jar...
\build 用ant动态生成的构建目录
\build\classes 编译的类文件
\build\docs copy "\docs"的手写说明文档,和ant生成的api文档
\build\lib 放置我们自己的HelloAnt.class打包成品hello-ant.jar
\dist\bin copy "\src\script" 得执行文件
\dist\docs copy "\build\docs" 的文档
\dist\lib 除了copy "\build\lib"下的hello-ant.jar外,
还应copy "\lib"的程序所需jar,这里我们没有。
以上是我学老外的文件组织,大家可以按照自己的爱好组织
我们编写必要的文件:
hello.ant. HelloAnt.java |
已有 |
\docs\index.html 随便写一个手写的文档 |
hello ant 软件项目手册docs
访问api文档 |
build.xml多了些,但其实很简单:(注释比较详细可以参照,这里再简单说一下)
一个build.xml包含一个工程的自动化处理的完整xml说明,并且基本由3种东东组成:
<project >
1.全局变量的定义
<property/>
2.任务组
<target>
3.许多单项任务... 像copy,delete,javac,jar...
<task1/>
<task2/>
<task3/>
</target>
</project>
参考及下载:
本文程序:第1个hello-ant
本文程序:第2个进阶的hello-ant
ant最新下载:
http://jakarta.apache.org/ant/index.html
ant具体的编写方法参考ant手册以下2部分就形,
http://jakarta.apache.org/ant/manual/using 使用说明
http://jakarta.apache.org/ant/manual/coretasklist.html 核心tasks
其他一大堆东西你要看也行。不过我觉得比较浪费时间。
http://jakarta.apache.org/ant/manual/index.html 手册index
huihoo.com翻译改编的ant/manual/using
http://www.huihoo.com/java/ant.html
用ANT构造Application作者:余斌斌
http://developer.ccidnet.com/pub/disp/Article?columnID=295&articleID=27619&pageNO=1
ibm 利用 Ant 和 JUnit 进行增量开发——使用单元测试来逐步改进代码
http://www-900.ibm.com/developerWorks/cn/java/j-ant/index.shtml
posted @
2005-12-07 14:33 surffish 阅读(571) |
评论 (1) |
编辑 收藏
本文以最新发布的Ant 1.5.1为例,介绍这款优秀的Build工具的安装配置、基本应用和一些高级话题。最新的Ant下载地址是
http://jakarta.apache.org/ant/ 。
Ant是一种基于Java的Build工具。理论上来说,它有些类似于C中的make,但比make优越。现在存在的大多数Build工具,如make、gnumake、nmake、jam等都存在这样或那样的不足,比如依赖于特定的平台、配置文件过于复杂或者对格式无法检查而容易出错等。与这些工具相比较,Ant的两个特性决定了它是一款优秀的Build工具:
1. 基于Java的实现。具有良好的跨平台性,同时可以通过增加新的Java类来扩展Ant的功能,而无需去了解不同平台上不同的脚本语言。
2.基于XML的配置文件。Ant以XML树来描述Target/Task的关系,文件结构清晰、易读易写,并且利用XML对格式的控制来避免由于配置文件的错误造成的Build操作失败。
安装与配置
Ant的安装非常简单,把从网上下载的jakarta-ant-1.5.1-bin.zip解开到一个目录下即可(以下假定安装在目录D:\jakarta-ant-1.5.1)。接下来需要进行环境变量配置:
SET ANT_HOME=D:\jakarta-ant-1.5.1 //注意是Ant的安装目录,不是bin子目录 SET PATH=%PATH%;%ANT_HOME%\bin; |
在配置环境变量之前,请确认已经正确设置了JAVA_HOME系统变量。输入ant命令,看到如下输出说明已成功安装了Ant工具:
Buildfile: build.xml does not exist! Build failed |
提示信息表明在当前目录不存在build.xml配置文件,但这本身已经说明Ant成功运行了。
快速入门
下面用一个最简单也是最经典的例子-HelloWorld来感受一下Ant吧。
//HelloWorld.java package com.sharetop.antdemo; public class HelloWorld { public static void main( String args[] ) { System.out.println("Hello world. "); } } |
要让Ant编译这个文件,首先需要编写一个Build配置文件。在一般情况下,这个文件被命名为build.xml。
<?xml version="1.0" encoding="UTF-8" ?> <project name="HelloWorld" default="run" basedir="." > <property name="src" value="src"/> <property name="dest" value="classes"/> <property name="hello_jar" value="hello.jar" /> <target name="init"> <mkdir dir="${dest}"/> </target> <target name="compile" depends="init"> <javac srcdir="${src}" destdir="${dest}"/> </target> <target name="build" depends="compile"> <jar jarfile="${hello_jar}" basedir="${dest}"/> </target> <target name="run" depends="build"> <java classname="com.sharetop.antdemo.HelloWorld" classpath="${hello_jar}"/> </target> </project> |
来看一下这个文件的内容,它描述了以下信息:工程的名字为HelloWorld,工程有四个target,分别是init、compil、build和run,缺省是run。compile只有一个任务javac,源文件位于src目录下,输出的类文件要放在classes目录下。build的任务是jar,生成的jar文件为hello.jar,它打包时以classes为根目录。而run则是执行这个HelloWorld类,用hello.jar作为classpath。这四个target之间有一个依赖关系,这种关系用depends来指定。即如果Target A依赖于Target B,那么在执行Target A之前会首先执行Target B。所以从下面运行缺省Target(run)的输出看,这四个Target的执行顺序是:init→compile→build→run。文件目录结构如图1所示。HelloWorld.java文件在src\com\sharetop\antdemo子目录下。
图1 ant_demo应用的目录结构
在命令行输入命令:ant,然后运行,可以看到如下输出:
如果配置文件名不是build.xml,比如是build_front.xml,那么,可以使用-buildfile命令参数指定:
G:\myDoc\ant_demo>ant -buildfile build_front.xml |
也可以单独执行指定的某个target,比如,只编译不打包执行,可以使用下面输入命令即可:
G:\myDoc\ant_demo>ant compile |
在相应的目录下会找到编译出的HelloWorld.class文件。
再看看上面的build.xml配置文件,文件开头定义了3个属性,分别指定了源文件输出路径、类文件输出路径和生成的Jar文件名,后面对这些路径的引用都通过一个${property name}来引用。所以,要注意这样一个原则“目录的定义与目录的引用应该分开”。
基本应用
建立工程的目录 一般要根据工程的实际情况来建立工程的目录结构。但是,有一些比较通用的组织形式可供参考,比如所有的jakarta项目都使用类似的目录结构。下面让我们来看一下这种目录结构的特点。
表1
目录 |
文件 |
bin |
公共的二进制文件,以及运行脚本 |
build |
临时创建的文件,如类文件等 |
dist |
目标输出文件,如生成Jar文件等。 |
doc/javadocs |
文档。 |
lib |
需要导出的Java包 |
src |
源文件 |
对于一个简单的工程,一般包括表1的几个目录。其中bin、lib、doc和src目录需要在CVS的控制之下。当然在这样的目录结构上,也可以做一些调整,例如,可以建立一个extra目录来放置需要发布的Jar文件、Inf文件及图像文件等。同样,如果开发Web应用可以建立一个Web目录放置JSP、HTML等文件。
如果我们开发的是一个比较复杂的项目,包括多个子项目,并且各个子项目是由不同的开发人员来完成的,那么要如何来设计它的目录结构?首先有一点是需要确定的,不同的子项目应该拥有不同的Build文件,并且整个项目也应该有一个总的Build文件。可以通过Ant任务或是AntCall任务调用子项目的Build文件,如下例:
<target name="core" depends="init"> <ant dir="components" target="core"/> <ant dir="waf/src" target="core"/> <ant dir="apps" target="core"/> </target> |
在各个子项目的耦合不是非常紧密的情况下,各个子项目应该有各自独立的目录结构,也就是说它们可以有自己的src、doc、build、dist等目录及自己的build.xml文件,但是可以共享lib和bin目录。而对于那些耦合紧密的子项目,则推荐使用同一个src目录,但是不同的子项目有不同的子目录,各个子项目的build.xml文件可以放在根目录下,也可以移到各个子项目的目录下。
编写Build文件 要用好Ant工具,关键是要编写一个build.xml文件。要编写出一个结构良好、灵活可扩展的Build文件,有两个问题要考虑,一是了解Build文件的基本结构,二是了解Ant定义的大量任务。
Ant的Build文件是一个标准的XML文件,它包含一个根节点Project,每个Project定义了至少一个或多个Target,每个Target又是一系列Task的集合。它们之间的关系如图2所示。
图2 build.xml文件的结构
每个Task是一段可被执行的代码,比如,前例中的javac、jar就是两个最常用的Task。Ant定义了大量的核心Task,我们要考虑的第二个问题正是如何去掌握这大量的Task。其实唯一的方法就是边学习边实践,这方面最好的参考就是官方的Ant使用手册。
外部文件的使用 使用外部的Property文件可以保存一些预设置的公共属性变量。这些属性可以在多个不同的Build文件中使用。
可以将一个外部的XML文件导入Build文件中,这样多个项目的开发者可以通过引用来共享一些代码,同样,这也有助于Build文件的重用,示例代码如下所示:
<!DOCTYPE project [ <!ENTITY share-variable SYSTEM "file:../share-variable.xml"> <!ENTITY build-share SYSTEM "file:../build-share.xml"> ]> <project name="main" default="complie" basedir="."> &share-variable; &build-share; ... ... |
在J2EE项目中的应用
只要掌握了Ant的使用方法,在J2EE项目中的应用与在其它项目中的应用并没有太大的不同,但是仍有几点是需要注意的。
一是要清楚War和Jar文件的目录结构,主要是War的配置文件web.xml文件的位置和EJB的配置文件(ejb-jar.xml和weblogic-ejb-jar.xml等)的位置,在调用Jar任务打包文件时一定要记得把它们也包含进来。一般在编译之前就要注意把这些需打包的文件拷入相应目录下。二是在J2EE项目中可能会涉及到一些特殊的任务,比如在Weblogic中会调用ejbc预编译EJB的代码存根,或者需要在Ant中同时发布Jar到相应的服务器中等。可以用两种途径实现这些任务,一是扩展Ant任务实现这些任务,二是直接用Java任务来执行这些命令。下面是打包、发布一个EJB的build.xml配置文件片断,代码如下:
<target name="deploy_HelloEJB" depends="compile"> <delete dir="${temp}/ejb_make"/> <!-- 首先删除临时目录 --> <delete file="${temp}/helloEJB.jar"/> <!-- 删除WebLogic域中老版本的EJB --> <delete file="${weblogic.deploy.dest}/helloEJB.jar"/> <!-- 创建META-INF目录,放置ejb-jar.xml和weblogic-ejb-jar.xml --> <mkdir dir="${temp}/ejb_make/META-INF"/> <!-- 拷贝ejb-jar.xml和weblogic-ejb-jar.xml 到临时目录--> <copy todir="${temp}/ejb_make/META-INF"> <fileset dir="etc/baseinfo"> <include name="*.xml"/> </fileset> </copy> <!-- 拷贝所有的helloEJB类到临时目录 --> <copy todir="${temp}/ejb_make/"> <fileset dir="${dest.classes}/"> <!-- dest.classes是输出的类文件目录 --> <include name="${dest.classes}/helloEJB/**"/> </fileset> </copy> <!-- 将所有这些文件打包成helloEJB.jar --> <jar jarfile="${temp}/helloEJB.jar" basedir="${temp}/ejb_make"/> <!-- 进行weblogic.ejbc编译 --> <java classpath="${wl_cp}" classname="weblogic.ejbc" fork="yes" > <classpath> <fileset dir="lib"> <include name="*.jar" /> </fileset> </classpath> <arg value="${temp}/helloEJB.jar" /> <arg value="${temp}/helloEJB_deploy.jar" /> </java> <!-- 拷贝/发布到WebLogic的{DOMAIN}\applications目录 --> <copy file="${temp}/helloEJB_deploy.jar" todir="${weblogic.deploy.dest}"/> </target> |
用Ant配合JUnit实现单元测试
Ant 提供了JUnit任务,可以执行单元测试代码。如何使用JUnit,以及如何编写测试用例(TestCase),感兴趣的读者可以参阅JUnit的相关文档。在Ant中使用JUnit的方法非常简单,首先需要把junit.jar拷入ANT_HOME\lib下,确认在这个目录下有optional.jar,因为JUnit是Ant的扩展任务,需要引用这个扩展包。然后就是在Build文件中加入JUnit的任务,代码如下:
<target name="run" depends="client"> <junit printsummary="yes" fork="yes" haltonfailure="yes"> <classpath> <pathelement location="client.jar" /> </classpath> <formatter type="plain" /> <test name="com.sharetop.antdemo.HelloWorldTest" /> </junit> </target> |
高级话题
为Ant开发扩展任务 为Ant实现扩展任务其实是非常容易的,只需按照以下几个步骤即可:
1. 创建一个Java类继承org.apache.tools.ant.Task类;
2. 对每个属性实现set方法。Ant会根据需要自动完成类型转换;
3. 如果扩展的任务需要嵌套其它的Task,那么这个Java类必需实现接口org.apache.tools.ant.TaskContainer;
4. 如果扩展的任务要支持Text,需要增加一个方法void addText(String);
5. 对每个嵌套的元素,实现create、add 或 addConfigured 方法;
6. 实现public void execute方法;
7. 在build.xml文件中使用 <taskdef> 来引用自定义的Task。
下面以一个简单的例子来说明如何为Ant增加一个hello任务,它可以连续打印多条信息,打印的次数由属性count指定,而打印的内容则由它内嵌的一个helloinfo任务的message属性指定,看上去这非常类似JSP中自定义标签的一些概念,实现代码如下:
//HelloInfoTask.java package com.sharetop.antdemo; import org.apache.tools.ant.*; public class HelloInfoTask { private String msg; public void execute() throws BuildException { System.out.println(msg); } public void setMessage(String msg) { this.msg = msg; } } |
下面是外部Task类的代码:
//HelloTask.java package com.sharetop.antdemo; import org.apache.tools.ant.*; public class HelloTask extends Task implements org.apache.tools.ant.TaskContainer { private Task info; private int count; public void execute() throws BuildException { for(int i=0;i<count;i++) info.execute(); } public void setCount(int c){ this.count=c; } public void addTask(Task t){ this.info=t; } } |
实现了这两个Task,在build.xml文件中定义它的task name,就可以在Target中执行它了。如果你不想使用 <taskdef> 标签来定义Task,也可以通过修改default.properties文件来实现引入新Task,这个文件位于org.apache.tools.ant.taskdefs 包里。下例是一个使用 标签来引入新Task的Build文件部分:
<target name="hello" depends="client"> <taskdef name="hello" classname="com.sharetop.antdemo.HelloTask" classpath="client.jar"/> <taskdef name="helloinfo" classname="com.sharetop.antdemo.HelloInfoTask" classpath="client.jar"/> <hello count="3" > <helloinfo message="hello world" /> </hello> </target> |
在自己的程序中调用Ant
Ant的任务其实就是一段功能代码。Ant内置的大量任务对于我们开发Java应用具有非常大的意义,为什么我们不能直接使用呢?
因为尽管在程序中调用Ant的任务并不复杂,而且我们知道Ant的任务其实都是一些Java类,调用的方法无非就是执行这些类而已,不过在执行之前需要对它做一些初始化的工作,所以我们需要引用一个Task类的子类来实现这个功能,比如如下代码:
package com.sharetop.antdemo; import org.apache.tools.ant.*; import org.apache.tools.ant.taskdefs.*; import java.io.File; public class RunAntTask { public RunAntTask() { } public static void main(String args[]){ AntJAR j = new AntJAR(); j.setBasedir(new File("./classes")); j.setJarfile(new File("aaa.jar")); j.execute(); } } final class AntJAR extends Jar { public AntJAR() { project = new Project(); project.init(); taskType = "jar"; taskName = "jar"; } } |
注意AntJAR类的构造方法,先创建了Project并初始化它,这是直接调用Task的必需条件。
如果要在自己的程序中执行Ant,需要了解的是Ant定义的几个BuildEvent,它包括:
◆ Build started
◆ Build finished
◆ Target started
◆ Target finished
◆ Task started
◆ Task finished
◆ Message logged
我们需要做的是实现BuildListener接口来处理各种事件,而执行Ant的方法与上面给的例子非常类似,以实际开发的AntBuilder软件的部分代码为例:
public void buildTarget(String targetName,String buildFileName) { try { Project p = new Project(); p.init(); File f = new File(buildFileName); p.setUserProperty("ant.file",f.getAbsolutePath()); ProjectHelper.configureProject(p,f); p.addBuildListener(this); if( targetName==null ) p.executeTarget(p.getDefaultTarget()); else p.executeTarget(targetName); } catch (Exception ex) { jTextArea1.append(ex.getMessage()); } } |
创建Project并初始化,设置它的配置文件(build.xml),执行它缺省的或指定的Target,然后在实现了BuildListenser接口的监听器类中对你感兴趣的事件作处理,代码如下:
public void buildStarted(BuildEvent event){ /* nothing*/ } public void buildFinished(BuildEvent event) { /* nothing*/ } public void targetStarted(BuildEvent event) { this.jTextArea1.append(event.getTarget().getName()+": \n\r"); } public void targetFinished(BuildEvent event) {/* nothing*/ } public void taskStarted(BuildEvent event) {/* nothing*/ } public void taskFinished(BuildEvent event) { /* nothing*/ } public void messageLogged(BuildEvent event) { int prior = event.getPriority(); switch(prior){ case Project.MSG_ERR : this.jTextArea1.append("["+event.getTask().getTaskName()+"]Err:" +event.getMessage()); break; case Project.MSG_INFO: this.jTextArea1.append("["+event.getTask().getTaskName()+"]"+event.getMessage ()); break; case Project.MSG_WARN: this.jTextArea1.append("["+event.getTask().getTaskName()+"]" +event.getMessage()); break; case Project.MSG_VERBOSE: this.jTextArea1.append(event.getMessage()); break; } } |
Build.xml文件的写法每个公司都有不同,这里没有太大的参考价值,所以略去。(全文完)
posted @
2005-12-07 14:29 surffish 阅读(1645) |
评论 (0) |
编辑 收藏
//服务器代码
/********************************Main******************************/
import java.io.*;
import java.net.*;
import java.util.*;
public class ServerMain{
public static Vector socketVector=new Vector();
public static void main(String[] args) throws IOException{
System.out.println("服务器启动........");
ServerSocket s = new ServerSocket(5000);
while(true){
Socket soc = s.accept();
SocketThread st=new SocketThread(soc);
socketVector.addElement(st);
st.start();
}
}
public static void sendEveryone(String msg){
Object object=null;
int len=socketVector.size();
for(int i=0;i<len;i++){
try {
object=socketVector.elementAt(i);
SocketThread st = (SocketThread)object;
st.sender.send(msg);
}
catch (Exception ex) {
socketVector.removeElement(object);
}
}
}
public static void removeObject(Object object){
socketVector.removeElement(object);
}
public static void removeObject(Sender sender) throws Exception{
int len=socketVector.size();
for(int i=0;i<len;i++){
Object object=socketVector.elementAt(i);
SocketThread st = (SocketThread)object;
if(st.sender==sender)
socketVector.removeElement(object);
}
}
}
/*********************************SocketThread **********************/
import java.io.*;
import java.net.*;
import java.util.*;
public class SocketThread implements Runnable{
public Socket socke;
public DataInputStream dis;
public DataOutputStream dos;
public Sender sender;
private boolean stop;
Calendar date;// = Calendar.getInstance(TimeZone.getTimeZone("Asia/ShangHai"));
public SocketThread(Socket sok){
socke=sok;
}
public void start(){
Thread t=new Thread(this);
t.start();
}
public void run() {
try {
socke.setKeepAlive(true);
dis = new DataInputStream(socke.getInputStream());
dos = new DataOutputStream(socke.getOutputStream());
sender = new Sender(dos);
while (true) {
StringBuffer sb = new StringBuffer();
char c;
while (((c = dis.readChar()) != '\n') && (c != -1)) {
sb.append(c);
}
if (c == -1) {
break;
}
date = Calendar.getInstance(TimeZone.getTimeZone("Asia/ShangHai"));
String ljnTime="("+date.get(date.YEAR)+"/"+(date.get(date.MONTH)+1)+"/"+date.get(date.DATE)+" "+date.get(date.HOUR_OF_DAY)+":"+date.get(date.MINUTE)+":"+date.get(date.SECOND)+")";
String acceptMsg=sb.toString();
System.out.println(ljnTime+acceptMsg);
ServerMain.sendEveryone(acceptMsg);
}
stop();
ServerMain.removeObject(this);
} catch (IOException ioe) {
} catch (Exception e) {
e.printStackTrace();
}
}
public void stop() {
try {
stop = true;
if(sender!=null){
sender.stop1();
}
if (dis != null) {
dis.close();
}
if (dos != null) {
dos.close();
}
if (socke != null) {
socke.close();
}
} catch (IOException ioe) {
}
}
}
/*********************************************Sender**************************/
import java.io.*;
public class Sender extends Thread {
private DataOutputStream dos;
private String message;
public Sender(DataOutputStream os) {
this.dos = os;
start();
}
public synchronized void send(String msg) {
message = msg;
notify();
}
public synchronized void run() {
while (true) {
if (message == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
if (message == null) {
break;
}
try {
dos.writeChars(message);
dos.writeChars("\r\n");
} catch (IOException ioe) {
try {
ServerMain.removeObject(this);
}
catch (Exception ex) {
}
}
message = null;
}
}
public synchronized void stop1() {
message = null;
notify();
}
}
//下面为手机客户端代码
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class ChatClientMIDlet extends MIDlet {
private static ChatClientMIDlet instance;
public static ChatForm displayable = new ChatForm();
/** Constructor */
public ChatClientMIDlet() {
instance = this;
}
/** Main method */
public void startApp() {
Display.getDisplay(this).setCurrent(displayable);
}
/** Handle pausing the MIDlet */
public void pauseApp() {
}
/** Handle destroying the MIDlet */
public void destroyApp(boolean unconditional) {
}
public static void setCurrent(Displayable s){
Display.getDisplay(instance).setCurrent(s);
}
/** Quit the MIDlet */
public static void quitApp() {
instance.destroyApp(true);
instance.notifyDestroyed();
instance = null;
}
}
/********************************************************ChatForm ************************/
import com.nec.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import java.io.*;
public class ChatForm extends Form implements CommandListener,Runnable {
public Form mainForm=new Form("聊天室");
public Command enter=new Command("进入",Command.OK,0);
public Command send=new Command("发送",Command.OK,1);
public Command exit=new Command("退出",Command.EXIT,0);
public DataInputStream dis;
public DataOutputStream dos;
public SocketConnection sc;
public Sender sender;
public boolean stop;
public TextField textField=new TextField("请输入昵称:", null, 10, TextField.ANY);
public TextField info=new TextField("请输入消息:", null,255, TextField.ANY);
public ChoiceGroup choiceGroup=new ChoiceGroup(null,ChoiceGroup.EXCLUSIVE);
public String MyName="游客";
public boolean isCurrent=false;;
public ChatForm() {
super("我的聊天室");
append(textField);
addCommand(enter);
mainForm.append(info);
mainForm.append(choiceGroup);
choiceGroup.append("www.modia.cn",null);
setCommandListener(this);
mainForm.addCommand(send);
mainForm.addCommand(exit);
mainForm.setCommandListener(this);
}
public void commandAction(Command c, Displayable dis) {
if(c==enter){
if(textField.getString().length()==0){
Alert alert=new Alert("警告","昵称不能为空!", null, AlertType.WARNING) ;
alert.setTimeout(3000);
ChatClientMIDlet.setCurrent(alert);
return;
}else
{
MyName = textField.getString();
append("正在进入......");
}
Thread t=new Thread(this);
t.start();
}else if(c==send){
if(info.getString().length()==0){
Alert alert=new Alert("警告","消息内容不能为空!", null, AlertType.WARNING) ;
alert.setTimeout(3000);
ChatClientMIDlet.setCurrent(alert);
return;
}
sender.send(MyName+"说:"+info.getString());
info.setString("");
}
else{
stop();
ChatClientMIDlet.quitApp();
}
}
public void run() {
try {
sc = (SocketConnection) Connector.open("socket://127.0.0.1:5000");
sc.setSocketOption(SocketConnection.LINGER, 5);
dis = new DataInputStream(sc.openInputStream());
dos = new DataOutputStream(sc.openOutputStream());
sender = new Sender(dos);
sender.send("欢迎"+MyName+"进入房间");
while (true) {
if(stop) break;
StringBuffer sb = new StringBuffer();
char c;
while (((c = dis.readChar()) != '\n') && (c != -1)) {
sb.append(c);
}
if (c == -1) break;
if(!isCurrent){
ChatClientMIDlet.setCurrent(mainForm);
isCurrent=true;
}
String msg=sb.toString();
msg=msg.substring(0,msg.length()-2);
choiceGroup.insert(0,msg,null);
choiceGroup.setSelectedIndex(0,true);
}
stop();
} catch (ConnectionNotFoundException cnfe) {
} catch (IOException ioe) {
if (!stop) {
ioe.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void stop() {
try {
stop = true;
if (sender != null) {
sender.stop1();
}
if (dis != null) {
dis.close();
}
if (dos != null) {
dos.close();
}
if (sc != null) {
sc.close();
}
} catch (IOException ioe) {
}
}
}
/**************************Sender*********************************/
同上
posted @
2005-11-22 12:54 surffish 阅读(1070) |
评论 (2) |
编辑 收藏
//怎样获取一个文件夹下中的所有文件File dir = new File("fileDirectory");
String[] filel= dir.list();
//oracle启动方法
--启动数据库
su - oracle
sqlplus /nolog
connect /as sysdba
startup
exit
--监听
lsnrctl
start
posted @
2005-11-21 09:57 surffish 阅读(226) |
评论 (0) |
编辑 收藏
telnet 10.194.132.1
ftp 192.168.10.1
ls
vi
i
esc
:wq
ctrl + c
cp
posted @
2005-11-10 15:23 surffish 阅读(283) |
评论 (0) |
编辑 收藏
在Weblogic 8.1 sp1 上调试《精通EJB 2》源代码
----连接池及数据源ejbPool的配置.
作者: naxin9999@163.com
--------------------------------------------------------------------------------
一. 首先在mysql数据库中新建一个me2库,再建一个sa用户(密码:222222).授予权限.再提交源代码中的EJB.sql文件 ( 在EJB.sql文件头加上use me2; )。
具体如下:
d:\mysql –u root –p
mysql> create database me2;
mysql> GRANT ALL PRIVILEGES ON me2.* TO sa@'localhost' IDENTIFIED BY '222222' WITH GRANT OPTION;
mysql> quit
d:\ mysql –u sa –p < EJB.sql
password: (输入222222)
完成.
二. 下面开始创建DS:
a. 把mysql的驱动程序mm.mysql-2.0.14-bin.jar复制到 D:\bea\weblogic81\server\lib 目录下.
b. 修改D:\bea\user_projects\domains\mydomain 目录下的startweblogic.cmd文件中的 CLASSPATH设置,加上%WL_HOME%\server\lib\mm.mysql-2.0.14-bin.jar;
c. 起动weblogic,登陆 http://localhost:7001/console
--------------------------------------------------------------------------------
1.先建个pool (下图)
--------------------------------------------------------------------------------
2. (下图)
--------------------------------------------------------------------------------
3. (下图)
--------------------------------------------------------------------------------
4. (下图)
--------------------------------------------------------------------------------
5. (下图)
--------------------------------------------------------------------------------
6. (下图)
--------------------------------------------------------------------------------
7. 再建JDBC DS (下图)
--------------------------------------------------------------------------------
8. (下图)
--------------------------------------------------------------------------------
9. (下图)
--------------------------------------------------------------------------------
10(下图)
--------------------------------------------------------------------------------
11. (下图)
--------------------------------------------------------------------------------
12. 部署你的EJB,可以进行测试了(下图)
--------------------------------------------------------------------------------
13. 运行cmp 2.0的例子. Client输出:
14. Console 上输出
1. 在http://localhost:7001/console 找到 Configure a new JMS Topic..
2.如下图输入:
3.Create成功后.
4.部署你的消息驱动Bean
5.运行客户端,run_client.bat
6.weblogic控制台上的输出.
posted @
2005-10-31 15:35 surffish 阅读(269) |
评论 (0) |
编辑 收藏
/*$T BYBARP~1.H GC 1.137 09/13/05 08:37:52 */
#ifndef ByBarPrinterH
#define ByBarPrinterH
#include "vcl.h"
#define BPLA_OK 1000 //一切正常
///
#define BPLA_COMERROR 1001 //通讯错或者未联接打印机
#define BPLA_PARAERROR 1002 //参数错误
#define BPLA_FILEOPENERROR 1003 //文件打开错误
#define BPLA_FILEREADERROR 1004 //文件读错误
#define BPLA_FILEWRITEERROR 1005 //文件写错误
#define BPLA_FILEERROR 1006 //文件不合要求
#define BPLA_NUMBEROVER 1007 //指定的接收信息数量过大
#define BPLA_IMAGETYPEERROR 1008 //图象文件格式不正确
#define BPLA_PAPERSHORT 1009 //缺纸
#define BPLA_RIBBIONSHORT 1010 //缺色带
#define BPLA_BUSY 1011 //解释器忙
#define BPLA_PAUSE 1012 //暂停
///中
#define BPLA_HEADHEAT 1013 //打印头过热
#define BPLA_HEADOVER 1014 //打印头抬起
#define BPLA_CUT 1015 //切刀错
#define BPLA_READERROR 1016 //read error
//打开端口
typedef int (__stdcall *mBY_Open_Port) (int devtype, int porttype, char *opencode, int codelength, int baudrate);
//打印
typedef int (__stdcall *mBY_Print_Label)
(
int LabelType,
const char *JiDaJu,
const char *FenFaJu,
const char *TiaoMaXinXi,
const char *HaoMa,
const char *JianShu,
const char *ZhongLiang,
const char *ZhuanKouJuDaiHao,
const char *XingHao,
const char *BeiZhu,
const char *BiaoPaiBianHao,
const char *ZhongLeiJianCheng,
const char *BenZhuan,
int Pieces
);
//查询状态
typedef int (__stdcall *mBY_Query_Status) ();
//关闭端口
typedef int (__stdcall *mBY_Close_Port) (char *closecode, long codelength);
class PACKAGE CByBarPrinter
{
public:
long OpenPort(long nPcIndex, long nPortIndex, long baudrate);
void ClosePort(void);
long PrintLabel
(
long LabelType,
String JiDaJu,
String FenFaJu,
String TiaoMaXinXi,
String HaoMa,
String JianShu,
String ZhongLiang,
String ZhuanKouJuDaiHao,
String XingHao,
String BeiZhu,
String BiaoPaiBianHao,
String ZhongLeiJianCheng,
String BenZhuan,
long Pieces
);
CByBarPrinter(void);
long GetStatus(void);
private:
HINSTANCE m_hDll;
bool LoadLib(void);
};
#endif
/*$T BYBARP~1.CPP GC 1.137 09/13/05 08:37:52 */
#pragma hdrstop
#include "ByBarPrinter.h"
/* ---------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------- */
CByBarPrinter::CByBarPrinter(void)
{
m_hDll = NULL;
}
/* ---------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------- */
bool CByBarPrinter::LoadLib(void)
{
if(m_hDll == NULL) m_hDll = ::LoadLibrary("byprndll.dll");
if(m_hDll == NULL)
return false;
else
return true;
}
/* ---------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------- */
long CByBarPrinter::OpenPort(long nPcIndex, long nPortIndex, long baudrate)
{
long retCode;
if(!LoadLib())
{
retCode = -2;
return retCode;
}
mBY_Open_Port BY_Open_Port;
BY_Open_Port = (mBY_Open_Port) GetProcAddress(m_hDll, "BY_Open_Port");
if(BY_Open_Port == NULL)
{
retCode = -1;
}
else
{
String szCom;
szCom = "COM";
szCom += IntToStr(nPortIndex);
int nLen = szCom.Length();
retCode = BY_Open_Port(nPcIndex, nPortIndex, szCom.c_str(), nLen, 19200);
}
return retCode;
}
/* ---------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------- */
void CByBarPrinter::ClosePort(void)
{
if(!LoadLib()) return;
mBY_Close_Port BY_Close_Port;
BY_Close_Port = (mBY_Close_Port) GetProcAddress(m_hDll, "BY_Close_Port");
char CloseCode[50];
int CodeLength = 0;
memset(CloseCode, 0, 50);
BY_Close_Port(CloseCode, CodeLength);
//FreeLibrary(m_hDll);
delete[] CloseCode;
return;
}
/* ---------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------- */
long CByBarPrinter::PrintLabel
(
long LabelType,
String JiDaJu,
String FenFaJu,
String TiaoMaXinXi,
String HaoMa,
String JianShu,
String ZhongLiang,
String ZhuanKouJuDaiHao,
String XingHao,
String BeiZhu,
String BiaoPaiBianHao,
String ZhongLeiJianCheng,
String BenZhuan,
long Pieces
)
{
long retCode;
if(!LoadLib())
{
retCode = -2;
return retCode;
}
//USES_CONVERSION;
mBY_Print_Label BY_Print_Label;
BY_Print_Label = (mBY_Print_Label) GetProcAddress(m_hDll, "BY_Print_Label");
if(BY_Print_Label == NULL)
{
retCode = -1;
return S_OK;
}
retCode = BY_Print_Label
(
LabelType,
JiDaJu.c_str(),
FenFaJu.c_str(),
TiaoMaXinXi.c_str(),
HaoMa.c_str(),
JianShu.c_str(),
ZhongLiang.c_str(),
ZhuanKouJuDaiHao.c_str(),
XingHao.c_str(),
BeiZhu.c_str(),
BiaoPaiBianHao.c_str(),
ZhongLeiJianCheng.c_str(),
BenZhuan.c_str(),
Pieces
);
return retCode;
}
/* ---------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------- */
long CByBarPrinter::GetStatus(void)
{
long state;
if(!LoadLib())
{
state = -1;
return state;
}
mBY_Query_Status BY_Query_Status;
BY_Query_Status = (mBY_Query_Status) GetProcAddress(m_hDll, "BY_Query_Status");
if(BY_Query_Status == NULL)
state = -1;
else
state = BY_Query_Status();
return state;
}
#pragma package(smart_init)
posted @
2005-10-31 13:54 surffish 阅读(243) |
评论 (0) |
编辑 收藏
substr(string,1,8)
1.
//将一个表中符合条件的记录批量更新到另外一张表。(假设表名为:toffice,tofficetemp)
update toffice a
set a.office_code = (select b.office_code from tofficetemp b where a.jgdm = b.jgdm)
where a.jgdm in (select jgdm from tofficetemp)
update tofficenexas set isuse = '0' where rowid in
(select rowid from tofficenexas a where
rowid !=(select max(rowid) from tofficenexas b where a.office_id = b.office_id and
a.father_office_id = b.father_office_id and a.big_kind_id =b.big_kind_id))
2
//查询某个时间段的值
select * from tdespatch t
where t.out_time between to_date('2005-10-18 15:47:42','yyyy-mm-dd hh24:mi:ss')
and to_date('2005-10-20 10:47:42','yyyy-mm-dd hh24:mi:ss')
3。
//复制表(只复制结构,源表名:a 新表名:b)
SQL: select * into b from a where 1<>1
4。
//拷贝表(拷贝数据,源表名:a 目标表名:b)
SQL: insert into b(a, b, c) select d,e,f from b;
5。
说明:显示文章、提交人和最后回复时间
SQL: select a.title,a.username,b.adddate from table a,(select max(adddate) adddate from table where table.title=a.title) b
说明:外连接查询(表名1:a 表名2:b)
SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c
说明:日程安排提前五分钟提醒
SQL: select * from 日程安排 where datediff('minute',f开始时间,getdate())>5
说明:两张关联表,删除主表中已经在副表中没有的信息
SQL:
delete from info where not exists ( select * from infobz where info.infid=infobz.infid )
说明:--
SQL:
SELECT A.NUM, A.NAME, B.UPD_DATE, B.PREV_UPD_DATE
FROM TABLE1,
(SELECT X.NUM, X.UPD_DATE, Y.UPD_DATE PREV_UPD_DATE
FROM (SELECT NUM, UPD_DATE, INBOUND_QTY, STOCK_ONHAND
FROM TABLE2
WHERE TO_CHAR(UPD_DATE,'YYYY/MM') = TO_CHAR(SYSDATE, 'YYYY/MM')) X,
(SELECT NUM, UPD_DATE, STOCK_ONHAND
FROM TABLE2
WHERE TO_CHAR(UPD_DATE,'YYYY/MM') =
TO_CHAR(TO_DATE(TO_CHAR(SYSDATE, 'YYYY/MM') ¦¦ '/01','YYYY/MM/DD') - 1, 'YYYY/MM') ) Y,
WHERE X.NUM = Y.NUM (+)
AND X.INBOUND_QTY + NVL(Y.STOCK_ONHAND,0) <> X.STOCK_ONHAND ) B
WHERE A.NUM = B.NUM
说明:--
SQL:
select * from studentinfo where not exists(select * from student where studentinfo.id=student.id) and 系名称='"&strdepartmentname&"' and 专业名称='"&strprofessionname&"' order by 性别,生源地,高考总成绩
说明:
从数据库中去一年的各单位电话费统计(电话费定额贺电化肥清单两个表来源)
SQL:
SELECT a.userper, a.tel, a.standfee, TO_CHAR(a.telfeedate, 'yyyy') AS telyear,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '01', a.factration)) AS JAN,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '02', a.factration)) AS FRI,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '03', a.factration)) AS MAR,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '04', a.factration)) AS APR,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '05', a.factration)) AS MAY,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '06', a.factration)) AS JUE,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '07', a.factration)) AS JUL,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '08', a.factration)) AS AGU,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '09', a.factration)) AS SEP,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '10', a.factration)) AS OCT,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '11', a.factration)) AS NOV,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '12', a.factration)) AS DEC
FROM (SELECT a.userper, a.tel, a.standfee, b.telfeedate, b.factration
FROM TELFEESTAND a, TELFEE b
WHERE a.tel = b.telfax) a
GROUP BY a.userper, a.tel, a.standfee, TO_CHAR(a.telfeedate, 'yyyy')
说明:四表联查问题:
SQL: select * from a left inner join b on a.a=b.b right inner join c on a.a=c.c inner join d on a.a=d.d where .....
说明:得到表中最小的未使用的ID号
SQL:
SELECT (CASE WHEN EXISTS(SELECT * FROM Handle b WHERE b.HandleID = 1) THEN MIN(HandleID) + 1 ELSE 1 END) as HandleID
FROM Handle
WHERE NOT HandleID IN (SELECT a.HandleID - 1 FROM Handle a)
6 根据父表 只取子表最新的一条记录{
SQL> select * from testa ;
F1
----------
1
2
3
SQL> select * from testb ;
F1 F2
---------- -------------------
1 2006-04-10 14:56:41
1 2006-04-10 14:56:53
1 2006-04-10 14:57:00
2 2006-04-10 14:57:08
3 2006-04-10 14:57:19
3 2006-04-10 14:57:25
6 rows selected.
SQL> select testa.f1,v1.f1,v1.f2 from testa ,(select f1,f2,row_number() over (partition by f1 order
by f2 desc) rn from testb) v1 where testa.f1 = v1.f1 and v1.rn = 1 ;
F1 F1 F2
---------- ---------- -------------------
1 1 2006-04-10 14:57:00
2 2 2006-04-10 14:57:08
3 3 2006-04-10 14:57:25
------------------------------------------
7 查询语句的优化
select t.* from acc$ t
where
not exists (在此不使用not in 是为了性能上的考虑)
(select 'a' from crm$ a
where
a.客户名=t.用户名称
--a.客户号=t.用户编号
)
posted @
2005-10-30 09:42 surffish 阅读(274) |
评论 (0) |
编辑 收藏
作者:[本站编辑] 来源:[CSDN] 浏览:[
] |
|
Java Servlet作为首选的服务器端数据处理技术,正在迅速取代CGI脚本。Servlet超越CGI的优势之一在于,不仅多个请求可以共享公用资源,而且还可以在不同用户请求之间保留持续数据。本文介绍一种充分发挥该特色的实用技术,即数据库连接池。
一、实现连接池的意义
动态Web站点往往用数据库存储的信息生成Web页面,每一个页面请求导致一次数据库访问。连接数据库不仅要开销一定的通讯和内存资源,还必须完成用户验证、安全上下文配置这类任务,因而往往成为最为耗时的操作。当然,实际的连接时间开销千变万化,但1到2秒延迟并非不常见。如果某个基于数据库的Web应用只需建立一次初始连 接,不同页面请求能够共享同一连接,就能获得显著的性能改善。 Servlet是一个Java类。Servlet引擎(它可能是Web服务软件的一部分,也可能是一个独立的附加模块)在系统启动或Servlet第一次被请求时将该类装入Java虚拟机并创建它的一个实例。不同用户请求由同一Servlet实例的多个独立线程处理。那些要 求在不同请求之间持续有效的数据既可以用Servlet的实例变量来保存,也可以保存在独立的辅助对象中。 用JDBC访问数据库首先要创建与数据库之间的连接,获得一个连接对象(Connection),由连接对象提供执行SQL语句的方法。 本文介绍的数据库连接池包括一个管理类DBConnectionManager,负责提供与多个连接池对象(DBConnectionPool类)之间的接口。每一个连接池对象管理一组JDBC连接对象,每一个连接对象可以被任意数量的Servlet共享。 类DBConnectionPool提供以下功能:
1) 从连接池获取(或创建)可用连接。 2) 把连接返回给连接池。 3) 在系统关闭时释放所有资源,关闭所有连接。
此外, DBConnectionPool类还能够处理无效连接(原来登记为可用的连接,由于某种原因不再可用,如超时,通讯问题) ,并能够限制连接池中的连接总数不超过某个预定值。 管理类DBConnectionManager用于管理多个连接池对象,它提供以下功能:
1) 装载和注册JDBC驱动程序。 2) 根据在属性文件中定义的属性创建连接池对象。 3) 实现连接池名字与其实例之间的映射。 4) 跟踪客户程序对连接池的引用,保证在最后一个客户程序结束时安全地关闭所有连接池。
本文余下部分将详细说明这两个类,最后给出一个示例演示Servlet使用连接池的一般过程。
二、具体实现
DBConnectionManager.java程序清单如下:
001 import java.io.*; 002 import java.sql.*; 003 import java.util.*; 004 import java.util.Date; 005 006 /** 007 * 管理类DBConnectionManager支持对一个或多个由属性文件定义的数据库连接 008 * 池的访问.客户程序可以调用getInstance()方法访问本类的唯一实例. 009 */ 010 public class DBConnectionManager { 011 static private DBConnectionManager instance; // 唯一实例 012 static private int clients; 013 014 private Vector drivers = new Vector(); 015 private PrintWriter log; 016 private Hashtable pools = new Hashtable(); 017 018 /** 019 * 返回唯一实例.如果是第一次调用此方法,则创建实例 020 * 021 * @return DBConnectionManager 唯一实例 022 */ 023 static synchronized public DBConnectionManager getInstance() { 024 if (instance == null) { 025 instance = new DBConnectionManager(); 026 } 027 clients++; 028 return instance; 029 } 030 031 /** 032 * 建构函数私有以防止其它对象创建本类实例 033 */ 034 private DBConnectionManager() { 035 init(); 036 } 037 038 /** 039 * 将连接对象返回给由名字指定的连接池 040 * 041 * @param name 在属性文件中定义的连接池名字 042 * @param con 连接对象 043 */ 044 public void freeConnection(String name, Connection con) { 045 DBConnectionPool pool = (DBConnectionPool) pools.get(name); 046 if (pool != null) { 047 pool.freeConnection(con); 048 } 049 } 050 051 /** 052 * 获得一个可用的(空闲的)连接.如果没有可用连接,且已有连接数小于最大连接数 053 * 限制,则创建并返回新连接 054 * 055 * @param name 在属性文件中定义的连接池名字 056 * @return Connection 可用连接或null 057 */ 058 public Connection getConnection(String name) { 059 DBConnectionPool pool = (DBConnectionPool) pools.get(name); 060 if (pool != null) { 061 return pool.getConnection(); 062 } 063 return null; 064 } 065 066 /** 067 * 获得一个可用连接.若没有可用连接,且已有连接数小于最大连接数限制, 068 * 则创建并返回新连接.否则,在指定的时间内等待其它线程释放连接. 069 * 070 * @param name 连接池名字 071 * @param time 以毫秒计的等待时间 072 * @return Connection 可用连接或null 073 */ 074 public Connection getConnection(String name, long time) { 075 DBConnectionPool pool = (DBConnectionPool) pools.get(name); 076 if (pool != null) { 077 return pool.getConnection(time); 078 } 079 return null; 080 } 081 082 /** 083 * 关闭所有连接,撤销驱动程序的注册 084 */ 085 public synchronized void release() { 086 // 等待直到最后一个客户程序调用 087 if (--clients != 0) { 088 return; 089 } 090 091 Enumeration allPools = pools.elements(); 092 while (allPools.hasMoreElements()) { 093 DBConnectionPool pool = (DBConnectionPool) allPools.nextElement(); 094 pool.release(); 095 } 096 Enumeration allDrivers = drivers.elements(); 097 while (allDrivers.hasMoreElements()) { 098 Driver driver = (Driver) allDrivers.nextElement(); 099 try { 100 DriverManager.deregisterDriver(driver); 101 log("撤销JDBC驱动程序 " + driver.getClass().getName()+"的注册"); 102 } 103 catch (SQLException e) { 104 log(e, "无法撤销下列JDBC驱动程序的注册: " + driver.getClass().getName()); 105 } 106 } 107 } 108 109 /** 110 * 根据指定属性创建连接池实例. 111 * 112 * @param props 连接池属性 113 */ 114 private void createPools(Properties props) { 115 Enumeration propNames = props.propertyNames(); 116 while (propNames.hasMoreElements()) { 117 String name = (String) propNames.nextElement(); 118 if (name.endsWith(".url")) { 119 String poolName = name.substring(0, name.lastIndexOf(".")); 120 String url = props.getProperty(poolName + ".url"); 121 if (url == null) { 122 log("没有为连接池" + poolName + "指定URL"); 123 continue; 124 } 125 String user = props.getProperty(poolName + ".user"); 126 String password = props.getProperty(poolName + ".password"); 127 String maxconn = props.getProperty(poolName + ".maxconn", "0"); 128 int max; 129 try { 130 max = Integer.valueOf(maxconn).intValue(); 131 } 132 catch (NumberFormatException e) { 133 log("错误的最大连接数限制: " + maxconn + " .连接池: " + poolName); 134 max = 0; 135 } 136 DBConnectionPool pool = 137 new DBConnectionPool(poolName, url, user, password, max); 138 pools.put(poolName, pool); 139 log("成功创建连接池" + poolName); 140 } 141 } 142 } 143 144 /** 145 * 读取属性完成初始化 146 */ 147 private void init() { 148 InputStream is = getClass().getResourceAsStream("/db.properties"); 149 Properties dbProps = new Properties(); 150 try { 151 dbProps.load(is); 152 } 153 catch (Exception e) { 154 System.err.println("不能读取属性文件. " + 155 "请确保db.properties在CLASSPATH指定的路径中"); 156 return; 157 } 158 String logFile = dbProps.getProperty("logfile", "DBConnectionManager.log"); 159 try { 160 log = new PrintWriter(new FileWriter(logFile, true), true); 161 } 162 catch (IOException e) { 163 System.err.println("无法打开日志文件: " + logFile); 164 log = new PrintWriter(System.err); 165 } 166 loadDrivers(dbProps); 167 createPools(dbProps); 168 } 169 170 /** 171 * 装载和注册所有JDBC驱动程序 172 * 173 * @param props 属性 174 */ 175 private void loadDrivers(Properties props) { 176 String driverClasses = props.getProperty("drivers"); 177 StringTokenizer st = new StringTokenizer(driverClasses); 178 while (st.hasMoreElements()) { 179 String driverClassName = st.nextToken().trim(); 180 try { 181 Driver driver = (Driver) 182 Class.forName(driverClassName).newInstance(); 183 DriverManager.registerDriver(driver); 184 drivers.addElement(driver); 185 log("成功注册JDBC驱动程序" + driverClassName); 186 } 187 catch (Exception e) { 188 log("无法注册JDBC驱动程序: " + 189 driverClassName + ", 错误: " + e); 190 } 191 } 192 } 193 194 /** 195 * 将文本信息写入日志文件 196 */ 197 private void log(String msg) { 198 log.println(new Date() + ": " + msg); 199 } 200 201 /** 202 * 将文本信息与异常写入日志文件 203 */ 204 private void log(Throwable e, String msg) { 205 log.println(new Date() + ": " + msg); 206 e.printStackTrace(log); 207 } 208 209 /** 210 * 此内部类定义了一个连接池.它能够根据要求创建新连接,直到预定的最 211 * 大连接数为止.在返回连接给客户程序之前,它能够验证连接的有效性. 212 */ 213 class DBConnectionPool { 214 private int checkedOut; 215 private Vector freeConnections = new Vector(); 216 private int maxConn; 217 private String name; 218 private String password; 219 private String URL; 220 private String user; 221 222 /** 223 * 创建新的连接池 224 * 225 * @param name 连接池名字 226 * @param URL 数据库的JDBC URL 227 * @param user 数据库帐号,或 null 228 * @param password 密码,或 null 229 * @param maxConn 此连接池允许建立的最大连接数 230 */ 231 public DBConnectionPool(String name, String URL, String user, String password, 232 int maxConn) { 233 this.name = name; 234 this.URL = URL; 235 this.user = user; 236 this.password = password; 237 this.maxConn = maxConn; 238 } 239 240 /** 241 * 将不再使用的连接返回给连接池 242 * 243 * @param con 客户程序释放的连接 244 */ 245 public synchronized void freeConnection(Connection con) { 246 // 将指定连接加入到向量末尾 247 freeConnections.addElement(con); 248 checkedOut--; 249 notifyAll(); 250 } 251 252 /** 253 * 从连接池获得一个可用连接.如没有空闲的连接且当前连接数小于最大连接 254 * 数限制,则创建新连接.如原来登记为可用的连接不再有效,则从向量删除之, 255 * 然后递归调用自己以尝试新的可用连接. 256 */ 257 public synchronized Connection getConnection() { 258 Connection con = null; 259 if (freeConnections.size() > 0) { 260 // 获取向量中第一个可用连接 261 con = (Connection) freeConnections.firstElement(); 262 freeConnections.removeElementAt(0); 263 try { 264 if (con.isClosed()) { 265 log("从连接池" + name+"删除一个无效连接"); 266 // 递归调用自己,尝试再次获取可用连接 267 con = getConnection(); 268 } 269 } 270 catch (SQLException e) { 271 log("从连接池" + name+"删除一个无效连接"); 272 // 递归调用自己,尝试再次获取可用连接 273 con = getConnection(); 274 } 275 } 276 else if (maxConn == 0 || checkedOut < maxConn) { 277 con = newConnection(); 278 } 279 if (con != null) { 280 checkedOut++; 281 } 282 return con; 283 } 284 285 /** 286 * 从连接池获取可用连接.可以指定客户程序能够等待的最长时间 287 * 参见前一个getConnection()方法. 288 * 289 * @param timeout 以毫秒计的等待时间限制 290 */ 291 public synchronized Connection getConnection(long timeout) { 292 long startTime = new Date().getTime(); 293 Connection con; 294 while ((con = getConnection()) == null) { 295 try { 296 wait(timeout); 297 } 298 catch (InterruptedException e) {} 299 if ((new Date().getTime() - startTime) >= timeout) { 300 // wait()返回的原因是超时 301 return null; 302 } 303 } 304 return con; 305 } 306 307 /** 308 * 关闭所有连接 309 */ 310 public synchronized void release() { 311 Enumeration allConnections = freeConnections.elements(); 312 while (allConnections.hasMoreElements()) { 313 Connection con = (Connection) allConnections.nextElement(); 314 try { 315 con.close(); 316 log("关闭连接池" + name+"中的一个连接"); 317 } 318 catch (SQLException e) { 319 log(e, "无法关闭连接池" + name+"中的连接"); 320 } 321 } 322 freeConnections.removeAllElements(); 323 } 324 325 /** 326 * 创建新的连接 327 */ 328 private Connection newConnection() { 329 Connection con = null; 330 try { 331 if (user == null) { 332 con = DriverManager.getConnection(URL); 333 } 334 else { 335 con = DriverManager.getConnection(URL, user, password); 336 } 337 log("连接池" + name+"创建一个新的连接"); 338 } 339 catch (SQLException e) { 340 log(e, "无法创建下列URL的连接: " + URL); 341 return null; 342 } 343 return con; 344 } 345 } 346 } 三、类DBConnectionPool说明
该类在209至345行实现,它表示指向某个数据库的连接池。数据库由JDBC URL标识。一个JDBC URL由三部分组成:协议标识(总是jdbc),驱动程序标识(如 odbc、idb、oracle等),数据库标识(其格式依赖于驱动程序)。例如,jdbc:odbc:de mo,即是一个指向demo数据库的JDBC URL,而且访问该数据库要使用JDBC-ODBC驱动程序。每个连接池都有一个供客户程序使用的名字以及可选的用户帐号、密码、最大连接数限制。如果Web应用程序所支持的某些数据库操作可以被所有用户执行,而其它一些操作应由特别许可的用户执行,则可以为两类操作分别定义连接池,两个连接池使用相同的JDBC URL,但使用不同的帐号和密码。 类DBConnectionPool的建构函数需要上述所有数据作为其参数。如222至238行所示,这些数据被保存为它的实例变量: 如252至283行、285至305行所示, 客户程序可以使用DBConnectionPool类提供的两个方法获取可用连接。两者的共同之处在于:如连接池中存在可用连接,则直接返回,否则创建新的连接并返回。如果没有可用连接且已有连接总数等于最大限制 数,第一个方法将直接返回null,而第二个方法将等待直到有可用连接为止。 所有的可用连接对象均登记在名为freeConnections的向量(Vector)中。如果向量中有多于一个的连接,getConnection()总是选取第一个。同时,由于新的可用连接总是从尾部加入向量,从而使得数据库连接由于长时间闲置而被关闭的风险减低到最小程度。 第一个getConnection()在返回可用连接给客户程序之前,调用了isClosed()方法验证连接仍旧有效。如果该连接被关闭或触发异常,getConnection()递归地调用自己以尝试获取另外的可用连接。如果在向量freeConnections中不存在任何可用连 接,getConnection()方法检查是否已经指定最大连接数限制。如已经指定,则检查当前连接数是否已经到达极限。此处maxConn为0表示没有限制。如果没有指定最大连接数限制或当前连接数小于该值,该方法尝试创建新的连接。如创建成功,则增加已使用连接的计数并返回,否则返回空值。 如325至345行所示,创建新连接由newConnection()方法实现。创建过程与是否已经指定数据库帐号、密码有关。 JDBC的DriverManager类提供多个getConnection()方法,这些方法要用到JDBC URL与其它一些参数,如用户帐号和密码等。 DriverManager将使用指定的JDBC URL确定适合于目标数据库的驱动程序及建立连接。 在285至305行实现的第二个getConnection()方法需要一个以毫秒为单位的时间参数,该参数表示客户程序能够等待的最长时间。建立连接的具体操作仍旧由第一个getConnection()方法实现。 该方法执行时先将startTime初始化为当前时间。在while循环中尝试获得一个连接。如果失败,则以给定的时间值为参数调用wait()。wait()的返回可能是由于其它线程调用notify()或notifyAll(),也可能是由于预定时间已到。为找出wait()返回的真正原因,程序用当前时间减开始时间(startTime),如差值大于预定时间则返回空值,否则再次调用getConnection()。 把空闲的连接登记到连接池由240至250行的freeConnection()方法实现,它的参数为返回给连接池的连接对象。该对象被加入到freeConnections向量的末尾,然后减少已使用连接计数。调用notifyAll()是为了通知其它正在等待可用连接的线程。 许多Servlet引擎为实现安全关闭提供多种方法。数据库连接池需要知道该事件以保证所有连接能够正常关闭。DBConnectionManager类负协调整个关闭过程,但关闭连接池中所有连接的任务则由DBConnectionPool类负责。在307至323行实现的release()方法供DBConnectionManager调用。该方法遍历freeConnections向量并关闭所有连接,然后从向量中删除这些连接。
四、类DBConnectionManager 说明
该类只能创建一个实例,其它对象能够调用其静态方法(也称为类方法)获得该唯一实例的引用。如031至036行所示,DBConnectionManager类的建构函数是私有的,这是为了避免其它对象创建该类的实例。 DBConnectionManager类的客户程序可以调用getInstance()方法获得对该类唯一实例的引用。如018至029行所示,类的唯一实例在getInstance()方法第一次被调用期间创建,此后其引用就一直保存在静态变量instance中。每次调用getInstance() 都增加一个DBConnectionManager的客户程序计数。即,该计数代表引用DBConnectionManager唯一实例的客户程序总数,它将被用于控制连接池的关闭操作。 该类实例的初始化工作由146至168行之间的私有方法init()完成。其中 getResourceAsStream()方法用于定位并打开外部文件。外部文件的定位方法依赖于类装载器的实现。标准的本地类装载器查找操作总是开始于类文件所在路径,也能够搜索CLASSPATH中声明的路径。db.properties是一个属性文件,它包含定义连接池的键-值对。可供定义的公用属性如下:
drivers 以空格分隔的JDBC驱动程序类列表 logfile 日志文件的绝对路径
其它的属性和特定连接池相关,其属性名字前应加上连接池名字:
<poolname>.url 数据库的 JDBC URL <poolname>.maxconn 允许建立的最大连接数,0表示没有限制 <poolname>.user 用于该连接池的数据库帐号 <poolname>.password 相应的密码
其中url属性是必需的,而其它属性则是可选的。数据库帐号和密码必须合法。用于Windows平台的db.properties文件示例 如下:
drivers=sun.jdbc.odbc.JdbcOdbcDriver jdbc.idbDriver logfile=D:\\user\\src\\java\\DBConnectionManager\\log.txt
idb.url=jdbc:idb:c:\\local\\javawebserver1.1\\db\\db.prp idb.maxconn=2
access.url=jdbc:odbc:demo access.user=demo access.password=demopw
注意在Windows路径中的反斜杠必须输入2个,这是由于属性文件中的反斜杠同时也是一个转义字符。 init()方法在创建属性对象并读取db.properties文件之后,就开始检查logfile属性。如果属性文件中没有指定日志文件,则默认为当前目录下的DBConnectionManager.log文件。如日志文件无法使用,则向System.err输出日志记录。 装载和注册所有在drivers属性中指定的JDBC驱动程序由170至192行之间的loadDrivers()方法实现。该方法先用StringTokenizer将drivers属性值分割为对应于驱动程序名称的字符串,然后依次装载这些类并创建其实例,最后在 DriverManager中注册 该实例并把它加入到一个私有的向量drivers。向量drivers将用于关闭服务时从DriverManager取消所有JDBC 驱动程序的注册。 init()方法的最后一个任务是调用私有方法createPools()创建连接池对象。如109至142行所示,createPools()方法先创建所有属性名字的枚举对象(即Enumeration对象,该对象可以想象为一个元素系列,逐次调用其nextElement()方法将顺序返 回各元素),然后在其中搜索名字以“.url”结尾的属性。对于每一个符合条件的属性,先提取其连接池名字部分,进而读取所有属于该连接池的属性,最后创建连接池对象并把它保存在实例变量pools中。散列表(Hashtable类 )pools实现连接 池名字到连接池对象之间的映射,此处以连接池名字为键,连接池对象为值。 为便于客户程序从指定连接池获得可用连接或将连接返回给连接池,类DBConnectionManager提供了方法getConnection()和freeConnection()。所有这些方法都要求在参数中指定连接池名字,具体的连接获取或返回操作则调用对应的连接池对象完成。它们的实现分别在051至064行、066至080行、038至049行。 如082至107行所示,为实现连接池的安全关闭,DBConnectionManager提供了方法release()。在上面我们已经提到,所有DBConnectionManager的客户程序都应该调用静态方法getInstance()以获得该管理器的引用,此调用将增加客户程序计数。 客户程序在关闭时调用release()可以递减该计数。当最后一个客户程序调用release(),递减后的引用计数为0,就可以调用各个连接池的release()方法关闭所有连接了。管理类release()方法最后的任务是撤销所有JDBC驱动程序的注册。
五、Servlet使用连接池示例
Servlet API所定义的Servlet生命周期类如:
1) 创建并初始化Servlet(init()方法)。 2) 响应客户程序的服务请求(service()方法)。 3) Servlet终止运行,释放所有资源(destroy()方法)。
本例演示连接池应用,上述关键步骤中的相关操作为:
1) 在init(),用实例变量connMgr 保存调用DBConnectionManager.getInstance()所返回的引用。 2) 在service(),调用getConnection(),执行数据库操作,用freeConnection()将连接返回给连接池。 3) 在destroy(),调用release()关闭所有连接,释放所有资源。
示例程序清单如下:
import java.io.*; import java.sql.*; import javax.servlet.*; import javax.servlet.http.*; public class TestServlet extends HttpServlet { private DBConnectionManager connMgr;
public void init(ServletConfig conf) throws ServletException { super.init(conf); connMgr = DBConnectionManager.getInstance(); }
public void service(HttpServletRequest req, HttpServletResponse res) throws IOException {
res.setContentType("text/html"); PrintWriter out = res.getWriter(); Connection con = connMgr.getConnection("idb"); if (con == null) { out.println("不能获取数据库连接."); return; } ResultSet rs = null; ResultSetMetaData md = null; Statement stmt = null; try { stmt = con.createStatement(); rs = stmt.executeQuery("SELECT * FROM EMPLOYEE"); md = rs.getMetaData(); out.println("<H1>职工数据</H1>"); while (rs.next()) { out.println("<BR>"); for (int i = 1; i < md.getColumnCount(); i++) { out.print(rs.getString(i) + ", "); } } stmt.close(); rs.close(); } catch (SQLException e) { e.printStackTrace(out); } connMgr.freeConnection("idb", con); }
public void destroy() { connMgr.release(); super.destroy(); } } |
posted @
2005-10-28 09:50 surffish 阅读(299) |
评论 (0) |
编辑 收藏
exp airport/airport@ems file=airport2.dmp owner=airport log=exp2.log
posted @
2005-10-26 09:56 surffish 阅读(1182) |
评论 (0) |
编辑 收藏
Java打印程序设计
文枫 深圳全通数码技术总监
1 前言
在我们的实际工作中,经常需要实现打印功能。但由于历史原因,Java提供的打印功能一直都比较弱。实际上最初的jdk根本不支持打印,直到jdk1.1才引入了很轻量的打印支持。所以,在以前用Java/Applet/JSP/Servlet设计的程序中,较复杂的打印都是通过调用ActiveX/OCX控件或者VB/VC程序来实现的,非常麻烦。实际上,SUN公司也一直致力于Java打印功能的完善,而Java2平台则终于有了一个健壮的打印模式的开端,该打印模式与Java2D图形包充分结合成一体。更令人鼓舞的是,新发布的jdk1.4则提供了一套完整的"Java 打印服务 API" (Java Print Service API),它对已有的打印功能是积极的补充。利用它,我们可以实现大部分实际应用需求,包括打印文字、图形、文件及打印预览等等。本文将通过一个具体的程序实例来说明如何设计Java打印程序以实现这些功能,并对不同版本的实现方法进行分析比较。希望大家能从中获取一些有益的提示。
2 Java中的打印
2.1 Java的打印API
Java的打印API主要存在于java.awt.print包中。而jdk1.4新增的类则主要存在于javax.print包及其相应的子包javax.print.event和javax.print.attribute中。其中javax.print包中主要包含打印服务的相关类,而javax.print.event则包含打印事件的相关定义,javax.print.attribute则包括打印服务的可用属性列表等。
2.2 如何实现打印
要产生一个打印,至少需要考虑两条:
- 需要一个打印服务对象。这可通过三种方式实现:在jdk1.4之前的版本,必须要实现java.awt.print.Printable接口或通过Toolkit.getDefaultToolkit().getPrintJob来获取打印服务对象;在jdk1.4中则还可以通过javax.print.PrintSerivceLookup来查找定位一个打印服务对象。
- 需要开始一个打印工作。这也有几种实现方法:在jdk1.4之前可以通过java.awt.print.PrintJob(jdk1.1提供的,现在已经很少用了)调用print或printAll方法开始打印工作;也可以通过java.awt.print.PrinterJob的printDialog显示打印对话框,然后通过print方法开始打印;在jdk1.4中则可以通过javax.print.ServiceUI的printDialog显示打印对话框,然后调用print方法开始一个打印工作。
2.3 打印机对话框
2.3.1 Printable的打印对话框
开始打印工作之前,可以通过PrinterJob.printDialog来显示一个打印对话框。它给用户一个机会以选择应该打印的页码范围,并可供用户改变打印设置。它是一个本地对话框。
事实上,当从一个Printable对象进行一个打印工作时,打印对象并不知道需要打印多少页。它只是不停地调用print方法。只要print方法返回Printable.PAGE_EXISTS值,打印工作就不停地产生打印页,直到print方法返回Printable.NO_SUCH_PAGE时,打印工作才停止。
由于打印工作只有在打印完成后才进行准确的页数计算,所以在对话框上的页码范围是尚未初始化的[1,9999]。我们可以通过构建一个java.awt.print.Book对象传递给打印对象;也可以通过指定的格式计算需要打印的页数并传递给打印对象,使其准确地知道要打印多少页。
2.3.2 ServiceUI的打印对话框
与Printable的对话框不同的是,在jdk1.4提供ServiceUI的打印机对话框的缺省行为已经用新的 API 更改了:缺省情况下对话框不显示。我们必须使用ServiceUI类调用printDialog方法创建如下所示的打印对话框。
3 Java打印程序设计实例
3.1 打印文本
3.1.1 应用场景
假设我们需要打印一个窗体的某个文本编辑域(可能只有几行,也可能包含多页)的内容,并且每页最多打印54行,如何实现呢?
3.1.2 解决方法
基本思路如下:首先我们需要实现Printable接口,然后按照每页最多54行的格式计算共需要打印多少页,当打印文本的按钮被点击时,执行相应的打印动作。打印文本的具体操作可通过Graphics2D的drawString方法来实现。
1、实现Printable接口
/*Graphic指明打印的图形环境;PageFormat指明打印页格式(页面大小以点为计量单位,
1点为1英才的1/72,1英寸为25.4毫米。A4纸大致为595×842点);page指明页号*/
public int print(Graphics g, PageFormat pf, int page) throws PrinterException
{
Graphics2D g2 = (Graphics2D)g;
g2.setPaint(Color.black); //设置打印颜色为黑色
if (page >= PAGES) //当打印页号大于需要打印的总页数时,打印工作结束
return Printable.NO_SUCH_PAGE;
g2.translate(pf.getImageableX(), pf.getImageableY());//转换坐标,确定打印边界
drawCurrentPageText(g2, pf, page); //打印当前页文本
return Printable.PAGE_EXISTS; //存在打印页时,继续打印工作
}
/*打印指定页号的具体文本内容*/
private void drawCurrentPageText(Graphics2D g2, PageFormat pf, int page)
{
String s = getDrawText(printStr)[page];//获取当前页的待打印文本内容
//获取默认字体及相应的尺寸
FontRenderContext context = g2.getFontRenderContext();
Font f = area.getFont();
String drawText;
float ascent = 16; //给定字符点阵
int k, i = f.getSize(), lines = 0;
while(s.length() > 0 && lines < 54) //每页限定在54行以内
{
k = s.indexOf('\n'); //获取每一个回车符的位置
if (k != -1) //存在回车符
{
lines += 1; //计算行数
drawText = s.substring(0, k); //获取每一行文本
g2.drawString(drawText, 0, ascent); //具体打印每一行文本,同时走纸移位
if (s.substring(k + 1).length() > 0)
{
s = s.substring(k + 1); //截取尚未打印的文本
ascent += i;
}
}
else //不存在回车符
{
lines += 1; //计算行数
drawText = s; //获取每一行文本
g2.drawString(drawText, 0, ascent); //具体打印每一行文本,同时走纸移位
s = ""; //文本已结束
}
}
}
/*将打印目标文本按页存放为字符串数组*/
public String[] getDrawText(String s)
{
String[] drawText = new String[PAGES];//根据页数初始化数组
for (int i = 0; i < PAGES; i++)
drawText[i] = ""; //数组元素初始化为空字符串
int k, suffix = 0, lines = 0;
while(s.length() > 0)
{
if(lines < 54) //不够一页时
{
k = s.indexOf('\n');
if (k != -1) //存在回车符
{
lines += 1; //行数累加
//计算该页的具体文本内容,存放到相应下标的数组元素
drawText[suffix] = drawText[suffix] + s.substring(0, k + 1);
if (s.substring(k + 1).length() > 0)
s = s.substring(k + 1);
}
else
{
lines += 1; //行数累加
//将文本内容存放到相应的数组元素
drawText[suffix] = drawText[suffix] + s;
s = "";
}
}
else //已满一页时
{
lines = 0; //行数统计清零
suffix++; //数组下标加1
}
}
return drawText;
}
2、计算需要打印的总页数
public int getPagesCount(String curStr)
{
int page = 0;
int position, count = 0;
String str = curStr;
while(str.length() > 0) //文本尚未计算完毕
{
position = str.indexOf('\n'); //计算回车符的位置
count += 1; //统计行数
if (position != -1)
str = str.substring(position + 1); //截取尚未计算的文本
else
str = ""; //文本已计算完毕
}
if (count > 0)
page = count / 54 + 1; //以总行数除以54获取总页数
return page; //返回需打印的总页数
}
3.1、以jdk1.4以前的版本实现打印动作按钮监听,并完成具体的打印操作
private void printTextAction()
{
printStr = area.getText().trim(); //获取需要打印的目标文本
if (printStr != null && printStr.length() > 0) //当打印内容不为空时
{
PAGES = getPagesCount(printStr); //获取打印总页数
PrinterJob myPrtJob = PrinterJob.getPrinterJob(); //获取默认打印作业
PageFormat pageFormat = myPrtJob.defaultPage(); //获取默认打印页面格式
myPrtJob.setPrintable(this, pageFormat); //设置打印工作
if (myPrtJob.printDialog()) //显示打印对话框
{
try
{
myPrtJob.print(); //进行每一页的具体打印操作
}
catch(PrinterException pe)
{
pe.printStackTrace();
}
}
}
else
{
//如果打印内容为空时,提示用户打印将取消
JOptionPane.showConfirmDialog
(null, "Sorry, Printer Job is Empty, Print Cancelled!", "Empty",
JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE);
}
}
3.2、以jdk1.4新版本提供的API实现打印动作按钮监听,并完成具体的打印操作
private void printText2Action()
{
printFlag = 0; //打印标志清零
printStr = area.getText().trim();//获取需要打印的目标文本
if (printStr != null && printStr.length() > 0) //当打印内容不为空时
{
PAGES = getPagesCount(printStr); //获取打印总页数
//指定打印输出格式
DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
//定位默认的打印服务
PrintService printService = PrintServiceLookup.lookupDefaultPrintService();
//创建打印作业
DocPrintJob job = printService.createPrintJob();
//设置打印属性
PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
DocAttributeSet das = new HashDocAttributeSet();
//指定打印内容
Doc doc = new SimpleDoc(this, flavor, das);
//不显示打印对话框,直接进行打印工作
try
{
job.print(doc, pras); //进行每一页的具体打印操作
}
catch(PrintException pe)
{
pe.printStackTrace();
}
}
else
{
//如果打印内容为空时,提示用户打印将取消
JOptionPane.showConfirmDialog(null, "Sorry, Printer Job is Empty, Print Cancelled!", "Empty", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE);
}
}
3.2 打印预览
3.2.1应用场景
大多少商业应用都需要提供打印预览机制,它可以让我们在屏幕上看到页面,这样就不会因为不喜欢的打印结果而浪费纸张。假设我们在打印上一节所说的文本之前,需要先进行打印预览。那么该怎么实现呢?
界面实现图示如下:(Next预览下一页,Preview预览前一页,Close则关闭预览)
3.2.2解决方法
基本思路:虽然Java2平台的打印API并不提供标准的打印预览对话框,但是自己来进行设计也并不复杂。正常情况下,print方法将页面环境绘制到一个打印机图形环境上,从而实现打印。而事实上,print方法并不能真正产生打印页面,它只是将待打印内容绘制到图形环境上。所以,我们可以忽略掉屏幕图形环境,经过适当的缩放比例,使整个打印页容纳在一个屏幕矩形里,从而实现精确的打印预览。
在打印预览的设计实现中,主要需要解决两个问题。第一,如何将打印内容按合适的比例绘制到屏幕;第二,如何实现前后翻页。下面我给出这两个问题的具体实现方法,完整的实现请参看附件中的PrintPreviewDialog.java文件。
/*将待打印内容按比例绘制到屏幕*/
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
PageFormat pf = PrinterJob.getPrinterJob().defaultPage(); //获取页面格式
double xoff; //在屏幕上页面初始位置的水平偏移
double yoff; //在屏幕上页面初始位置的垂直偏移
double scale; //在屏幕上适合页面的比例
double px = pf.getWidth(); //页面宽度
double py = pf.getHeight(); //页面高度
double sx = getWidth() - 1;
double sy = getHeight() - 1;
if (px / py < sx / sy)
{
scale = sy / py; //计算比例
xoff = 0.5 * (sx - scale * px); //水平偏移量
yoff = 0;
}
else
{
scale = sx / px; //计算比例
xoff = 0;
yoff = 0.5 * (sy - scale * py); //垂直偏移量
}
g2.translate((float)xoff, (float)yoff); //转换坐标
g2.scale((float)scale, (float)scale);
Rectangle2D page = new Rectangle2D.Double(0, 0, px, py); //绘制页面矩形
g2.setPaint(Color.white); //设置页面背景为白色
g2.fill(page);
g2.setPaint(Color.black);//设置页面文字为黑色
g2.draw(page);
try
{
preview.print(g2, pf, currentPage); //显示指定的预览页面
}
catch(PrinterException pe)
{
g2.draw(new Line2D.Double(0, 0, px, py));
g2.draw(new Line2D.Double(0, px, 0, py));
}
}
/*预览指定的页面*/
public void viewPage(int pos)
{
int newPage = currentPage + pos;
//指定页面在实际的范围内
if (0 <= newPage && newPage < preview.getPagesCount(printStr))
{
currentPage = newPage; //将指定页面赋值为当前页
repaint();
}
}
这样,在按下"Next"按钮时,只需要调用canvas.viewPage(1);而在按下"Preview"按钮时,只需要调用canvas.viewPage(-1)即可实现预览的前后翻页。
3.3 打印图形
3.3.1应用场景
在实际应用中,我们还需要打印图形。譬如,我们有时需要将一个Java Applet的完整界面或一个应用程序窗体及其所包含的全部组件都打印出来,又应该如何实现呢?
3.3.2解决方法
基本思路如下:在Java的Component类及其派生类中都提供了print和printAll方法,只要设置好属性就可以直接调用这两个方法,从而实现对组件及图形的打印。
/*打印指定的窗体及其包含的组件*/
private void printFrameAction()
{
Toolkit kit = Toolkit.getDefaultToolkit(); //获取工具箱
Properties props = new Properties();
props.put("awt.print.printer", "durango");//设置打印属性
props.put("awt.print.numCopies", "2");
if(kit != null)
{
//获取工具箱自带的打印对象
PrintJob printJob = kit.getPrintJob(this, "Print Frame", props);
if(printJob != null)
{
Graphics pg = printJob.getGraphics();//获取打印对象的图形环境
if(pg != null)
{
try
{
this.printAll(pg);//打印该窗体及其所有的组件
}
finally
{
pg.dispose();//注销图形环境
}
}
printJob.end();//结束打印作业
}
}
}
3.4 打印文件
3.4.1应用场景
在很多实际应用情况下,我们可能都需要打印用户指定的某一个文件。该文件可能是图形文件,如GIF、JPEG等等;也可能是文本文件,如TXT、Java文件等等;还可能是复杂的PDF、DOC文件等等。那么对于这样的打印需求,我们又应该如何实现呢?
3.4.2解决方法
基本思路:在jdk1.4以前的版本,要实现这样的打印功能将非常麻烦和复杂,甚至是难以想象的。但幸运的是,jdk1.4的打印服务API提供了一整套的打印文件流的类和方法。利用它们,我们可以非常方便快捷地实现各式各样不同类型文件的打印功能。下面给出一个通用的处理方法。
/*打印指定的文件*/
private void printFileAction()
{
//构造一个文件选择器,默认为当前目录
JFileChooser fileChooser = new JFileChooser(SystemProperties.USER_DIR);
int state = fileChooser.showOpenDialog(this);//弹出文件选择对话框
if (state == fileChooser.APPROVE_OPTION)//如果用户选定了文件
{
File file = fileChooser.getSelectedFile();//获取选择的文件
//构建打印请求属性集
PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
//设置打印格式,因为未确定文件类型,这里选择AUTOSENSE
DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;
//查找所有的可用打印服务
PrintService printService[] = PrintServiceLookup.lookupPrintServices(flavor, pras);
//定位默认的打印服务
PrintService defaultService = PrintServiceLookup.lookupDefaultPrintService();
//显示打印对话框
PrintService service = ServiceUI.printDialog(null, 200, 200, printService
, defaultService, flavor, pras);
if (service != null)
{
try
{
DocPrintJob job = service.createPrintJob();//创建打印作业
FileInputStream fis = new FileInputStream(file);//构造待打印的文件流
DocAttributeSet das = new HashDocAttributeSet();
Doc doc = new SimpleDoc(fis, flavor, das);//建立打印文件格式
job.print(doc, pras);//进行文件的打印
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}
在上面的示例中,因尚未确定文件的类型,所以将指定文件的打印格式定义为DocFlavor.INPUT_STREAM.AUTOSENSE。事实上,如果在进行打印之前,就已确定地知道文件的格式,如为GIF,就应定义为DocFlavor.INPUT_STREAM.GIF ;如为PDF,就应该定义为DocFlavor.INPUT_STREAM.PDF;如为纯ASCII文件,就可以定义为 DocFlavor.INPUT_STREAM.TEXT_HTML_US_ASCII。等等。jdk1.4的javax.print.DocFlavor提供了极为丰富的文件流类型,你可以根据具体的应用需求进行合适的选择。具体的API参考文档可见本文的参考资料3。
4 结束语
以上是本人在两年多J2EE应用开发中,总结的关于用Java进行打印程序设计的一些经验,希望能给大家一些启示和裨益。尽管目前用Java来实现打印功能与用Microsoft的MFC API相比确实有更多的麻烦。但jdk1.4的推出,对Java以前较弱的打印功能是一个极好的补充。相信大家如果能够很好地理解前文所述的打印程序设计实例,并加以应用和拓展,应该可以解决目前大部分应用的实际编程问题。而随着Java的进一步发展和完善,必将更好地充实其基础类库及打印API,相信用Java实现高级打印功能也将越来越不成为我们这些Java痴迷者头痛的问题。
5 参考资料
《Java2核心技术 卷Ⅱ:高级特性》 机械工业出版社
Java打印服务参考文档:http://java.sun.com/j2se/1.4/docs/guide/jps/
jdk1.4 API参考文档:http://java.sun.com/j2se/1.4/docs/api/
6 例程源码
PrintSrc.zip包含下列java源代码和Class代码:
PrintTest.java包含了本文所描述的所有打印功能的实现源代码。相应的打印文本功能通过Print Text和PrintText2(jdk1.4实现)按钮调用;打印文件通过Print File按钮调用;打印图形通过Print Frame按钮调用;而Print Preview则进行打印预览。
PrintPreviewDialog.java包含打印预览源代码,你可以通过PrintTest窗体中的Print Preview按钮来调用。
关于作者
文枫,深圳全通数码技术总监。目前专注于J2EE应用与开发。休闲时间喜欢旅游、踢球。你可以通过 wenfb@sina.com 与我联系
posted @
2005-10-25 14:18 surffish 阅读(436) |
评论 (0) |
编辑 收藏
几个删除重复记录的SQL语句 |
在大的数据库应用中,经常因为各种原因遇到重复的记录,造成数据的冗余和维护上的不便。
1.用rowid方法
2.用group by方法
3.用distinct方法
1。用rowid方法
据据oracle带的rowid属性,进行判断,是否存在重复,语句如下: 查数据: select * from table1 a where rowid !=(select max(rowid) from table1 b where a.name1=b.name1 and a.name2=b.name2......) 删数据: delete from table1 a where rowid !=(select max(rowid) from table1 b where a.name1=b.name1 and a.name2=b.name2......)
2.group by方法
查数据: select count(num), max(name) from student --列出重复的记录数,并列出他的name属性 group by num having count(num) >1 --按num分组后找出表中num列重复,即出现次数大于一次 删数据: delete from student group by num having count(num) >1 这样的话就把所有重复的都删除了。
3.用distinct方法 -对于小的表比较有用
create table table_new as select distinct * from table1 minux truncate table table1; insert into table1 select * from table_new;
|
select sum(bag_weight),sum(bag_total) from tdespatch
posted @
2005-10-25 13:58 surffish 阅读(708) |
评论 (0) |
编辑 收藏