Ecology系统程序员开发指南
文件编号: 生效日期: FFPD-0100- JSWD- 001 2003-08-25 密级: 版次:1.1 总页数 正文 编制:杨国生 审核:刘煜 受控编号: FFPD-0100- JSWD- 001 修改状态: 附录 批准:刘煜
文件修改控制
修改记录编号 A1 修改 状态 新建V1.0 修改页码及条款 修改人 审核人 批准人 杨国生 刘煜 刘煜 修改日期 2003-08-25
2 泛微网络有限公司 www.weaver.com.cn
目 录
1. 引言........................................................................................................................ 4 1.1 概述................................................................................................................ 4 1.2 定义................................................................................................................ 4 1.3 参考资料........................................................................................................ 4 2. 开发规范................................................................................................................ 5 3. 底层包应用及范例................................................................................................ 7 3.1 最基本的继承类............................................................................................ 7 3.2 怎样记录日志................................................................................................ 8 3.3 怎样获取属性文件的值.............................................................................. 11 3.4 怎样获取系统的运行目录.......................................................................... 13 3.5 怎样访问和运行数据库脚本...................................................................... 14 3.6 如何上传一个文件...................................................................................... 18 3.7 如何访问已经上传的文件.......................................................................... 22 3.8 如何使用缓存提高系统效率...................................................................... 23 3.9 其它底层类基本方法.................................................................................. 32
3 泛微网络有限公司 www.weaver.com.cn
1. 引言
1.1 概述
本需求文档为泛微协同商务系统(Ecology)程序员开发指导文档,讲述了开发的规范,底层工具包的应用,常用功能的开发。通过本文档,开发人员应掌握
1.2 定义
1.3 参考资料
书籍资料 编号 资料名称 简介 1 Thinking in java Java 思想-一本很不错的书 2 Java Servlet Programing Java Servlet 编程 3 J2EE 大全 J2EE 的一本学习书
Intenet上杂志、专业著作、技术标准以及他们的网址。 网址 简介 Java.sun.com SUN 的java 网站 www.java-cn.net java 中文站 (较多的书籍, 代码) www.javastudy.org java 学习网
4 泛微网络有限公司 www.weaver.com.cn
2. 开发规范
程序文件头部加上文件描述,包括标题,描述,公司,作者,版本,创建日期,修改记录,如下: /**
* Title: 数据库链接池集合
* Description: 所有的数据库链接池的集合,用链接池名称区别每一个 * 链接池 * Company: 泛微软件 * @author : 杨国生 * @version 1.0
* create date : 2001-10-23 * modify log:
* 2002-09-13 杨国生 增加链接池Encode参数 * * */
各个明显部分间加空行,各方法间加空行,方法前面加方法的注释,遵从JavaDoc规范,与所释方法间不加空行。注释用中文给出: /**
* 从指定名称的链接池中获得一个可用连接.若没有可用连接,且已有连接 * 数小于最大连接数限制, 则创建并返回新连接.否则, 在指定的时间内等 * 待其它线程释放连接.如指定的时间内没有链接返回,则返回空。 *
* @param name 连接池名字
* @param time 以毫秒计的等待时间
* @return WeaverConnection 可用连接或null */
public WeaverConnection getConnection(String name, int time) {
DBConnectionPool pool = (DBConnectionPool) pools.get( name ) ; if (pool == null) pool = createPools( name ) ; if (pool != null) { clients ++ ; return pool.getConnection( time ) ; } return null; }
泛微网络有限公司 www.weaver.com.cn
5
大括号{ 不换行, } 需要换行。当中代码要缩进。缩进为4个空格,必须以空格代替Tab键。
圆括号()与前面的方法名间留一个空格,括号内的每个参数前留一个空格,最后一个参数后也留一个空格。
逗号后面,分号前面,运算符号和赙值符号的前后,留一个空格。
类、接口名大写字母开头,后面单词首字母大写。尽量不用下划线。如UserGroup(Class)。
对象实例、变量小写字母开头,后面单词大写。如userSQLBean(UserSQL对象的实例) , locationName(变量)。
方法名小写字母开头,首单词一般为动词,后面单词大写开头。如User.getUserName,UserSQLBean.formatSQLSearch。
开发代码不能使用当前开发环境已经不建议使用的方法。在开发环境升级的时候,需要重新编译原有系统的代码,如发现原有系统存在升级的环境中不建议使用的方法,应进行更新。
6 泛微网络有限公司 www.weaver.com.cn
3. 底层包应用及范例
本章对ecology系统的底层工具包进行讲解,开发人员可以从这里学到怎样利用底层的工具包进行开发
3.1 最基本的继承类
每一个java bean 都需要继承 weaver.general.BaseBean 类 ,每一个 servlet 都需要继承weaver.general. DynamicServlet。这两个类都实现了记录日志和获取属性文件值的方法。继承这两个类的其它类可直接应用这些方法来记录日志,获取属性文件某一个属性的值。方法的实现见后面的例子。继承了weaver.general. DynamicServlet类的servlet已经是一个servlet了,只需要实现weaver.general. DynamicServlet类的抽象方法doProcess就可以实现 doGet 和doPost 方法了。
继承的例子如下:
java bean 的继承
public class ResourceComInfo extends BaseBean { public void doSomething() { //某一个方法 方法的处理……… writeLog(s) ; // 写日志 } }
servlet 的继承
public class HelloServlet extends DynamicServlet {
public void doProcess(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
对请求的处理……….. } }
7 泛微网络有限公司 www.weaver.com.cn
3.2 怎样记录日志
继承了weaver.general.BaseBean 或weaver.general. DynamicServlet的类,可以直接使用 writeLog方法记录日志信息。注意这里是使用,而不是调用,因为这个方法是这两个被继承类中的方法。
注意writeLog 方法的使用: /**
* 将某个对象写入Log文件 * @param obj 被写入的对象 */
public void writeLog(Object obj)
我们看到,可以被记入日志的是任意一个java对象。这些java对象将被自动转换成字符串对象(String)记入到日志文件中。日志文件将每天生成一个,以日志文件名称中的日期来区别,比如:ecology20030812.log,代表2003年8月12日的日志。每一条日志的记录格式为 :
YYYY.MM.DD-HH:MM:SS 记录日志的类名 – 日志信息 比如:
2003.03.11-06:52:05 weaver.datacenter.OutReportResult - sql is select ROUND(sum(F_sksr),5) from T_yyrb A , CRM_CustomerInfo where CRM_CustomerInfo.id=A.crmid and CRM_CustomerInfo.id in(5) and
A.reportdate >= '2004-01-10' and A.reportdate <= '2004-03-10' and A.inputstatus >= '0' and A.inputstatus<>'9' and A.modtype='0'
日志的记录有两种模式,第一种为调试模式,第二种为在线模式。在第一种模式下,所有的java对象都会记录到日志文件中,包括调试信息,在第二种模式下,只有为Exception (异常)的对象才会记录到日志文件中。模式的设置在weaver.properties 属性文件中的DEBUG_MODE 属性来指定,如下:
DEBUG_MODE = true // 为调试模式 DEBUG_MODE = false // 为在线模式
正式上线的系统需要将模式设置为在线模式。
记录日志的例子如下:
public class ResourceComInfo extends BaseBean {
private void setResourceInfo() throws Exception{
try{
业务处理过程……….
String debugInfo = \
8 泛微网络有限公司 www.weaver.com.cn
}
// 在调试模式下将会记入日志文件
writeLog(\ }
catch(Exception e) { // 在任何模式下出现异常,都将会记入日志文件 writeLog(e) ; throw e ; }
}
记录的日志文件形式为:
2003.03.11-06:52:05 weaver.hrm.resource. ResourceComInfo - debug info is This is test
如果有异常,将会记录为:
2003.03.11-06:52:05 weaver.hrm.resource. ResourceComInfo -
java.sql.SQLException: [Microsoft][SQLServer JDBC Driver][SQLServer]形式参数 '@id_1' 定义为 OUTPUT,但实际参数却未声明为 OUTPUT。 at com.microsoft.jdbc.base.BaseExceptions.getException(Unknown Source) at com.microsoft.jdbc.sqlserver.tds.TDSRequest.processErrorToken(Unknown Source) at
com.microsoft.jdbc.sqlserver.tds.TDSRequest.processReplyToken(Unknown Source) at
com.microsoft.jdbc.sqlserver.tds.TDSRPCRequest.processReplyToken(Unknown Source) at com.microsoft.jdbc.sqlserver.tds.TDSRequest.processReply(Unknown Source) at
com.microsoft.jdbc.sqlserver.SQLServerImplStatement.getNextResultType(Unknown Source) at com.microsoft.jdbc.base.BaseStatement.commonExecute(Unknown Source) at com.microsoft.jdbc.base.BaseStatement.executeInternal(Unknown Source) at com.microsoft.jdbc.base.BasePreparedStatement.execute(Unknown Source) at weaver.conn.RecordSet.executeProc(RecordSet.java:155) at weaver.conn.RecordSet.executeProc(RecordSet.java:109) at weaver.hrm.resource. ResourceComInfo (ResourceComInfo.java:59) at com.caucho.jsp.JavaPage.service(JavaPage.java:87) at com.caucho.jsp.JavaPage.subservice(JavaPage.java:81) at com.caucho.jsp.Page.service(Page.java:410) at com.caucho.server.http.Invocation.service(Invocation.java:319) at
com.caucho.server.http.RunnerRequest.handleRequest(RunnerRequest.java:333) at
com.caucho.server.http.RunnerRequest.handleConnection(RunnerRequest.java:266) at com.caucho.server.TcpConnection.run(TcpConnection.java:140) at java.lang.Thread.run(Thread.java:484)
9 泛微网络有限公司 www.weaver.com.cn
从上面的日志信息,可以看到ResourceComInfo类的59行代码运行的时候出现错误,这个错误是执行数据库操作的脚本错误,具体的错误信息是:形式参数 '@id_1' 定义为 OUTPUT,但实际参数却未声明为 OUTPUT。说明调用数据库脚本的时候参数的数量有出入。
定期检查日志文件,可以发现代码中的各种错误。
泛微网络有限公司www.weaver.com.cn 10
3.3 怎样获取属性文件的值
继承了weaver.general.BaseBean 或weaver.general. DynamicServlet的类,可以直接使用getPropValue方法获取属性文件的值。注意这里是使用,而不是调用,因为这个方法是这两个被继承类中的方法。
注意getPropValue方法的使用: /**
* 从配置文件中获取某个属性的值 * @param fname 属性文件名称 * @param key 值
* @return String 属性值 */
public String getPropValue(String fname , String key)
getPropValue 方法指定了从某一个属性文件fname 中获取键值key 的值。
这里的属性文件必须存放在系统运行目录下的prop 目录下,文件名称为参数fname指定的文件名,不包括属性文件的后缀名,属性文件的后缀名必须为 .properties。
比如:
系统的运行目录为 d:\\ecology\\,那么属性文件必须放在d:\\ecology\\prop\\ 目录下,取名为 thefilename.properties ,其中thefilename是任意的。
在属性文件中某一个键值的值用等号来赋值,等号后面的值必须放在一行,如果一行不够写(或者为了查看的方便),可以用 \\ 来链接多行。否则其它行的值不能被键值取得。等号左右都可以有空格,对键值和键值的值没有影响。
比如 :
thekeyname = thevalue
将键值的值放到多行: thekeyname = thevalue1 \\ thevalue2 \\ thevalue3 thevalue4
这时候thekeyname 的值为thevalue1thevalue2thevalue3 ,thevalue4 取不到,因为thevalue3后面没有 \\
在程序中要取得上述属性文件中键值thekeyname的值,使用方法:
getPropValue(―thefilename‖ , ―thekeyname‖) ;
11 泛微网络有限公司 www.weaver.com.cn
获取属性文件的值的例子如下:
public class ResourceComInfo extends BaseBean {
private void setResourceInfo() throws Exception{
业务处理过程……….
String keyValue = getPropValue(―thefilename‖ , ―thekeyname‖) ;
// 将键值thekeyname的值keyValue记入日志文件
writeLog(\
}
}
小密密:
系统的主属性文件 weaver.properties 的文件名“weaver” 作为系统常量放在weaver.general.GCONST 类中,可以使用getConfigFile() 方法来返回
“weaver ”,在编程的过程中,如果需要用到weaver.properties属性文件中的键值,请用GCONST. getConfigFile() 来获取,当主属性文件名称因为需要改变得时候,不必改变所有用到这个属性文件的类,只需要改变GCONST类中常量的值
12 泛微网络有限公司 www.weaver.com.cn
3.4 怎样获取系统的运行目录
weaver.general.GCONST 类提供了一个静态方法getRootPath() ,返回系统的运行目录,比如系统的运行目录为d 盘的ecology目录,将返回d:\\ecology\\
获取系统的运行目录的例子如下:
public class TestBean extends BaseBean { import weaver.general.GCONST ;
public void getSysRunPath(){
String sysRunPath = GCONST. GetRootPath() ;
// 将系统的运行目录sysRunPath的值记入日志文件
writeLog(\
}
}
泛微网络有限公司 www.weaver.com.cn 13
3.5 怎样访问和运行数据库脚本
这是所有编程人员都关心的一个问题。在ecology系统中,大量的数据库访问,链接的建立和持续性,事务的处理,链接池的维护等问题都被封装在weaver.conn 包下面的各个类中,应用程序的实现者不需要去关心这些问题, 而只需要调用weaver.conn.RecordSet 类来执行各种数据库操作。
weaver.conn.RecordSet 类实现了从数据库链接池中获取链接,执行指定的数据库脚本或者存储过程,并在脚本或者存储过程执行完毕后将链接及时地归还到链接池中。
Ecology系统的链接池管理请参见 weaver.conn.ConnectionPool ,
weaver.conn.DBConnectionPool ,weaver.conn.ConnCheckerTimer 类的API 文档,weaver.conn.ConnectionPool 用于管理ecology系统中的所有数据库链接池(ecology系统可以同时链接多个数据库,每一个数据库均有一个对应的数据库链接池,由weaver.conn.DBConnectionPool负责管理,而
weaver.conn.ConnectionPool则是这些链接池的大管家,负责所有链接池的协调和统一对外接口),weaver.conn.DBConnectionPool用于建立和管理对某一个数据库的链接池,weaver.conn.ConnCheckerTimer用于监控各个链接池的状况,定期对数据库链接池中不符合要求的链接进行清理,并监视是否需要在某一链接池中建立新的链接。
调用weaver.conn.RecordSet,实现对数据的操作,下面进行详细的说明:
weaver.conn.RecordSet类采用 java.sql 中的 CallableStatement 和 Statement 执行数据库操作。客户端直接调用该类进行数据库操作。不需要考虑数据库链接的建立。其中客户端指所有调用该类进行数据库操作的应用程序,不特指用户的客户端。
RecordSet 执行数据库操作有两种形式,一种为调用存储过程,另一种为直接执行SQL语句。与ConnStatement不同 ,RecordSet 执行SQL语句不分查询和修改,都在一条语句中执行。RecordSet执行脚本的方式如下:
1、使用默认的链接池执行SQL语句: RecordSet rs = new RecordSet() ;
rs.executeSql(\while( rs.next() ) {
String thename = rs.getString(\其它处理代码....……
}
2、使用指定的链接池ecologytest执行SQL语句 RecordSet rs = new RecordSet() ;
rs.executeSql(\
14 泛微网络有限公司 www.weaver.com.cn
3、使用指定的链接池ecologytest执行存储过程 PD_Example_UpdateById 存储过程PD_Example_UpdateById 如下:
CREATE PROCEDURE [PD_Example_UpdateById] (@name varchar(100), @id int,
@flag integer output,
@msg varchar(80) output) AS
update TB_Example set name = @name where id = @id
GO
RecordSet rs = new RecordSet() ;
String newname = ....... ; String id = ...... ;
String procpara = newname + Util.getSeparator() + id ;
rs.executeProc( \
procpara 是存储过程的参数值组成的字符串变量,多个参数值之间用 weaver.general.Util.getSeparator() 分开
4、在一个客户程序多个执行之间,查询结果可以保留到下一次查询 RecordSet rs = new RecordSet() ;
rs.executeSql(\
rs.executeSql(\while( rs.next() ) {
String thename = rs.getString(\得到修改前查询的值 其它处理代码....…….
}
rs.executeSql(\while( rs.next() ) {
String thename = rs.getString(\得到修改后查询的值 其它处理代码....……
}
访问和运行数据库脚本的例子:
public class ResourceComInfo extends BaseBean {
泛微网络有限公司 www.weaver.com.cn 15
private void setResourceInfo() throws Exception{
}
业务处理过程……….
String sqlStr = ―select * from Hrmresorce‖ ; RecordSet rt = new RecordSet() ; rt.executeSql(sqlStr) ; while(rt.next()){
String id = Util.null2String(rt.getString(\
String loginid = Util.null2String(rt.getString(\ String lastname = Util.null2String(rt.getString(\ // 将数据库的值记入日志文件 writeLog(\
writeLog(\
writeLog(\ }
}
其它关于系统数据库信息的管理
a)、weaver.conn.ConnectionPool 类的管理和设置:
所有的数据库链接池的集合,用链接池名称区别每一个链接池.支持对一个或多个由属性文件定义的数据库连接池的访问.客户程序可以调用getInstance()方法访问本类的唯一实例。
对于一个链接池集合来说,可以有多个链接池,分别链接不同的数据库。应用程序指定链接池的名称来调用不同的链接池。如果应用程序没有指定链接池的名称,将使用默认的链接池。默认链接池的指定有两种方式:
1、在Application Server设置的初始参数serverName的值 ,以Resin 为例:
2、如果Application Server没有相应设置,则系统使用属性文件(weaver.properties)中设置的默认链接池名称: DefaultPoolName = ecology
16 泛微网络有限公司 www.weaver.com.cn
对于所有的链接池,系统需要加载相应的 JDBC Driver来建立与相应数据库的链接。这些 Driver 在属性文件(weaver.properties)中指定,多个Driver中间用空格隔开:
DriverClasses = com.microsoft.jdbc.sqlserver.SQLServerDriver
对于每一个链接池需要用到的参数在属性文件(weaver.properties)中指定,属性文件中参数的名称以链接池的名称开头,中间加入小数点,比如链接池 ecology 的参数指定为 :
ecology.url = jdbc:microsoft:sqlserver://10.1.1.205:1433;DatabaseName=AIS2002 //db url
ecology.user = sa //db user name ecology.password = 123456 //db user password ecology.charset = ISO_1 //db encode ecology.maxconn = 10 //max conn in conn pool ecology.minconn = 2 //min conn in conn pool ecology.maxusecount = 30 //the max use times of a conn ecology.maxidletime = 30 //the max unuse time of a conn (分)
ecology.maxalivetime = 2 //一个链接被调用后的最大未归回时间 (分) ecology.checktime = 500 //检查的频率 (秒)
b)、weaver.conn. DBConnectionPool类的管理和设置:
数据库链接池,所有创建的链接存放在链接池中,在客户端链接数据库时从链接池中取出链接,客户端在完成数据库操作后将链接返回链接池。
链接池创建链接需要用到的参数在属性文件(weaver.properties)中指定,属性文件中参数的名称以链接池的名称开头,中间加入小数点,比如链接池 ecology 的参数指定为 :
ecology.url = jdbc:microsoft:sqlserver://10.1.1.205:1433;DatabaseName=AIS2002 //db url
ecology.user = sa //db user name ecology.password = 123456 //db user password ecology.charset = ISO_1 //db encode ecology.maxconn = 10 //max conn in conn pool ecology.minconn = 2 //min conn in conn pool ecology.maxusecount = 30 //the max use times of a conn ecology.maxidletime = 30 //the max unuse time of a conn (分)
ecology.maxalivetime = 2 //一个链接被调用后的最大未归回时间 (分) ecology.checktime = 500 //检查的频率 (秒)
17 泛微网络有限公司 www.weaver.com.cn
3.6 如何上传一个文件
上传一个文件只需要调用weaver.file.FileUpload 类。weaver.file.FileUpload 类支持各种文件格式的上传,也支持多文件的上传。
B/S 架构的文件上传采用的是multipart/form-data 协议,而不是HTTP协议,采用这种协议上传的数据必须通过特殊的处理,而不能用常规的方法来获取,否则不能得到数据。weaver.file.FileUpload 封装了底层处理的代码,应用程序的实现者不需要关心这些细节!
weaver.file.FileUpload上传数据的方法有: /**
* 进行上传一个文件的操作
* @param uploadname 需要上传的文件字段名称 * @return String 返回保存文件信息的imagefileid */
public String uploadFiles(String uploadname) /**
* 进行上传一个文件的操作
* @param uploadnames需要上传的多个文件字段名称
* @return String[] 返回保存多个文件信息的imagefileid数组 */
public String[] uploadFiles(String[] uploadnames)
这里,需要注意两点,一个是传给uploadFiles 方法的参数, 是上传文件字段的名称,而不是文件的名称(这个时候你并不知道文件的名称)。比如在jsp 或者html页面中文件浏览的字段代码为:
那么这里的文件字段的名称为\。 第二点是这个方法返回的信息是保存在数据库表 ImageFile 中关于这个文件信息的键值imagefileid ,这个表的结构如下: Column(s) of \Name Datatype Null Option Comment imagefileid int NOT NULL 文件id imagefilename varchar(200) NULL 文件名称 imagefiletype varchar(50) NULL 文件MIME类型 filerealpath varchar(255) NULL 文件存放目录 imagefileused int NULL 文件使用次数 iszip char(1) NULL 是否压缩 0:否 1:是 18 泛微网络有限公司 www.weaver.com.cn
Column(s) of \Name Datatype isencrypt char(1) Null Option NULL Comment 是否加密 0:否 1:是 文件(存在在数据库中的文件内容,现已不使用) imagefile image NULL 从表结构可以看出,我们得到了某一个文件信息的imagefileid,就可以从该表中得到相应的文件名称(这里指文件的实际名称,比如test.doc), 文件MIME类型(比如 txt,doc,gif等) ,文件存放目录(这里指在服务器中存放的实际路径信息,包括实际存放的文件名称,比如
e:\\ecologyfilesystem\\2003\\08\\A\\23143567.zip)。通过这些信息,可以对文件进行操作,当然weaver.file.FileUpload 还提供了其它很多方法来获取文件相关的信息,不需要通过数据表的查询就能得到这些信息。存放在数据表中的信息只是供今后文件处理的时候使用!
上传文件的例子:
首先,我们要创建一个提交数据的页面,可以是jsp 的页面,也可以是html 的页面,我们将数据提交给 weaver.test.MutiFileUpload 的servlet 类来处理:
这是一个文件上传的例子