2005年05月16日

winver———检查windows版本
wmimgmt.msc—-打开windows管理体系结构(wmi)
wupdmgr——–windows更新程序
wscript——–windows脚本宿主设置
write———-写字板
winmsd———系统信息
wiaacmgr——-扫描仪和照相机向导
winchat——–xp自带局域网聊天
mem.exe——–显示内存使用情况
msconfig.exe—系统配置实用程序
mplayer2——-简易widnows media player
mspaint——–画图板
mstsc———-远程桌面连接
mplayer2——-媒体播放机
magnify——–放大镜实用程序
mmc————打开控制台
mobsync——–同步命令


——————————————————————————–

dxdiag———检查directx信息
drwtsn32—— 系统医生
devmgmt.msc— 设备管理器
dfrg.msc——-磁盘碎片整理程序
diskmgmt.msc—磁盘管理实用程序
dcomcnfg——-打开系统组件服务
ddeshare——-打开dde共享设置
dvdplay——–dvd播放器



——————————————————————————–

net stop messenger—–停止信使服务
net start messenger—-开始信使服务
notepad——–打开记事本
nslookup——-网络管理的工具向导
ntbackup——-系统备份和还原
narrator——-屏幕"讲述人"
ntmsmgr.msc—-移动存储管理器
ntmsoprq.msc—移动存储管理员操作请求
netstat -an—-(tc)命令检查接口



——————————————————————————–

syncapp——–创建一个公文包
sysedit——–系统配置编辑器
sigverif——-文件签名验证程序
sndrec32——-录音机
shrpubw——–创建共享文件夹
secpol.msc—–本地安全策略
syskey——系统加密,不能解开,保护windows xp 的双重密码
services.msc—本地服务设置
sndvol32——-音量控制程序
sfc.exe——–系统文件检查器
sfc /scannow—windows文件保护



——————————————————————————–

tsshutdn——-60秒倒计时关机命令
tourstart——xp简介(安装完成后出现的漫游xp程序)
taskmgr——–任务管理器



——————————————————————————–

eventvwr——-事件查看器
eudcedit——-造字程序
explorer——-打开资源管理器



——————————————————————————–

packager——-对象包装程序
perfmon.msc—-计算机性能监测程序
progman——–程序管理器



——————————————————————————–

regedit.exe—-注册表
rsop.msc——-组策略结果集
regedt32——-注册表编辑器
rononce -p —-15秒关机
regsvr32 /u *.dll—-停止dll文件运行
regsvr32 /u zipfldr.dll——取消zip支持



——————————————————————————–

cmd.exe——–cmd命令提示符
chkdsk.exe—–chkdsk磁盘检查
certmgr.msc—-证书管理实用程序
calc———–启动计算器
charmap——–启动字符映射表
cliconfg——-sql server 客户端网络实用程序
clipbrd——–剪贴板查看器
conf———–启动netmeeting
compmgmt.msc—计算机管理
cleanmgr——-垃圾整理
ciadv.msc——索引服务程序




——————————————————————————–

osk————打开屏幕键盘
odbcad32——-odbc数据源管理器
oobe/msoobe /a—-检查xp是否激活
lusrmgr.msc—-本机用户和组
logoff———注销命令




——————————————————————————–

iexpress——-木马捆绑工具,系统自带



——————————————————————————–

nslookup——-ip地址侦测器



——————————————————————————–

fsmgmt.msc—–共享文件夹管理器



——————————————————————————–



utilman——–辅助工具管理器


——————————————————————————–

gpedit.msc—–组策略

2005年05月13日

三层体系结构与数据库编程

作者:XC-SOFT( gxc@263.net )

如转载,请说明出处 http://www.xc-soft.com

源码下载

数据库脚本下载本下载

作者:XC-SOFT( gxc@263.net )

作者:XC-SOFT( gxc@263.net )

如转载,请说明出处 http://www.xc-soft.com

源码下载

数据库脚本下载本下载

作者:XC-SOFT( gxc@263.net )

如转载,请说明出处 http://www.xc-soft.com

源码下载

数据库脚本下载本下载

接要 本文主要介绍了基于三层体系结构的网络数据库设计,并结合面向对象,分布式数据库开发等理论。全文围绕一个典型而简单的例子,通过VB编程语言,从分析、建模、设计、编码等各个角度对三层体系与数据库进行了全面而详细的阐述,文中提供了全部源代码。

关键词  三层体系  数据库  面向对象  分布式开发

1.     三层体系结构

我们经常会看到许多应聘者在简历上写着“精通数据库编程”的字样,也经常会在招聘网站上看到软件公司的招聘要求中某一项为“精通数据库编程”。于是这些应聘者去这些软件公司面试,于是我们看到了许多“精通”者落选的现象。

一些程序员在设计数据库应用时,通常会采用数据控件绑定的方法实现。用鼠标拉几个控件,再用鼠标设置几个属性,连键盘都不用动,就完成了一个数据库应用的开发!当然,这的确是一种快速的数据库应用开发方式,但快速并不意味着精通。

对于大型的数据库应用系统,或是拥有众多客户端的应用系统,我们需要另外一种“精通”,这就是几乎每个程序员都听说过的“三层体系结构”。

1.1. 传统的C/S模式

在传统的数据库应用体系中,客户端与数据库完全分开,在客户端上运行了大部分服务,如数据访问规则、业务规则、合法性校验等等。每一个客户端都存在数据引擎,并且每个客户端与数据库服务器建立独立的数据库连接(DB Connection)。

基于该种体系的数据库应用系统的优势:开发周期较短,能够适应大部分中小型数据库应用系统的要求(当客户端数量少于50时)。

但是,随着数据库应用的日渐发展、数据容量的不断增加、客户端数量的不断增加,该种体系结构显示出了诸多缺陷,主要体现在以下几个方面:

1、              可扩充性:对于数据库服务器端,每当建立一个数据连接,就会占用大量的系统资源,当数据连接达到一定数量(如20个)时,数据库服务器的响应速度与处理速度将大打折扣。

2、              可维护性:基于传统C/S的数据库应用系统,业务规则通常置于客户端应用程序中。如果业务规则一旦发生变化(随便举个例子,如身份证号码有可能升为19位)时,我们就必须修改客户端应用程序,并且将每个客户端进行相应的升级工作。

3、              可重用性:采用传统C/S的设计模式时,数据库访问、业务规则等都固化在客户端应用程序中。如果客户另外提出了B/S的应用需求,则需要在WEB服务器中重新进行数据库访问、业务规则、合法性校验等编码(例如将数据库访问写入ASP代码),而所做的工作与客户端应用程序中的功能完全重复,从而加大了工作量,又使得程序开发者心里感到极不舒服。

正因为以上的诸多缺陷,使得三层(多层)体系结构成为目前数据库应用开发的首选,甚至客户有时也会提出该种技术需求。

1.2. 三层体系结构

所谓三层体系结构,是在客户端与数据库之间加入了一个“中间层”,也叫组件层。这里所说的三层体系,不是指物理上的三层,不是简单地放置三台机器就是三层体系结构,也不仅仅有B/S应用才是三层体系结构,三层是指逻辑上的三层,即使这三个层放置到一台机器上。

三层体系的应用程序将业务规则、数据访问、合法性校验等工作放到了中间层进行处理。通常情况下,客户端不直接与数据库进行交互,而是通过COM/DCOM通讯与中间层建立连接,再经由中间层与数据库进行交互。

这样的好处显而易见:

1、              由于数据访问是通过中间层进行的,因此客户端不再与数据库直接建立数据连接。也就是说,建立在数据库服务器上的连接数量将大大减少。例如一个500个客户端的应用系统,500个客户端分别与中间层服务器建立DCOM连接,而DCOM通讯所占用的系统资源极为有限,并且是动态建立与释放连接,因此客户端数量将不再受到限制。同时,中间层与数据库服务器之间的数据连接通过“连接池”进行连接数量的控制,动态分配与释放数据连接,因此数据连接的数量将远远小于客户端数量。

2、              可维护性得以提高。因为业务规则、合法性校验存在于中间层,因此当业务规则发生改变时,只需更改中间层服务器上的某个组件(如某个DLL文件),而客户端应用程序不需做任何处理,有些时候,甚至不必修改中间层组件,只需要修改数据库中的某个存储过程就可以了。

3、              良好的可重用性。同样,如果需要开发B/S应用,则不必要重新进行数据访问、业务规则等的开发,可以直接在WEB服务器端调用现有的中间层(如可以采用基于IISWebClass开发,或直接编写ASP代码)。

4、              事务处理更加灵活,可以在数据库端、组件层、MTS(或COM+)管理器中进行事务处理。

如果现在你仍然感到不理解,没关系,请看下面的例子。

2.     简单的人事管理系统

下面以一个极为简单的人事管理系统为例详细讲述如何实现三层体系结构。编程语言为Visual Basic 6.0

为了全面介绍程序设计方法,VB代码中采用了不同的方法实现相同的功能,如数据库访问中,同时采用了存储过程与ADO连接。读者可自行选择最适合的方法。

由于在代码中加入了大量注释,因此不再过多地说明函数功能与原理。

在团队开发中,代码中注释部分应占整个代码的1/3左右,而且应在代码编写前就写好注释。如果另一个程序员认为你的代码中注释全部是废话,那么这些注释肯定是在写完代码之后才加上去的!

2.1. 需求

简单的部门/人员管理系统,要求:

1、  部门的属性有部门名称,人员的属性有姓名、年龄、性别;

2、  部门存在上下级关系;

3、  人员必须属于一个部门;

4、  人员、部门需要实现增加、删除、修改功能

5、  可以按人员的名称、年龄查询人员

6、  如果一个部门存在人员,或存在下级部门,则该部门不可删除

以上即为系统的简单需求。

2.2. 数据库

数据库采用SQL Server 7设计,数据库名称为“TEST”,存在两个数据表(此处假设读者已掌握数据库设计,因为这个数据库实在太简单了)。

tDept

字段名称

类型

nID

Int

DeptName

Char(50)

SuperID

Int

tEmployee

字段名称

类型

nID

Int

DeptID

Int

EmpName

Char(10)

EmpAge

Smallint

EmpGender

Bit

 

其中,tDeptnIDSuperID为表内关联。

2.3. 中间层

打开VB6,选择“新建ActiveX DLL”,并引用ADO 2.5

新添加一个模块,命名为mdlPublic,新填加5个类,分别命名为cDeptcEmpcDeptscEmpscPublic。其中,cEmpscDepts分别为cEmpcDept的集合类,cPublic为定义枚举的类,无实际意义。将工程的启动模块设为“Sub Main”(重要!)。

SQL ServerTEST库中,添加一个存储过程AddDept

全部代码如下:

2.3.1.   mdlPublic.bas

Option Explicit

 

Public g_Cn As Connection ‘用于全局的数据连接

 

‘ActiveX DLL的启动程序,为DLL初始化时执行

Public Sub Main()

  If ConnectToDatabase = False Then

    Err.Raise vbObjectError + 1, , "连接数据库出错!"

  End If

End Sub

 

连接到数据库

Public Function ConnectToDatabase() As Boolean

  On Error GoTo ERR_CONN

  Set g_Cn = New Connection

 

  设置服务器名称,数据库名称,登录名(此时假设密码为空)

  Dim ServerName As String, DBName As String, UserName As String

  ServerName = "gxc-notepad"

  DBName = "TEST"

  UserName = "sa"

 

  连接到数据库

  With g_Cn

     .CursorLocation = adUseClient

     .CommandTimeout = 10

     .ConnectionString = "Provider=SQLOLEDB.1;Persist Security Info=True;User ID=" & UserName & ";Initial Catalog=" & DBName & ";Data Source=" & ServerName

     .Open

  End With

  ConnectToDatabase = True

  Exit Function

ERR_CONN:

  ConnectToDatabase = False

End Function

 

去掉字符串中的单引号

Public Function RealString(strOrigional) As String

  RealString = Replace(strOrigional, "’", "")

End Function

 

得到某个数据表中主键的下一个值,即当前主键值加1

Public Function NextID(ByVal strTable As String, ByVal strID As String) As Long

  两个参数分别是表的名称与主键的名称

  Dim rs As Recordset

  Set rs = g_Cn.Execute("SELECT MAX(" & strID & ") FROM " & strTable)

 

  If IsNull(rs(0)) Then

    如果值为NULL,则说明无任何数据记录,此时ID应为1

    NextID = 1

  Else

    使新ID为最大ID+1

    NextID = rs(0).Value + 1

  End If

End Function

 

查看某个数据表中,是否存在某个字段等于某个值的记录(整型)

Public Function ExistByID(ByVal strTable As String, ByVal strID As String, ByVal lngID As Long) As Boolean

  第一个参数为表名,第二个为字段名,第三个为具体的字段值

  Dim rs As Recordset

  Set rs = g_Cn.Execute("Select Count(*) from " & strTable & " where " & strID & "=" & lngID)

  ExistByID = (rs(0).Value = 1)

End Function

 

查看某个数据表中,是否存在某个字段等于某个值的记录(字符型)

Public Function ExistByName(ByVal strTable As String, ByVal strFieldName As String, ByVal strName As String, ByVal ThisID As Long) As Boolean

  第一个参数为表名,第二个为字段名,第三个为具体的字段值

  Dim rs As Recordset

  Set rs = g_Cn.Execute("Select Count(*) from " & strTable & " where " & strFieldName & "=’" & strName & "’ and nID<>" & ThisID)

  ExistByName = (rs(0).Value = 1)

End Function

以上两个函数实际上可以合并,本程序中为了说明问题,故而分开

2.3.2.   cPublic.cls

Option Explicit

该类无实际意义,只为保存一些自定义枚举

 

自定义枚举,用于表示性别

Public Enum gxcGender

    Male = 1

    Female = 0

End Enum

 

””””””””””””””””””””””””””””””’

以下枚举用于“部门”对象的操作

 

用于表示部门删除结果的枚举

Public Enum gxcDelete

  DeleteOK = 0

  DeleteFail = 1 ‘未知原因导致不能删除

  DeleteSubExists = 2 ‘由于存在子部,因此不能删除

  DeleteEmpExists = 3 ‘该部门存在人员,不能删除

End Enum

 

用于表示部门更新结果的枚举

Public Enum gxcUpdate

  UpdateOK = 0

  UpdateFail = 1

  DuplicateName_Update = 2 ‘名字不可重复

  RecordNotExist = 3 ‘当前更新的记录已被其它客户端删除

End Enum

 

用于表示部门新增结果的枚举

Public Enum gxcAddNew

  AddNewOK = 0

  AddNewFail = 1

  DuplicateName_AddNew = 2 ‘名字不可重复

  SuperNotExist = 3 ‘指定的上级部门的ID不存在

End Enum

””””””””””””””””””””””””””””””

2.3.3.   cDept.cls

Option Explicit

 

Private mvarDeptName As String

Private mvarID As Long

Private mvarSuperID As Long

 

””””””””””””””””””””””””””””””

以下为部门的属性

 

上级部门ID

Public Property Let SuperID(ByVal vData As Long)

  mvarSuperID = vData

End Property

Public Property Get SuperID() As Long

  SuperID = mvarSuperID

End Property

 

本部门的ID

Public Property Let ID(ByVal vData As Long)

  mvarID = vData

End Property

Public Property Get ID() As Long

  ID = mvarID

End Property

 

本部门的名称

Public Property Let DeptName(ByVal vData As String)

  vData = Trim(vData) ‘去除两边的空格

 

  控制名称的长度不可大于50

  If Len(vData) > 50 Then vData = Left(vData, 50)

 

  mvarDeptName = vData

End Property

Public Property Get DeptName() As String

  DeptName = mvarDeptName

End Property

属性结束

”””””””””””””””””””””””””””””””

 

 

”””””””””””””””””””””””””””’

以下为方法

 

新增一个部门,并返回操作的结果

Public Function AddNew(Optional strName As String = "", _

                       Optional lngSuperID As Long = -1) As gxcAddNew

  根据传入的参数更新属性值

  On Error GoTo ERR_ADDNEW

 

  如果参数被传入,则以传入的参数更新属性

  If strName <> "" Then Me.DeptName = strName

  If lngSuperID <> -1 Then Me.SuperID = lngSuperID ‘上级部门的ID

 

  通过Command对象调用存储过程,由存储过程

  进行添加部门的操作,并由存储过程返回操作结果

  Dim cmd As ADODB.Command

  Set cmd = New ADODB.Command

  With cmd

    Set .ActiveConnection = g_Cn

    .CommandType = adCmdStoredProc ‘设置Command类型为“存储过程”

    .CommandText = "AddDept" ‘存储过程的名称

   

    传入两个参数,分别为部门的名称与上级部门的ID

    .Parameters.Append .CreateParameter("@Name", adChar, adParamInput, 50, Me.DeptName)

    .Parameters.Append .CreateParameter("@SuperID", adInteger, adParamInput, , Me.SuperID)

   

    传入两个返回型的参数,分别返回新记录的ID与操作结果

    .Parameters.Append .CreateParameter("@ID", adInteger, adParamOutput)

    .Parameters.Append .CreateParameter("@Return", adInteger, adParamOutput)

    .Execute

  End With

 

  Dim RTN As gxcAddNew

  RTN = cmd.Parameters("@Return").Value ‘得到操作结果

 

  如果操作成功,则给对象赋以ID

  If RTN = AddNewOK Then Me.ID = cmd.Parameters("@ID").Value

 

  AddNew = RTN ‘返回操作结果

  Set cmd = Nothing

  Exit Function

ERR_ADDNEW:

  来到这里,则说明出错了

  If Not cmd Is Nothing Then Set cmd = Nothing

  AddNew = AddNewFail

End Function

 

修改部门信息,返回操作结果

Public Function Update() As gxcUpdate

  通过ID判断是否存在该记录,即该记录是否被其它客户端删除

  如果不存在该记录,则返回相应的操作结果给调用者

  If Not ExistByID("tDept", "nID", Me.ID) Then

    Update = RecordNotExist

    Exit Function

  End If

 

  通过名称判断是否存在相同名称的记录,如果存在相同的名称,

  则返回调用者“存在相同名称”的信息

  If ExistByName("tDept", "DeptName", Me.DeptName, Me.ID) Then

    Update = DuplicateName_Update

    Exit Function

  End If

 

  On Error Resume Next

  Dim strSQL As String

  构造SQL语句,注意需调用RealString函数去除字符串中的单引号

  strSQL = "Update tDept Set DeptName=’" & RealString(Me.DeptName) & "’,"

  strSQL = strSQL & "SuperID=" & IIf(Me.SuperID = 0, "null", Me.SuperID)

  strSQL = strSQL & " where nID=" & Me.ID

 

  g_Cn.Execute strSQL ‘执行SQL语句

 

  根据是否出错,返回给调用者相应的信息

  If Err.Number = 0 Then

    Update = UpdateOK

  Else

    Update = UpdateFail

  End If

End Function

 

删除一个部门

Public Function Delete(Optional ByVal lngID As Long = 0) As gxcDelete

  如果调用该函数时传入了ID,则更新该对象的ID

  If lngID <> 0 Then Me.ID = lngID

 

  如果该部门下面有人员,则也不能删除

  If ExistByID("tEmployee", "DeptID", Me.ID) Then

    Delete = DeleteEmpExists

    Exit Function

  End If

 

  如果该部门下有子部门,则不能删除

  If ExistByID("tDept", "SuperID", Me.ID) Then

    Delete = DeleteSubExists

    Exit Function

  End If

 

  On Error Resume Next

  执行删除操作并返回操作结果

  g_Cn.Execute "Delete from tDept where nID=" & Me.ID

  Delete = IIf(Err.Number = 0, DeleteOK, DeleteFail)

End Function

 

得到本部门的所有员工

Public Function Employees() As cEmps

  Dim objEmps As New cEmps

  调用cEmps类的Find方法,只传第三个参数,即“部门ID

  Set Employees = objEmps.Find(, , Me.ID)

End Function

 

得到本部门的所有子部门

Public Function SubDepartments() As cDepts

  Dim objDepts As New cDepts

  调用cDeptsFind方法,通过上级部门的ID查找

  Set SubDepartments = objDepts.Find(, Me.ID)

End Function

 

得到本部门的上级部门,以对象返回

Public Function SuperDepartment() As cDept

  Dim objDepts As New cDepts

  调用cDeptsFind方法,将该类的“SuperID”作为查找条件

  从而查找出其上级部门

  objDepts.Find Me.SuperID

  If objDepts.Count > 0 Then Set SuperDepartment = objDepts.Item(1)

End Function

方法结束

”””””””””””””””””””””””””””’

2.3.4.   cDepts.cls

Option Explicit

 

Private mCol As Collection

 

往集合中加入一个“部门”对象

Public Sub Add(objDept As cDept)

  mCol.Add objDept, "A" & objDept.ID

  在加入对象是,最好同时加入其“KEY”属性

  KEY”属性不可以是数字型,因此在前面随便加

  一个字母,此处加了一个“A

End Sub

 

Public Property Get Item(vntIndexKey As Variant) As cDept

  Set Item = mCol(vntIndexKey)

End Property

 

Public Property Get Count() As Long

  Count = mCol.Count

End Property

 

Public Sub Remove(vntIndexKey As Variant)

  mCol.Remove vntIndexKey

End Sub

 

Public Property Get NewEnum() As IUnknown

  本属性允许用 For…Each 语法枚举该集合。

  Set NewEnum = mCol.[_NewEnum]

End Property

 

清除集合中的全部元素

Public Sub Clear()

  注意!在清除时必须倒序清除,否则要出错!

  Dim i As Long

  For i = mCol.Count To 1 Step -1

    mCol.Remove i

  Next i

End Sub

 

Private Sub Class_Initialize()

  Set mCol = New Collection

End Sub

 

Private Sub Class_Terminate()

  Set mCol = Nothing

End Sub

 

按条件查找部门,以集合类的方式返回

Public Function Find(Optional lngID As Long = 0, Optional lngSuperID As Long = -1) As cDepts

  按输入的参数查询,并返回一个集合类

  Dim strSQL As String

 

  构造SQL语句

  strSQL = "Select * from tDept where "

  If lngID <> 0 Then strSQL = strSQL & "nID=" & lngID & " and "

  If lngSuperID <> -1 Then

    If lngSuperID = 0 Then ‘如果传入0,则表示没有上级部门

      strSQL = strSQL & "SuperID is null and "

    Else

      strSQL = strSQL & "SuperID=" & lngSuperID & " and "

    End If

  End If

  strSQL = strSQL & "nID>0"

 

  清空当前集合

  Me.Clear

 

  Dim rs As Recordset

  Set rs = g_Cn.Execute(strSQL)

 

  往集合中添加查询结果

  Dim i As Long

  Dim objDept As cDept

  For i = 1 To rs.RecordCount

    Set objDept = New cDept

    With objDept

      .ID = rs("nID").Value

      .DeptName = Trim(rs("DeptName").Value)

      .SuperID = IIf(IsNull(rs("SuperID").Value), 0, rs("SuperID").Value)

    End With

    Me.Add objDept

    Set objDept = Nothing

    rs.MoveNext

  Next i

 

  Set rs = Nothing

  Set Find = Me

End Function

2.3.5.   cEmp.cls

Option Explicit

 

Private mvarID As Long

Private mvarEmpName As String

Private mvarEmpAge As Integer

Private mvarEmpGender As gxcGender

Private mvarDeptID As Long

Private mvarDeptName As String

 

””””””””””””””””””””””””””””””

以下为类的属性

 

部门名称

Public Property Let DeptName(ByVal vData As String)

  mvarDeptName = vData

End Property

Public Property Get DeptName() As String

  DeptName = mvarDeptName

End Property

 

部门ID

Public Property Let DeptID(ByVal vData As Long)

  mvarDeptID = vData

End Property

Public Property Get DeptID() As Long

  DeptID = mvarDeptID

End Property

 

性别

Public Property Let EmpGender(ByVal vData As gxcGender)

  mvarEmpGender = vData

End Property

Public Property Get EmpGender() As gxcGender

  EmpGender = mvarEmpGender

End Property

 

年龄

Public Property Let EmpAge(ByVal vData As Integer)

  If vData < 0 Then vData = 1 ‘年龄不可小于0

  mvarEmpAge = vData

End Property

Public Property Get EmpAge() As Integer

  EmpAge = mvarEmpAge

End Property

 

姓名

Public Property Let EmpName(ByVal vData As String)

  vData = Trim(vData) ‘去除两边的空格

 

  控制名称的长度不可大于10

  If Len(vData) > 10 Then vData = Left(vData, 10)

 

  mvarEmpName = vData

End Property

Public Property Get EmpName() As String

  EmpName = mvarEmpName

End Property

 

‘ID

Public Property Let ID(ByVal vData As Long)

  mvarID = vData

End Property

Public Property Get ID() As Long

  ID = mvarID

End Property

属性结束

”””””””””””””””””””””””””””’

 

”””””””””””””””””””””””””””’

以下为方法

 

添加一个人员

Public Function AddNew(Optional ByVal strName As String = "", _

                       Optional ByVal intAge As Integer = 0, _

                       Optional varGender As gxcGender = -1, _

                       Optional lngDeptID As Long = 0) As Boolean

  On Error Resume Next

 

  如果参数为缺省值,即未传入,则直接调和类中的参数,否则调用传入的参数

  If strName <> "" Then Me.EmpName = strName

  If intAge <> 0 Then Me.EmpAge = intAge

  If varGender <> -1 Then Me.EmpGender = varGender

  If lngDeptID <> 0 Then Me.DeptID = lngDeptID

 

  Dim strSQL As String

 

  g_Cn.BeginTrans

  开始一个事务,以免费得到的ID值已被其它客户端所使用

  此处调用NextID方法,得到该类对应的数据表的下一个ID,即最大ID+1

  Me.ID = NextID("tEmployee", "nID")

 

  构造SQL语句,注意需调用RealString去除字符串中的单引号

  strSQL = "Insert into tEmployee (nID,DeptID,EmpName,EmpAge,EmpGender) values ("

  strSQL = strSQL & Me.ID & "," & Me.DeptID & ","

  strSQL = strSQL & "’" & RealString(Me.EmpName) & "’,"

  strSQL = strSQL & Me.EmpAge & "," & Me.EmpGender & ")"

 

  执行SQL语句,并提交事务

  g_Cn.Execute strSQL

  g_Cn.CommitTrans

 

  如果发生错误,则返回FALSE,表示未成功添加

  AddNew = (Err.Number = 0)

End Function

 

修改人员信息

Public Function Update() As Boolean

  On Error Resume Next

  Dim strSQL As String

 

  构造SQL语句

  strSQL = "Update tEmployee set DeptID=" & Me.DeptID & ","

  strSQL = strSQL & "EmpName=’" & RealString(Me.EmpName) & "’,"

  strSQL = strSQL & "EmpAge=" & Me.EmpAge & ","

  strSQL = strSQL & "EmpGender=" & Me.EmpGender & " "

  strSQL = strSQL & "Where nID=" & Me.ID

 

  g_Cn.Execute strSQL

 

  如果发生错误,则返回FALSE,表示未成功更新

  Update = (Err.Number = 0)

End Function

 

删除人员资料

Public Function Delete(Optional ByVal lngID As Long = 0) As Boolean

  Dim strSQL As String

  On Error Resume Next

 

  如果已传入了要删除的ID,则按此ID删除

  If lngID <> 0 Then Me.ID = lngID

 

  strSQL = "DELETE FROM tEmployee WHERE nID=" & Me.ID

 

  g_Cn.Execute strSQL

 

  如果发生错误,则返回FALSE,表示未删除成功

  Delete = (Err.Number = 0)

End Function

方法结束

”””””””””””””””””””””””””””’

 

将某个人员移到指定的部门

Public Function AssignToDepartment(ByVal DeptID As Long) As Boolean

  实现很简单,将部门ID变一下,然后调用Update方法就行了

  Me.DeptID = DeptID

  AssignToDepartment = Me.Update

End Function

 

得到该人员所在部门,以对象返回

Public Function Department() As cDept

  Dim objDepts As New cDepts

  调用cDeptsFind方法,得到部门

  objDepts.Find Me.DeptID

  If objDepts.Count > 0 Then Set Department = objDepts.Item(1)

End Function

2.3.6.   cEmps.cls

Option Explicit

 

Private mCol As Collection ‘局部变量,保存集合

 

将一个“人员”对象加入集合

Public Sub Add(objEmp As cEmp)

  mCol.Add objEmp, "A" & objEmp.ID

  在加入对象时,最好同时加入其“KEY”属性

  KEY”属性不可以是数字型,因此在前面随便加

  一个字母,此处加了一个“A

End Sub

 

Public Property Get Item(vntIndexKey As Variant) As cEmp

  Set Item = mCol(vntIndexKey)

End Property

 

Public Property Get Count() As Long

  Count = mCol.Count

End Property

 

Public Sub Remove(vntIndexKey As Variant)

  mCol.Remove vntIndexKey

End Sub

 

Public Property Get NewEnum() As IUnknown

  本属性允许用 For…Each 语法枚举该集合。

  Set NewEnum = mCol.[_NewEnum]

End Property

 

清除集合中的全部元素

Public Sub Clear()

  清除时应倒序清除!

  Dim i As Long

  For i = mCol.Count To 1 Step -1

    mCol.Remove i

  Next i

End Sub

 

Private Sub Class_Initialize()

  Set mCol = New Collection

End Sub

 

Private Sub Class_Terminate()

  Set mCol = Nothing

End Sub

 

按条件查找人员,以集合类的方式返回

Public Function Find(Optional ByVal lngID As Long = 0, _

                     Optional ByVal strName As String = "", _

                     Optional ByVal lngDeptID As Long = 0) As cEmps

                    

  构造查询SQL

  Dim strSQL As String

  strSQL = "Select tEmployee.*,tDept.DeptName from tEmployee left outer join tDept "

  strSQL = strSQL & " ON tDept.nID=tEmployee.DeptID Where "

 

  If lngID <> 0 Then strSQL = strSQL & "tEmployee.nID=" & lngID & " and "

  如果是按名称查询,则采用“包含”的查询方法

  If strName <> "" Then strSQL = strSQL & "tEmployee.EmpName like’%" & RealString(strName) & "’% and "

  If lngDeptID <> 0 Then strSQL = strSQL & "tEmployee.DeptID=" & lngDeptID & " and "

  strSQL = strSQL & "tEmployee.nID>0"

 

  将查询结果加入集合类

  Dim rs As Recordset

  Set rs = g_Cn.Execute(strSQL)

  Dim i As Long

  Dim objEmp As cEmp

  For i = 1 To rs.RecordCount

    Set objEmp = New cEmp

    With objEmp

      .ID = rs("nID").Value

      .EmpName = Trim(rs("EmpName").Value)

      .EmpAge = rs("EmpAge").Value

      .EmpGender = Abs(rs("EmpGender").Value)

      .DeptID = rs("DeptID").Value

      .DeptName = Trim(rs("DeptName").Value)

    End With

    Me.Add objEmp

    Set objEmp = Nothing

    rs.MoveNext

  Next i

 

  Set rs = Nothing

  Set Find = Me

End Function

2.3.7.   AddDept存储过程

CREATE PROCEDURE AddDept

  @Name char(50),

  @SuperID int,

  @ID int output,

  @Return int output

AS

  begin transaction

 

  如果上级部门ID0,则在些将其设为NULL,表示无上级部门

  if @SuperID=0 Select @SuperID=Null 

 

  当前的ID为最大ID+1

  Select @ID=(Select Max(nID) from tDept)+1

  如果ID值为空,则表示尚无记录,人为地赋值为1

  if @ID is null select @ID=1

 

  如果存在相同的部门名称,则返回VB代码中定义的枚举类型

  if Exists(Select * from tDept where DeptName=@Name) begin

    select @Return=2

    rollback transaction

    return

  end

 

  如果不存在指定的上级部门ID,则返回VB中指定的枚举类型

  if not Exists(Select * from tDept where nID=@SuperID) and not(@SuperID is null) begin

    select @Return=3

    rollback transaction

    return

  end

 

  insert into tDept (nID,SuperID,DeptName) values (@ID,@SuperID,@Name)

 

  if @@error=0 begin

    select @Return=0

    commit transaction

  end else begin

    Select @Return=1

    rollback transaction

  end

2.3.8.   组件设计注意事项

至此,你可以仔细研究一下上面的代码,主要是两个基本类(人员对象与部门对象),两个集合类(人员集合与部门集合)。在这里,你可以将集合理解为“对象的数组”。

然后,仔细分析一下这四个类的结构、接口、相互关系,然后将它们画出来(请一定这样做一下,它会有助于你更好地理解面向对象)。你是不是发现,还可以再加入新的接口函数?当然是的!因为本文中的代码仅仅是个示例,它们有待于你的继续完善,比如你可以将“发工资”封装到“人员”类中。

将上述代码保存为myCom.vbp并编译,生成myCom.dll文件。该DLL文件即是一个中间层组件。

在此组件中,我们加入了大量的业务规则,如“年龄不可小于0”、不能删除有子部门或上级部门、部门内有人员时不可删除、部门名称不可大于50个字符等等。

在进行任何程序设计时,都必须考虑到用户使用的方便性。比如设计应用程序时,我们总是在考虑如何让直接用户更为方便地操作,如果使得操作逻辑更为用户所接受。

同样地,COM组件的设计也应为用户做相同考虑,如何让用户更加方便地使用。COM组件的用户不是最终用户,而是程序员! 是制作交互界面的程序员!因此在设计COM接口与结构时,应充分考虑到界面程序员的思维方式与使用方便性,例如函数应以表义性较强的字母组合命名等等。

最完美的状态是这样:使用你的COM组件的程序员心里想着:应该有这样的一个函数吧,并且名字应该是GetCustomerName,于是他真的在你的组件中发现了这个函数,而且函数名称,甚至输入参数都与他想象的完全一样,那么,你真的成功了!

COM组件编写完成后,应经过大量测试,测试到每一个函数与属性。可以编写简单的测试程序进行测试(有时为了节省时间,可以直接在界面中进行测试,但可能公增加程序员的沟通时间,有时反而会得不偿失)。

2.4. 客户端

既然COM组件(或中间层)已编写完成并通过测试,下面就可以进行界面的编写了。

很有趣的是,采用基于三层体系结构的设计模式,界面程序员可以完全不懂数据库编程!他完全不必知道数据库的格式,甚至不必了解是何种类型的数据库。

请看以下的例子:

首先,新建一个工程,然后引用myCom.DLL

2.4.1.   先举几个例子

2.4.1.1.     添加一个部门

  Dim objDept As New cDept ‘定义一个部门对象

  Dim Result As gxcAddNew, strResult As String

  With objDept

    .DeptName = "总部"

    .SuperID = 0 ‘0表示无上级部门

    Result = .AddNew ‘得到操作结果

    If Result = AddNewFail Then

      strResult = "添加失败!"

    ElseIf Result = DuplicateName_AddNew Then

      strResult = "存在相同名称的部门,请修改名称后重新添加!"

    ElseIf Result = SuperNotExist Then

      strResult = "指定的上级部门不存在或已被删除!"

    Else

      strResult = "添加成功!"

    End If

  End With

  MsgBox strResult, vbInformation

通过上面的代码,已完成了“增加一个部门”的操作,并且可以清楚地知道操作的结果。而代码中没有任何地方体现出这是对数据库进行编程。

上面代码中With块中的前三行还可以用下面的一行代码替换(因为你的AddNew函数中的参数全部都是可选的):

    Result = .AddNew("总部", 0)

2.4.1.2.     删除一个部门

  Dim objDept As New cDept ‘定义部门对象

  Dim Result As gxcDelete, strResult As String

  Result = objDept.Delete(1) ‘删除ID1的部门

  If Result = DeleteEmpExists Then

    strResult = "该部门内存在人员,不能删除!"

  ElseIf Result = DeleteFail Then

    strResult = "删除失败!"

  ElseIf Result = DeleteSubExists Then

    strResult = "该部门内存在子部门,不能删除!"

  Else

    strResult = "成功删除"

  End If

  MsgBox strResult, vbInformation

2.4.1.3.     查询所有子部门与部门内人员

  以下代码查找出ID12的部门,然后得到该部门下的所有人员与所有子部门。

Dim objDepts As New cDepts, objEmps As New cEmps ‘定义部门集合与人员集合

  If objDepts.Find(12).Count > 0 Then

    Set objEmps = objDepts(1).Employees ‘得到了部门内所有人员

    Set objDepts = objDepts(1).SubDepartments ‘得到了部门内的所有子部门

  End If

2.4.1.4.     更为有趣的操作

以下代码查找出名称中包含“张三”的第一个人员,然后找出同部门的所有同事。

  Dim objEmps As New cEmps

  If objEmps.Find(, "张三").Count > 0 Then

    得到了同一部门的所有人员

    Set objEmps = objEmps(1).Department.Employees

  End If

以下代码查看张三是否是李四的直接上司。

  On Error Resume Next

  Dim objEmps As New cEmps

  If objEmps.Find(, "张三").Item(1).Department Is objEmps.Find(, "李四").Item(1).Department.SuperDepartment Then

    MsgBox "张三是李四的顶头上司!"

  End If

以上的代码在实际编程中可能很少用到,或者永远不可能用到,但这也从另一个方面反映了组件开发的灵活性。

看到这,如果你还感觉不理解的话,请随便买一本VB初级入门的书,仔细研究研究。

2.4.2.   详细的界面例子

打开VB,新建一个工程。引用刚才生成的myCom.dll,加入微软通常控件(Common Control 6.0)。

添加一个窗口frmMain,加入一个Treeviw,用于显示分级显示的部门与人员,命名为tvwShow

加入一个ListView,用于显示人员的列表,命名为lvwEmp

加入六个按钮,分别用于部门/人员的增、改、删(为了更好地说明问题,特意加入六个按钮,在实际开发中没这么麻烦),分别命名为cmdAddDept, cmdEditDept, cmdDeleteDept, cmdAddEmp, cmdEditEmp, cmdDeleteEmp

加入一个图像列表,加入三个具有表义性的图标,其Key属性分别为“O”,“D”,“E”,用于根节点、部门、人员的图标。并将tvwShow的图像列表设为该控件。

2.4.2.1.     显示部门、人员到树型图

加入以下代码,实现部门与人员的加载。

将所有部门加入树型图

Private Sub DepartmentToTreeview(ByRef tvw As TreeView)

  Dim objDepts As New cDepts

  Dim i As Long

  先加入没有上级部门的部门

  objDepts.Find , 0

  Dim Nd As Node

  Set Nd = tvw.Nodes.Add(, , "O0", "所有部门", "O") ‘加入原始根节点。“O0”中,第一个为字母O,第二个为数字0

  Nd.Expanded = True

 

  For i = 1 To objDepts.Count

    加入没有上级部门的部门节点,图形列表ID为“D

    Set Nd = tvw.Nodes.Add("O0", tvwChild, "A" & objDepts(i).ID, objDepts(i).DeptName, "D")

    Nd.Expanded = True

    加载其下级部门节点

    LoadSubNodes tvw, Nd, objDepts(i).ID

  Next i

End Sub

 

调用递归,显示树型的部门结构

Private Sub LoadSubNodes(ByRef tvw As TreeView, Nd As Node, NodeID As Long)

  Dim Nd1 As Node

  Dim objDepts As New cDepts

  Dim i As Long

  objDepts.Find , NodeID ‘找到部门的所有子部门

  For i = 1 To objDepts.Count

    Set Nd1 = tvw.Nodes.Add(Nd, tvwChild, "A" & objDepts(i).ID, objDepts(i).DeptName, "D")

    Nd1.Expanded = True

    递归加载下级部门…..

    LoadSubNodes tvw, Nd1, objDepts(i).ID

  Next i

End Sub

 

将人员加入到树型图,树型图中已有部门节点

Private Sub EmployeeToTreeview(ByRef tvw As TreeView)

  On Error Resume Next ‘该代码为了防止错误而加入,实际编程中需要做判断,本处为了说明问题。

  Dim objEmps As New cEmps

  objEmps.Find ‘找到所有的人员

  Dim i As Long

  For i = 1 To objEmps.Count

    AddEmpToTvw objEmps(i), tvw

  Next i

End Sub

 

本来EmployeeToTreeview一个函数就可以完成“加入人员到树型图”,但

考虑到在单独新增人员时需用到下面的函数,因此将下面的代码单独提取

出来,做了一个单独的函数。(详见后面的代码)

将一个人员加入到树型图中,显示到相应的部门下面

Private Sub AddEmpToTvw(ByVal objEmp As cEmp, ByRef tvw As TreeView)

  On Error Resume Next

  tvw.Nodes.Add "A" & objEmp.DeptID, tvwChild, "B" & objEmp.ID, objEmp.EmpName, "E"

End Sub

 

将一个部门加入到树型图中

Private Sub AddDeptToTvw(ByVal objDept As cDept, ByRef tvw As TreeView)

  On Error Resume Next

  If objDept.SuperID = 0 Then

    O0”中,第一个为字母O,第二个为数字0

    tvw.Nodes.Add "O0", tvwChild, "A" & objDept.ID, objDept.DeptName, "D"

  Else

    tvw.Nodes.Add "A" & objDept.SuperID, tvwChild, "A" & objDept.ID, objDept.DeptName, "D"

  End If

End Sub

Form_Load事件中加入如下代码:

Private Sub Form_Load()

  DepartmentToTreeview tvwShow ‘将部门显示到树型图中

  EmployeeToTreeview tvwShow ‘将人员也加入到相同的树型图中

End Sub

此时,你可以手工在数据库中加入一些记录,然后运行程序。你会发现这些代码已实现了部门与人员的显示。

在上面的代码中,你仍然未看出任何数据库编程的特征。

2.4.2.2.     人员显示到列表框

以下代码实现了将人员显示到列表框的功能,参看代码中备注。

按照“人员”类的结构,设置ListView的显示样式

Public Sub InitEmployeeListview(ByRef lvw As ListView)

  With lvw

    .View = lvwReport

    .LabelEdit = lvwManual

    .GridLines = True

 

    .ColumnHeaders.Clear

    加入四个列首

    .ColumnHeaders.Add , , "姓名", 1000

    .ColumnHeaders.Add , , "所属部门", 2000

    .ColumnHeaders.Add , , "年龄", 800

    .ColumnHeaders.Add , , "性别", 700

  End With

End Sub

 

将人员集合显示到ListView

Public Sub EmployeesToListview(ByVal objEmps As cEmps, ByRef lvw As ListView)

  传入参数为人员的集合类与列表框

  Dim i As Long

 

  如果列表还未初始化,则初始化之(你可以采用其它方法判断是否初始化,这里是个笨办法)

  If lvw.ColumnHeaders.Count = 0 Then InitEmployeeListview lvw

  lvw.ListItems.Clear ‘清除当前的列表内容

 

  For i = 1 To objEmps.Count

    将每个“人员”都加入到该列表中,调用了单独的函数,没有全部做到这

    个函数中,为什么呢?参看AddEmpToLvw函数

    AddEmpToLvw objEmps.Item(i), lvw, False

  Next i

End Sub

 

将单个人员加入列表,或在列表中更新

特意将该函数单独做出来,而没有将本函数中的代码完全在EmployeesToListview函数中实现

‘Why?

因为在设计该功能时,你还应考虑到在以后的编程过程中,很可能要用到

将某个单独的“人员”对象加入列表框(比如新增加了一个人员)。

Public Sub AddEmpToLvw(ByVal objEmp As cEmp, ByRef lvw As ListView, ByVal IsOverWrite As Boolean)

  第三个参数如果是TRUE,则说明是更新当前已存在的某个列表项,否则是新加一个列表项

  Dim Itm As ListItem

  If IsOverWrite Then

    Set Itm = lvw.SelectedItem

    If Itm Is Nothing Then Exit Sub

  Else

    Set Itm = lvw.ListItems.Add(, "A" & objEmp.ID)

  End If

  With objEmp

    Itm.Text = .EmpName

    Itm.SubItems(1) = .DeptName

    Itm.SubItems(2) = .EmpAge

    Itm.SubItems(3) = IIf(.EmpGender = Female, "", "")

  End With

  Set Itm = Nothing

End Sub

Form_Load中加入以下代码行(使之成为第一行代码):

InitEmployeeListview lvwEmp ‘初始化列表

到此为止,我们已完成了基本的显示操作,下来一个问题是:当你选中了一个树型图节点后(比如一个部门节点),如何才能实例化这个对象,即从界面中取得对象?请继续看。

2.4.2.3.     从控件中取回对象

在上面的代码中,我们看到,将对象加入控件时,如果控件是树型图,我们将节点的Key值设为字母“A+对象的ID”(对于根节点是字母O+数字0,对于部门节点是字母A,人员节点是字母B,这样做是为了防止Key重复),如果控件是列表框,将列表项的Key值也设为相同的值。

这样,可以通过Key属性取回其ID值。因此再加入以下一个函数,取回ID值。

得到某个节点或列表项所表示的对象的实际ID,如“A1”,则得到1,“B2”,则得到2

Private Function GetID(strKey As String) As Long

GetID = Val(Right(strKey, Len(strKey) – 1))

End Function

再加入以下几个函数,函数功能与原理参看代码注释(别担心,很简单的)。

从列表或树型图中中得到一个人员对象

Public Function GetEmpFromControl(ByVal ctl As Object, ByRef objEmp As cEmp) As Boolean

  如果列表中没有被选择的项,则直接退出

  If ctl.SelectedItem Is Nothing Then

    GetEmpFromControl = False

    Exit Function

  End If

 

  Dim objEmps As New cEmps

  Dim ID As Long

  去除控件中节点或列表项的KEY属性前的字母“A”,即为该人员的ID

  ID = GetID(ctl.SelectedItem.Key)

 

  On Error Resume Next ‘为了防止未查找到,因此加入了错误判断语句

  Set objEmp = objEmps.Find(ID).Item(1)

  GetEmpFromControl = (Err.Number = 0)

End Function

 

从树型图中得到部门对象

Public Function GetDeptFromTreeview(ByVal tvw As TreeView, ByRef objDept As cDept) As Boolean

  If tvw.SelectedItem Is Nothing Then Exit Function

 

  Dim objDepts As New cDepts

  按选择的节点的KEY查找对象

  If objDepts.Find(GetID(tvw.SelectedItem.Key)).Count = 0 Then Exit Function

  On Error Resume Next ‘为了防止未查找到,因此加入了错误判断语句

  Set objDept = objDepts.Item(1)

  GetDeptFromTreeview = (Err.Number = 0)

End Function

以上函数的用法见后面的代码。

2.4.2.4.     部门的增、删、改

因为部门、人员都存在于一个树型图中,因此用户点击不同的节点后应有不同的操作功能,参看以下代码。

Private Sub tvwShow_NodeClick(ByVal Node As MSComctlLib.Node)

  Dim Flag As String

Flag = Left(Node.Key, 1) ‘得到当前选择的节点类型

 

  将所有按钮设为不可用

  Dim ctl As Control

  For Each ctl In Controls

    If TypeOf ctl Is CommandButton Then ctl.Enabled = False

  Next

 

  Select Case Flag

    选择了根节点,此时加以增加部门

    Case "O"

      cmdAddDept.Enabled = True

    Case "A"

      选择了部门节点,此时可增、删、改部门与增人员

      cmdAddDept.Enabled = True

      cmdEditDept.Enabled = True

      cmdDeleteDept.Enabled = True

      cmdAddEmp.Enabled = True

 

      显示该部门下的所有人员到列表框中

      此处纯粹是为了演示,实际应用情况可能会有更多要求

      Dim objEmps As New cEmps

      objEmps.Find , , GetID(Node.Key)

      EmployeesToListview objEmps, lvwEmp

    Case "B"

      选择了人员节点,此时可删除、修改人员

      cmdEditEmp.Enabled = True

      cmdDeleteEmp.Enabled = True

  End Select

End Sub

下面演示如何实现部门的增加、修改与删除功能。注意,因为部门只有一个“部门名称”属性,因此我们可以用输入框进行部门的编辑。

Private Sub cmdAddDept_Click()

  增加部门

  Dim strName As String

  strName = Trim(InputBox("请输入部门名称:"))

  If strName = "" Then Exit Sub

 

  Dim objDept As New cDept

  Dim Result As gxcAddNew

  Result = objDept.AddNew(strName, GetID(tvwShow.SelectedItem.Key))

  If Result = AddNewOK Then

    将部门加入树型图

    AddDeptToTvw objDept, tvwShow

  ElseIf Result = DuplicateName_AddNew Then

    MsgBox "有重名的部门存在,重新命名!"

  Else

    MsgBox "失败!"

  End If

End Sub

 

Private Sub cmdDeleteDept_Click()

  删除部门

  If MsgBox("真的要删除?", vbQuestion + vbYesNo + vbDefaultButton2) = vbNo Then Exit Sub

  Dim objDept As cDept

  If GetDeptFromTreeview(tvwShow, objDept) = False Then Exit Sub

 

  Dim Result As gxcDelete

  Result = objDept.Delete

  If Result = DeleteEmpExists Then

    MsgBox "存在人员,不能删除"

  ElseIf Result = DeleteSubExists Then

    MsgBox "存在子部门,不能删除"

  ElseIf Result = DeleteFail Then

    MsgBox "删除失败!"

  Else

    来到这,说明删除成功,从树型图中删除节点

    tvwShow.Nodes.Remove tvwShow.SelectedItem.Index

    RefreshButton

  End If

End Sub

 

Private Sub cmdEditDept_Click()

  编辑部门

  Dim objDept As cDept

  If GetDeptFromTreeview(tvwShow, objDept) = False Then Exit Sub

 

  Dim strName As String

  缺省显示原部门的部门名称

  strName = Trim(InputBox("请输入新的部门名称:", , objDept.DeptName))

  If strName = "" Then Exit Sub

 

  Dim Result As gxcUpdate

  objDept.DeptName = strName

  Result = objDept.Update

  If Result = UpdateOK Then

    将部门加入树型图

    tvwShow.SelectedItem.Text = objDept.DeptName

  ElseIf Result = DuplicateName_Update Then

    MsgBox "有重名的部门存在,重新命名!"

  Else

    MsgBox "失败!"

  End If

End Sub

再加入下面的一个函数。

Private Sub RefreshButton()

  刷新界面上的六个按钮。

  为什么要这样做呢?比如:

  你现在选择了一个“人员”节点,此时你可以点击“修改人员”按钮。

  但如果你将这个人员删除,此时树型图中已没有这个人员节点,而被

  选择的可能是一个部门节点,此时你的“修改人员”按钮应变为不可用

  状态。因此每当删除人员或部门后,都应调用这个函数

  If tvwShow.SelectedItem Is Nothing Then Exit Sub

  tvwShow_NodeClick tvwShow.SelectedItem

End Sub

试试吧,你可以进行部门的增加、删除、修改了!

2.4.2.5.     人员的增加、删除、修改

为什么将人员与部门分开介绍?我们可以通过一个输入框进行部门的新增与修改工作,但由于人员有许多属性,因此可能需要通过一个单独的窗口实现,例如该窗口中可能有一些文本框,下拉列表框,两个按钮分别用于确认与取消。

面向对象编程的一个特点是整个程序代码中充满了“对象”的概念。比如你需要增加或编辑一个“人员”,而且决定弹出一个单独的窗口进行编辑与显示(如一个模态窗口,名称为fEmp),则该窗口与主窗口间必然要进行数据通讯。

你可能想到编写以下的代码。

 

Private Sub AddNewEmployeeDemo()

  在这个函数中进行“修改一个人员”的操作

  假设在这里已经实例化了一个objEmp对象

  With fEmp’fEmp为编辑人员的模态窗口

    .Show ‘显示编辑窗口

    以下从编辑窗口中取得值

    objEmp.EmpName = .txtName.Text

    objEmp.EmpAge = Val(.txtAge.Text)

    If .cboGender.ListIndex = 0 Then

      objEmp.EmpGender = Female

    Else

      objEmp.EmpGender = Male

    End If

    在下面可能还要判断合法性,比如年龄不能输入字母等等

    ””If 输入不合法 Then

  End With

 

  通过以上代码,我们从“增加/修改人员”的窗口中取得了

  部分数据,从而构造了了一个“人员”对象,即可用于下面的

  增加或删除或修改操作,如:

  If objEmp.Update = True Then

    ‘…..

  Else

    ‘…..

  End If

End Sub

上面的代码当然可以正确运行,但如果在fEmp窗口中多做一些工作,则会使得代码更好看,以下为fEmp窗口的代码:

Option Explicit

 

Private OK As Boolean ‘确定用户按了OK还是CANCEL按钮

Private objEmp As cEmp

Private isAddNew As Boolean ‘这个参数表示该窗口打开是用于新增还是修改

Private DepartmentID As Long ‘所在部门的ID,如果是修改,则这个变量没用

 

Private Sub cmdOK_Click()

  检验是否输入了名字,或是否正确输入了年龄

  If Trim(txtName) = "" Or Not IsNumeric(txtAge) Then

    MsgBox "请输入合法的姓名与年龄"

    Exit Sub

  End If

  OK = True

 

  如果是新增状态,则新建立一个“人员”对象

  If isAddNew Then Set objEmp = New cEmp

 

  给“人员”对象赋值

  objEmp.EmpAge = Val(txtAge)

  objEmp.EmpName = Trim(txtName)

  objEmp.EmpGender = cboGender.ListIndex

 

  如果是新增状态,则设置人员的部门ID

  If isAddNew Then objEmp.DeptID = DepartmentID

 

  Me.Hide

End Sub

 

Private Sub cmdCancel_Click()

  按了取消按钮

  OK = False

  Me.Hide

End Sub

 

Private Sub SetStatus()

  根据是“新增”还是修改,确定显示内容

  If isAddNew Then

    txtName.Text = ""

    txtAge.Text = "20"

    cboGender.ListIndex = 0

  Else

    txtName.Text = objEmp.EmpName

    txtAge.Text = objEmp.EmpAge

    cboGender.ListIndex = objEmp.EmpGender

  End If

End Sub

 

Public Function RetrieveEmp(ByRef oEmp As cEmp, Optional DeptID As Long = -1) As Boolean

  Set objEmp = oEmp

 

  得到所属部门的ID,如果是编辑状态,则此ID没用

  DepartmentID = DeptID

 

  isAddNew = (DeptID <> -1) ‘根据是否传入了“部门ID”来确定是新增还是编辑状态

 

  SetStatus ‘根据新增或编辑状态设置显示内容

 

  Me.Show vbModal

  If OK = False Then Exit Function

 

  Set oEmp = objEmp

  RetriveEmp = True

  Unload Me

End Function

上面即为fEmp窗口的所有代码,该窗口有两个文本框,分别用于姓名与年龄的输入,一个下拉列表框用于性别输入(列表索引刚好与类中定义的枚举一一对应),两个按钮(OKCancel)。

可以看出,该窗口提供了一个唯一入口函数RetrieveEmp,该函数有两个参数,第一个参数为一对象变量,第二个参数是可选参数,表示人员所属的部门ID

这样,我们可以通过下面代码实现修改人员的信息:

 

假设在这里已经实例化了一个objEmp对象

  If fEmp.RetriveEmp(objEmp) = False Then Exit Sub

  If objEmp.Update = True Then

  Else

  End If

我们可以看到,只通过一个函数,即可以完成从“修改”窗口中获取人员信息。不同的是,我们在fEmp窗口中写了大量代码。这就是封装的概念,即我们将fEmp窗口封装成了一个类,用于新增/修改人员信息。该类只有一个入口即RetrieveEmp。如果你还需要在程序的其它地方新增或修改人员信息,只需简单地调用这个函数就行了,而不需要重复编写代码。

甚至,你可以单独做一个函数,如下:

Public Function GetMyEmp(Byref objEmp As cEmp) As Boolean

  这里只是为了举例子,在程序代码中未这样做

  GetMyEmp = fEmp.RetriveEmp(objEmp)

End Sub

下面继续介绍。

frmMain中加入以下代码用于人员的增、删、改:

Private Sub cmdAddEmp_Click()

  新增人员

  Dim objEmp As cEmp

  If fEmp.RetriveEmp(objEmp, GetID(tvwShow.SelectedItem.Key)) = False Then Exit Sub

 

  If objEmp.AddNew = True Then

    AddEmpToTvw objEmp, tvwShow

  Else

    MsgBox "错误"

  End If

End Sub

 

Private Sub cmdDeleteEmp_Click()

  删除人员

  If MsgBox("要删除人员?", vbInformation + vbYesNo + vbDefaultButton2) = vbNo Then Exit Sub

 

  Dim objEmp As cEmp

  If GetEmpFromControl(tvwShow, objEmp) = False Then Exit Sub

 

  If objEmp.Delete = True Then

    tvwShow.Nodes.Remove tvwShow.SelectedItem.Index

    RefreshButton

  Else

    MsgBox "错误"

  End If

End Sub

 

Private Sub cmdEditEmp_Click()

  编辑人员

  Dim objEmp As cEmp

  If GetEmpFromControl(tvwShow, objEmp) = False Then Exit Sub

  If fEmp.RetriveEmp(objEmp) = False Then Exit Sub

 

  If objEmp.Update = True Then

    AddEmpToLvw objEmp, lvwEmp, True

    tvwShow.SelectedItem.Text = objEmp.EmpName

  Else

    MsgBox "错误"

  End If

End Sub

OK!你可以运行整个程序了!

2.4.3.   扩展

上面的例子讲述了如何实现对象与界面的显示与获取。你可能会想到将这些方法封装在类里面,操作可能会更容易些。当然你可以这么做!

但有时候可能没必要这么做,只需在界面端做一个独立的模块用于界面显示操作就可以了,如果中间层与用户界面不在一台机器上,这样的结果可能会加大网络传输量。况且有些客户端可能需要将内容显示到不同的控件中(如网格、下拉列表等等)。

对于VB语言,界面设计实际上可以更为灵活。但不管采用哪一种方式,始终注意一点:你所做的东西应该让你的客户用起来舒服!

比如上面的fEmp窗口,只提供了一个函数接口,该窗口封装了大量代码(当然你还可以将该窗口做得更健壮)。记住,当你做这个窗口时,你的用户是其它程序员—-其它调用该窗口的程序员,因此,多多为他们考虑一下,如何才能让他们调用起来更为方便。当你真正做到了这一点,你将是一个真正“具有团队精神”的程序员!

记住,对于这些封装性很强的代码,尽量一次做好,全面测试通过,然后永远将其抛到脑后!

2.5. 扩展为B/S

一旦做好了“部门”与“人员”两个类,我们可以在程序的任何地方使用其接口,而不用多次编写重复的代码这也是为何在组件中编写了大量代码的原因。

现在,如果要做一个B/S版本的程序,工作就简单多了。既然有了中间层组件,而且组件中包含了全部的业务逻辑与接口,因此在ASP代码中(假设采用ASP开发)可以直接使用组件中提供的各种对象和接口,不必为建立数据库连接、记录集的返回、合法性较验而做过多的重复工作。下图显示了这种可重用性的原理。

3.     总结

通过上述例子可以看出,在中间层的开发过程中编写了大量代码,而且界面中的代码量也很吓人。实际上,在上面的例子中,采用多层体系结构的代码量和工作量大概是传统C/S工作量的2-3倍以上。

那么,为何还要采用三层体系结构呢?

你可以认为上面的例子是一个“纯粹”的三层体系结构,它是一种最理想化的体系结构。而且为了更为详细地介绍,我写了许多注释在里面;再者,其中有些代码是完全可以通过编程技巧进行简化与优化的,之所以如此详细是介绍,纯粹是为了更好地说明问题。优化后的代码量大概可以减少一半。

当你第一次开始使用这种方法时,可能会因此而延长开发周期,而你的不懂计算机的上司(假设他真的不懂)也可能会因此而感到不解,为何采用了新技术反而会加大开发成本,延长开发周期?

答案很简单。因为你或你的开发团队没有积累。当你采用这种方法做了两个项目的时候,你会发现许多做好的组件是完全可以重用的,也许只需经过一点很小的修改。

一点建议:为了减少代码输入量,可以采用VB自带的“类生成工具”进行类的生成。

如果仔细研究,会发现所有的实体类(即实际存在的业务对象)都与数据库中的某个实体表一一对应,且其属性也对应着数据表中的相应字段。并且都存在AddNewDeleteUpdate方法。要是你的项目组经常要做类似的项目,你完全可以做一个“代码生成器”,从数据库中读取数据库结构,直接生成所有的类模块当然你还需进行少量的修改工作。

如果你是一个优秀项目经理,你可以组织掌握不同技能的人成为一个项目组,有些成员可能擅长于界面制作,有些擅长于数据库编程,有些擅长组件设计,甚至有些人根本不会VB,他们使用DelphiC++。一个优秀的项目经理完全可以通过合理的分工使得项目顺利进行,然而可能直至项目结束时,有些项目组员也没机会了解数据库的结构,有些程序员甚至根本不知道程序界面长什么样子,但项目的确是按时按质完成了!

本文全部用VB完成了整个代码设计,如果你不使用VB,或不屑于使用VB,那么上面的方法依然适用,我们注重的是体系结构与整体思路。

其实,经常见到许多程序员对于编程语言级为挑剔,他们很在乎编程语言的先进性。但是,作为一个软件人员,或软件开发团队,甚至一个软件公司,什么是先进?作者认为,有效才是先进!同理,最先进的往往不一定有效。

我相信,对于任何一个程序员来说,既然从事了软件行业,那你的目标不可能永远是程序员,你可能将系统分析员、项目经理、高层开发管理逐一列为你的奋斗目标。既然这样,别再挑剔编程语言了,否则,你永远只能是一个程序员!尽管你可能会是一个很棒的程序员。

当然,三层体系结构的概念远远不至于此,优秀的分布式应用开发的过程,用到了向对象的分析/设计/编程/测试,UML建模、软件开发过程控制、并行开发、迭代增量开发等诸多先进技术与理念。

面向对象的技术,不仅可以使得软件开发过程更易于控制,软件稳定性、质量得以提高,而且对于其它领域分析问题的方法、思路都颇为有益。长期从事此道,你会发现其中的乐趣有如滔滔的江水,连绵不绝!

Word原文下载

(全文完)

三层体系结构与数据库编程

作者:XC-SOFT( gxc@263.net )

如转载,请说明出处 http://www.xc-soft.com

源码下载

数据库脚本下载本下载

作者:XC-SOFT( gxc@263.net )

如转载,请说明出处 http://www.xc-soft.com

源码下载

数据库脚本下载本下载

接要 本文主要介绍了基于三层体系结构的网络数据库设计,并结合面向对象,分布式数据库开发等理论。全文围绕一个典型而简单的例子,通过VB编程语言,从分析、建模、设计、编码等各个角度对三层体系与数据库进行了全面而详细的阐述,文中提供了全部源代码。

关键词  三层体系  数据库  面向对象  分布式开发

1.     三层体系结构

我们经常会看到许多应聘者在简历上写着“精通数据库编程”的字样,也经常会在招聘网站上看到软件公司的招聘要求中某一项为“精通数据库编程”。于是这些应聘者去这些软件公司面试,于是我们看到了许多“精通”者落选的现象。

一些程序员在设计数据库应用时,通常会采用数据控件绑定的方法实现。用鼠标拉几个控件,再用鼠标设置几个属性,连键盘都不用动,就完成了一个数据库应用的开发!当然,这的确是一种快速的数据库应用开发方式,但快速并不意味着精通。

对于大型的数据库应用系统,或是拥有众多客户端的应用系统,我们需要另外一种“精通”,这就是几乎每个程序员都听说过的“三层体系结构”。

1.1. 传统的C/S模式

在传统的数据库应用体系中,客户端与数据库完全分开,在客户端上运行了大部分服务,如数据访问规则、业务规则、合法性校验等等。每一个客户端都存在数据引擎,并且每个客户端与数据库服务器建立独立的数据库连接(DB Connection)。

基于该种体系的数据库应用系统的优势:开发周期较短,能够适应大部分中小型数据库应用系统的要求(当客户端数量少于50时)。

但是,随着数据库应用的日渐发展、数据容量的不断增加、客户端数量的不断增加,该种体系结构显示出了诸多缺陷,主要体现在以下几个方面:

1、              可扩充性:对于数据库服务器端,每当建立一个数据连接,就会占用大量的系统资源,当数据连接达到一定数量(如20个)时,数据库服务器的响应速度与处理速度将大打折扣。

2、              可维护性:基于传统C/S的数据库应用系统,业务规则通常置于客户端应用程序中。如果业务规则一旦发生变化(随便举个例子,如身份证号码有可能升为19位)时,我们就必须修改客户端应用程序,并且将每个客户端进行相应的升级工作。

3、              可重用性:采用传统C/S的设计模式时,数据库访问、业务规则等都固化在客户端应用程序中。如果客户另外提出了B/S的应用需求,则需要在WEB服务器中重新进行数据库访问、业务规则、合法性校验等编码(例如将数据库访问写入ASP代码),而所做的工作与客户端应用程序中的功能完全重复,从而加大了工作量,又使得程序开发者心里感到极不舒服。

正因为以上的诸多缺陷,使得三层(多层)体系结构成为目前数据库应用开发的首选,甚至客户有时也会提出该种技术需求。

1.2. 三层体系结构

所谓三层体系结构,是在客户端与数据库之间加入了一个“中间层”,也叫组件层。这里所说的三层体系,不是指物理上的三层,不是简单地放置三台机器就是三层体系结构,也不仅仅有B/S应用才是三层体系结构,三层是指逻辑上的三层,即使这三个层放置到一台机器上。

三层体系的应用程序将业务规则、数据访问、合法性校验等工作放到了中间层进行处理。通常情况下,客户端不直接与数据库进行交互,而是通过COM/DCOM通讯与中间层建立连接,再经由中间层与数据库进行交互。

这样的好处显而易见:

1、              由于数据访问是通过中间层进行的,因此客户端不再与数据库直接建立数据连接。也就是说,建立在数据库服务器上的连接数量将大大减少。例如一个500个客户端的应用系统,500个客户端分别与中间层服务器建立DCOM连接,而DCOM通讯所占用的系统资源极为有限,并且是动态建立与释放连接,因此客户端数量将不再受到限制。同时,中间层与数据库服务器之间的数据连接通过“连接池”进行连接数量的控制,动态分配与释放数据连接,因此数据连接的数量将远远小于客户端数量。

2、              可维护性得以提高。因为业务规则、合法性校验存在于中间层,因此当业务规则发生改变时,只需更改中间层服务器上的某个组件(如某个DLL文件),而客户端应用程序不需做任何处理,有些时候,甚至不必修改中间层组件,只需要修改数据库中的某个存储过程就可以了。

3、              良好的可重用性。同样,如果需要开发B/S应用,则不必要重新进行数据访问、业务规则等的开发,可以直接在WEB服务器端调用现有的中间层(如可以采用基于IISWebClass开发,或直接编写ASP代码)。

4、              事务处理更加灵活,可以在数据库端、组件层、MTS(或COM+)管理器中进行事务处理。

如果现在你仍然感到不理解,没关系,请看下面的例子。

2.     简单的人事管理系统

下面以一个极为简单的人事管理系统为例详细讲述如何实现三层体系结构。编程语言为Visual Basic 6.0

为了全面介绍程序设计方法,VB代码中采用了不同的方法实现相同的功能,如数据库访问中,同时采用了存储过程与ADO连接。读者可自行选择最适合的方法。

由于在代码中加入了大量注释,因此不再过多地说明函数功能与原理。

在团队开发中,代码中注释部分应占整个代码的1/3左右,而且应在代码编写前就写好注释。如果另一个程序员认为你的代码中注释全部是废话,那么这些注释肯定是在写完代码之后才加上去的!

2.1. 需求

简单的部门/人员管理系统,要求:

1、  部门的属性有部门名称,人员的属性有姓名、年龄、性别;

2、  部门存在上下级关系;

3、  人员必须属于一个部门;

4、  人员、部门需要实现增加、删除、修改功能

5、  可以按人员的名称、年龄查询人员

6、  如果一个部门存在人员,或存在下级部门,则该部门不可删除

以上即为系统的简单需求。

2.2. 数据库

数据库采用SQL Server 7设计,数据库名称为“TEST”,存在两个数据表(此处假设读者已掌握数据库设计,因为这个数据库实在太简单了)。

tDept

字%

随着Internet越来越广泛的应用,原来基于局域网的企业网开始采用Internet技术构筑和改建自己的企业网,即Intranet。于是,一种新兴的体系结构Browser/Server应运而生,并获得飞速发展,成为众多厂家争相采用的新型体系结构。本质上,Browser/Server也是一种Client/Server结构,它是一种由传统的二层Client/Server结构发展而来的三层Client/Server结构在Web上应用的特例。
在Browser/Server的系统中,用户可以通过浏览器向分布在网络上的许多服务器发出请求。Browser/Server结构极大的简化了客户机的工作,客户机上只需安装、配置少量的客户端软件即可, 服务器将担负更多的工作,对数据库的访问和应用程序的执行将在服务器上完成。
在Browser/Server三层体系结构下,表示层(Presentatioon)、功能层(Business Logic)、数据层(Data Service)被割成三个相对独立的单元:
第一层 表示层:Web浏览器
在表示层中包含系统的显示逻辑,位于客户端。它的任务是由Web浏览器向网络上的某一Web服务器提出服务请求,Web服务器对用户身份进行验证后用HTTP协议把所需的主页传送给客户端,客户机接受传来的主页文件,并把它显示在Web浏览器上。
第二层 功能层:具有应用程序扩展功能的Web服务器
在功能层中包含系统的事务处理逻辑,位于Web服务器端。它的任务是接受用户的请求,首先需要执行相应的扩展应用程序与数据库进行连接,通过SQL等方式向数据库服务器提出数据处理申请,而后等数据库服务器将数据处理的结果提交给Web服务器,再由Web服务器传送回客户端。
第三层 数据层:数据库服务器
在数据层中包含系统的数据处理逻辑,位于数据库服务器端。它的任务是接受Web服务器对数据库操纵的请求,实现对数据库查询、修改、更新等功能,把运行结果提交给Web服务器。
仔细分析不难看出,三层的Browser/Server体系结构是把二层Client/Server结构的事务处理逻辑模块从客户机的任务中分离出来,由单独组成的一层来负担其任务,这样客户机的压力大大减轻了,把负荷均衡地分配给了Web服务器,于是由原来的两层的Client/server结构转变成三层的Browser/Server结构。这种三层体系结构如下图所示。



这种结构不仅把客户机从沉重的负担和不断对其提高的性能的要求中解放出来,也把技术维护人员从繁重的维护升级工作中解脱出来。由于客户机把事务处理逻辑部分分给了功能服务器,使客户机一下子"苗条"了许多,不再负责处理复杂计算和数据访问等关键事务,只负责显示部分,所以维护人员不再为程序的维护工作奔波于每个客户机之间,而把主要精力放在功能服务器上程序的更新工作。这种三层结构层与层之间相互独立,任何一层的改变不影响其它层的功能。它从根本上改变了传统的二层Client/Server体系结构的缺陷,它是应用系统体系结构中一次深刻的变革。
两种体系结构的对比
Browser/Server体系结构与Client/Server体系结构相比不仅具有Client/Server体系结构的全部优点,而且又有Clinet/Server体系结构所不具备的独特优势:
开放的标准:
Client/Server所采用的标准只要在内部统一就可,它的应用往往是专用的。Browser/Server所采用的标准都是开放的、非专用的,是经过标准化组织所确定的而非单一厂商所制定,保证了其应用的通用性和跨平台性。
较低的开发和维护成本:
Client/Server的应用必须开发出专用的客户端软件,无论是安装、配置还是升级都需要在所有的客户机上实施,极大地浪费了人力和物力。Browser/Server的应用只需在客户端装有通用的浏览器即可,维护和升级工作都在服务器端进行,不需对客户端进行任何改变,故而大大降低了开发和维护的成本。
使用简单,界面友好:
Client/Server用户的界面是由客户端软件所决定的,其使用的方法和界面各不相同,每推广一个Client/Server系统都要求用户从头学起,难以使用。Browser/Server用户的界面都统一在浏览器上,浏览器易于使用、界面友好,不须再学习使用其它的软件,一劳永逸的解决了用户的使用问题。
客户端消肿:
Client/Server的客户端具有显示与处理数据的功能,对客户端的要求很高,是一个"胖"客户机。Browser/Server的客户端不再负责数据库的存取和复杂数据计算的等任务,只需要其进行显示,充分发挥了服务器的强大作用,这样就大大的降低了对客户端的要求,客户端变得非常"瘦"。
系统灵活:
Client/Server系统的三部分模块中有一部分需改变就要关联到其它模块的变动,使系统极难升级。Browser/Server系统的三部分模块各自相对独立,其中一部分模块改变时其它模块不受影响,系统改进变得非常容易,且可以用不同厂家的产品来组成性能更佳的系统。
保障系统的安全性:
在Client/Server系统中由于客户机直接与数据库服务器进行连接,用户可以很轻易的改变服务器上的数据,无法保证系统的安全性。Browser/Server系统在客户机与数据库服务器之间增加了一层Web服务器,使两者不再直接相连,客户机无法直接对数据库操纵,有效地防止用户的非法入侵。
三层的Browser/Server体系结构具有许多传统Client/Server体系结构不具备的优点,而且又紧密的结合了Internet/Intranet技术,是技术发展的大势所趋,它把应用系统带入了一个崭新的发展时代

摘自《天极网》 http://www.yesky.com/

2005年05月10日

OICQ聊天软件,圆了万千网友们“无数次亲密接触”的美梦。而每一个QQ号码后面,都隐藏着一位极具个性的男女。他们或者是聊天高手,或者是初出茅庐的“菜鸟”,但相同的是,他们的QQ资料介绍,一样的极具个性与幽默天分。不信?那就看看下面的网友QQ资料展示吧:

“nzj9819”的QQ资料:
 陪人聊天,每字6毛,标点符号半价收费,千字以上打8折!
先付款,后聊天,款到即聊。对待非专业人士,偶不承担主动交谈义务。网络虛假,若受伤、受骗,偶不承担任何责任。

“牛家翠花”的QQ资料:
 1980年中国制造,长178cm,净重66kg。采用人工智能,各部分零件齐全,运转稳定,经二十多年的运行,属质量信得过产品。该“产品”手续齐全,无限期包退包换。现因发展需要,诚招志同道合者,共同研制开发第二代语言聊天软件,有意者请联系!

“女巫若若”的QQ资料:
每个女人都是为爱而折翼的天使,她们来到人间,就再也回不去天堂了,所以需要男人好好的珍惜。
 我也是天使,不过降落的时候不小心脸先着地了,回不去天堂是因为体重的原因。
还好,我还有一颗天使的心,善良、仁爱。

“我非典”的QQ 资料:
人还不错,除了长的帅点以外,也没什么缺点了!
 我有时候觉得,自己其实不太帅。但有一天,我被一群女孩子围住,她们说我帅,我不承认,她们就打我,还说我虚伪。

“蜘蛛没没”的QQ资料:
我是宇宙超级网虫,欢迎你打我、骂我、K我、扁我、踢我、踹我、揍我,甚至把我煮、煎、炒、炸、焖、炖、红烧、清蒸、干褊、水煮……我都毫无怨言,但前提是:这个人必须是天底下最最漂亮、可爱、美丽、温柔、善良、贤惠、有气质的大美女–某某(女友名字)

“qanaycom”的QQ资料:
 别看资料,看聊效!!!

“疯狂小马”的QQ资料:
曾经有个女孩子要与我共赴黄泉–“你再不还我钱,我就和你同归于尽!”
曾经有个女孩子与我相约到下辈子–“想追求我?下辈子吧!”
曾经有个女孩子肯为我而死–“跟你在一起,我宁愿死!”

“宏韬”的QQ资料:
家穷人丑,一米四九。小学文化,农村户口。破屋三间,薄田一亩。冷锅热灶,老婆没有。一年四季,药不离口,今日上网,广征女友,革命路上,并肩携手~~

“九天圣”的QQ资料:
女口果人尔能看日月白这段言舌,那言兑日月人尔白勺目艮目青有严重白勺散光

“不帅真好”的QQ资料:
我想要个健康平凡的,会炒番茄鸡蛋的,会操作洗衣机的,脸上可能还有点小雀斑的,见到生人会脸红的,知道酱油多少钱一瓶的,在我把大米扛回家时会帮我擦擦汗的那么一个姑娘。这样的要求,高不高?如果真的有那么一个姑娘,那么神啊,我祈求你赐我一个吧!

“荡秋千的猪”的QQ资料:
小人本住在黄河的一边,家中有屋又有田,生活乐无边。自从有了OICQ,它占我时间夺我钱,逼我卖楼又卖田,流落在街边……各位朋友可怜我,陪我聊聊天!聊聊天!

“风未止9428”的QQ资料:
上网了吧,网恋了吧,幼稚思想受骗了吧?
网恋了吧,投入了吧,感情走上绝路了吧?
投入了吧,见面了吧,没有以前来电了吧?
见面了吧,后悔了吧,美眉变成恐龙了吧?
后悔了吧,倒霉了吧,感情投资浪费了吧?
倒霉了吧,想死了吧,以后不敢上网了吧 ?

当你不想再跟原来的对象继续下去了,要跟她分手,又怕给对方造成伤害,怎么办呢?方法如下,只要你肯执行其中3-5条,结果一定如你所愿。信不信由你。
【注意:一旦你后悔则毫无挽救机会!!!】
1. 就餐时(最好是西餐),用刀叉严密护着你面前的食物,就象任何走近你的人都会抢走它们,并且一旦有人靠近就做出要刺他的样子,连服务员也不例外;
2. 把所有餐桌上放调料的小瓶子都拿过来,然后用它们堆起一个金字塔;
3. 用对方的袖子来擦鼻涕或眼泪(至少两次);
4. 向其他你不认识的人做鬼脸,并对他们困惑的反应嗤之以鼻;
5. 每说几….几个字就重…..重复一遍(扮结巴会吧);
6. 进餐的时候一边还要看书或者读报,不理对方;
7. 瞪着对方,使劲磨牙,还要让她听到;
8. 间或全身剧烈抽搐一下,当她问你怎么回事的时候,假装不明白她说些什么;
9. 每五分钟站起来一次,张开双臂作飞机状,围绕桌子“飞行”一圈;
10. 让服务员给你上一碗猪油;
11. 看到女人穿短裙就象她吹口哨或怪叫(如果是恐龙mm的话这招更管用!);
12. 信口胡编一些你以前怎么和另一个女人相亲相爱之缠绵故事;
13. 当对方开始说到她自己情况的时候,拿出一只口琴,随便吹点什么。若没有口琴,用筷子敲打碗碟也很管用;
14. 不吃鱼,而是用它来祭神,把它放在桌上离你们最远处,对它膜拜鞠躬;
15. 点菜时,一定问问服务员这餐馆里是否供应动物让人活吃的;
16. 跟对方抢着吃;
17. 流口水并且大声吧唧嘴;
18. 大声地张着嘴咀嚼,嘴里塞满食物也不停地说话,让食物渣到处飞溅;
19. 菜上来努力不到一分钟就把它们吃个精光;
20. 跟对方说你要去洗手间,然后找到餐厅领班,换一张桌子,再点一份饭菜,自己吃;等到你约会的人最后找到你的时候,不等对方开口,你先问:“怎么你洗手间去这么久?”
21. 问邻座的人,你可不可以吃他们的食物;
22. 请求你的对象把你的名字刺在身上(是隐秘的地方更好,比如说臀部),在遭到拒绝后还一而再再而三的说起这事;
23. 问你对象她有多少存款;
24. 刚到餐馆时,找一个远离窗口的座位,同时你能看到所有出口而且还要背靠着墙,就象电影里面怕人跟踪的间谍一样,并且表现得非常紧张和神经质;
25. 吃完饭,仔细把盘子舔一舔,并问对象要不要把他(她)的也舔一下;
26. 嘴里不时地发出很响的嗡嗡声(象牛蛙);
27. 进行一次争论,自己跟自己(如不会就看星仔电影练习);
28. 快付帐的时候,如果是对方付,再点一个最贵的菜来,吃一口,就再也不吃了;
29. 把桌上的骨头带走,告诉她说这是带回去给你残疾的老母亲吃的,这可比买食物给她吃便宜多了;
30. 指责你的对象,她在色咪咪地偷看她不该看的东西!

(前言)
今天就介绍一下怎么在eclipse下面开发j2ee程序.

(正文)

[J2EE 插件一览]

用eclipse开发j2ee的话, 可用的插件插件可就太多了:

其中比较出名的有:

Lomboz: http://forge.objectweb.org/projects/lomboz

MyEclipse: http://www.myeclipseide.com/(虽然好,但是不是免费的,个人不建议使
用,真有钱,用wsad)

Resin: http://membres.lycos.fr/resinforeclipse/

EclipseJ2EE: http://www.eclipsej2ee.com/

而j2ee服务器, 也有很多, 比较出名的有:

JBoss: http://sourceforge.net/projects/jboss (支持EJB)

Tomcat: http://jakarta.apache.org/tomcat/index.html

Resin: http://www.caucho.com/

这些插件各有千秋, 而服务器也根据你开发程序的级别有所选择.
那接下来我介绍一个比较大众化的开发模式(Eclipse+Lomboz+JBoss/Tomcat)来开发j2ee
程序

[配置 Lomboz, JBoss/Tomcat]

这里我们使用Eclipse 2.1.3作为测试平台

1. JBoss下载

最新版本的JBoss是4.0.0 RC3, 可以从jboss主页当下来, 这个版本已经支持JBoss-AOP
方面的应用, 如果想开发aop方面

的程序,可以使用这个服务器, 下载下来之后解压到某个目录,假设叫

2. Tomcat下载

Tomcat现在的最新版本是5.0.x, 可以从tomcat的主页当下来,支持最新的jsp2.0,下载下
来之后解压到某个目录,假设叫



3. Lomboz下载

Lomboz的版本最新为Lomboz 3m8 preview3, 这个最新版本还在测试中,将支持最新的J2E
E 1.4规范,不过我们这里测试的

版本是Lomboz 2.1.3版本的,其实功能上是差不多的. 只是对于的eclipse版本不同.

下载下来之后, 把包解压装到eclipse里即可(具体安装过程参看前一讲)

4. 配置lomboz中的jboss

启动eclipse,打开preference->lomboz, 让我们对lomboz的选项做一些设置.

这preference->lomboz这个页面, 设置JDK Tool.jar, (前提是你必须装一下jdk,最好是
1.4)
然后选择preference->lomboz->server definitions, 这个页面就是用来配置各个服务
器的
在这里面预先定义了很多种服务器, 我们先来配置jboss

选择server type: jboss 3.2.x (因为最新版的jboss 4.0还没加进lomboz里, 就用3.2.
x的配置凑合用用, 因为3.2.x和

4.0的配置都是一样的)

然后修改下面的属性: Application Server Directory:
Classpath Variable:
Classpath Variable: JBOSS400
Server Configuration: 已经有三个配置minimal/defa
ult/all, 可以选一个你合适的,

不懂的选default好了
Port: 默认是8080, 你可以自己修改
, 就是服务器开后的访问端口

如果你觉得4.0的配置在jboss3.2.x实在不爽, 也可以新建一个server type节点

在ECLIPSE_HOME/plugins/com.objectlearn.jdt.j2ee/servers 文件夹下新建一个后缀
名为server的文件,然后根据已

有的server文件为模版,用文本编辑器编辑server文件。建立了一个新的server文件以
后,也可以在Lomboz的属性页中

进行相应的设置。

5. 修改bug

照例说是没有这一步的,可没办法, 配置jboss这里就是有一个bug,要不到后面jboss就跑
不起来了
还是选择server type : jboss 3.2.x这个节点, 然后切换到server classpath 页
然后add library按下, 选择/lib/dom4j.jar, 这样jboss启动的classpath
才算完整了
否则,启动的时候会报这个错
java.lang.NoClassDefFoundError: org/dom4j/Element
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Unknown Source)
at java.lang.Class.getConstructor0(Unknown Source)
at java.lang.Class.newInstance0(Unknown Source)
at java.lang.Class.newInstance(Unknown Source)
at org.jboss.mx.modelmbean.RequiredModelMBeanInstantiator.instantiate(

RequiredModelMBeanInstantiator.java:36)
at org.jboss.mx.server.MBeanServerImpl.(MBeanServerImpl.java:163)
at
org.jboss.mx.server.MBeanServerBuilderImpl.newMBeanServer(MBeanServerBuilderI
mpl.java:56)
at
javax.management.MBeanServerBuilder.newMBeanServer(MBeanServerBuilder.java:62
)
at
javax.management.MBeanServerFactory.createMBeanServer(MBeanServerFactory.java
:177)
at
javax.management.MBeanServerFactory.createMBeanServer(MBeanServerFactory.java
:115)
at org.jboss.system.server.ServerImpl.doStart(ServerImpl.java:327)
at org.jboss.system.server.ServerImpl.start(ServerImpl.java:291)
at org.jboss.Main.boot(Main.java:144)
at org.jboss.Main$1.run(Main.java:389)
at java.lang.Thread.run(Unknown Source)


6.配置lomboz中的tomcat
选择 tomcat 5.0.x 这个server type,


然后修改下面的属性: Application Server Directory:
Classpath Variable:
Classpath Variable: TOMCAT_HOME
Port: 默认是8080, 你可以自己修改
, 就是服务器开后的访问端口

这里不用另外修改server classpath的, 所以ok.


[使用lomboz开发Web应用]

File-> New-> Project -> Java -> Lomboz J2EE Wizards -> Lomboz J2EE Project

在New Project里填入project的名字,然后next

在Create J2EE Modules的页面, add 一个新的web module, 比如叫Test
然后切换到 Targeted Server , 加入JBoss 3.2.x 和/或者 Tomcat 5.0.x (就是我们前
面配置好的那个服务器)

然后我们可以看到一个j2ee的web项目就建好了, 我们点击在Package Explorer这个view
中选择Test这个目录

右键点击,有一个菜单集叫做Lomboz J2EE,

我们可以利用这个菜单集来进行操作:

比如你加入了好几个服务器的话, 就可以用change default server来选择默认的服务器


选择好默认的服务器之后,我们就可以run server,
此时server就启动了, log信息会在console view中显示出来

等到server完全启动完毕之后, 我们可以deploy module,它就是执行一段ant,然后deplo
y到当前运行的服务器上
完成deploy之后,我们就可以看效果了

打开ie, http://localhost:8080/Test/index.jsp

你如果看到welcome, 那就说明一切都ok了

以后比如你在运行的时候,在编辑jsp, 编辑完之后,重新deploy,就可以马上看到更新的
结果

(前言)
自从有了eclipse, plug-in这么名词就越来越红火起来了, 虽然eclipse自带了jdt,pde
等plug-in, 但这些显然不能满足我们的基本需求,也不能完全体现出eclipse的强大之处
. 那这篇就开始要介绍一些关于第三方plugin的一些知识.

(正文)

[plug-in 的安装]

或许有很多人说, plug-in的安装还不简单, 解压copy重启动不就ok了? 呵呵, 当然这是一
种办法. 但是远不止这一种哦.

方法1: 解压+copy+启动

记得第二讲讲过,eclipse启动后会自动的搜索plugins目录下所有的plugin,然后在featu
res的控制下启动或禁用它们. 所以最简单的一个方法就是把你下载下来的第三方plugin
,解压后copy到这features和plugins这两个目录里面, 就一切ok了.
例如: 我们以eclipse_example这个plug-in为例(每个eclipse版本都有一个相应的examp
le plug-in, 你只要去下载eclipse,就能找到这个plug-in的下载)
下载下来之后,解压到一个temp目录,
/
plugins/
org.eclipse.ui.examples.readmetool_2.1.0/
org.eclipse.swt.examples_2.1.0/
org.eclipse.help.examples.ex1_2.1.0/
org.eclipse.swt.examples.paint_2.1.0/
org.eclipse.sdk.examples_2.1.1/
org.eclipse.ui.examples.multipageeditor_2.1.0/
org.eclipse.compare.examples_2.1.0/
org.eclipse.jdt.ui.examples.projects_2.1.0/
org.eclipse.ui.examples.propertysheet_2.1.0/
org.eclipse.ui.examples.javaeditor_2.1.0/
org.eclipse.team.examples.filesystem_2.1.0/
org.eclipse.swt.examples.controls_2.1.0/
org.eclipse.swt.examples.layouts_2.1.0/
org.eclipse.swt.examples.launcher_2.1.0/
org.eclipse.swt.examples.ole.win32_2.1.0/
org.eclipse.sdk.examples.win32_2.1.1/
features/
org.eclipse.sdk.examples.win32_2.1.1/
org.eclipse.sdk.examples_2.1.1/
这个example里面有16个plugins和2个features, 然后我们这些plugins和features 拷到
eclipse里面相对应的目录里去就ok了

如果你启动eclipse是用老的工作区, 那启动后, eclipse会发现和老的工作区配置相比
又多了两个feature, 所有它会跳出框提示你是否启动这个feature, 选是,然后再选重启
工作区,以使feature生效, 那整个安装过程就ok了

方法2: links方式 (推荐方法)

这种方式比较好管理, 因为你装了1,2个plug-in还好, 如果装了好多好多, 那可就难办
了, 你会发觉/plugins和/features那两个目录下有多的数不清的plugins和features,
你想删出一个插件,你都不知道该删哪个好. 那有什么其他安装plugin的方法吗? 有.

还是以eclipse example为例, 但是我们要对解压后的目录做一下调整, 变成这样的结构
/
eclipse/ <— 新加一个目录
plugins/

features/


然后在目录下创建一个links的目录, 在这个目录下创建一个文件叫
org.eclipse.sdk.examples.link,这文件中写入一下内容:
path= (就是example plug-in解压到的那个目录, 写这个目录路径时记得
把 "\" 写成 "/" )

然后,你就可以启动eclipse了, 效果和前面那个一样.

为什么呢? 理由很简单, 目录下的features和plugins两个目录只是ecli
pse搜索的默认路径, 如果你机器的别的目录中,也有plug-ins, 那就把那个目录的路径
写到links目录下的一个link文件中即可, 那启动的时候也会去那个目录下搜索plugin.

方法3: 自动安装

要实现自动安装, 那就要先启动eclipse了, 然后选help->software update ->
update manager, 这样会切换到install/update perspective, 这个透视图是专门为了
安装和更新插件而设计的.

在eclipse有一个站点的概念, 就是说plug-in制作完后,可以封装后发布到一个站点上,
这样用户就可以通过eclipse平台连接到这个站点,进行自动更新.

例如, 我们以ajdt这个plugin为例 (http://www.eclipse.org/ajdt , 我们要在线安装
这个plug-in,
在features update视图中, 右键点击选new-> site bookmark,
在new site bookmark对话框中, name填 ajdt, URL 填
http://download.eclipse.org/technology/ajdt/update
然后确定, 你会发觉在features update视图中多了一个节点叫ajdt, 展开这个节点:
+ ajdt
+ AspectJ
- Eclipse AspectJ Development Tools 1.1.4 <– 点击这个节点

然后,你会发觉在preview视图中有对这个plugin的简单介绍, 还有一个install按钮, 点
击此按钮,就进入了安装此plugin的过程, 然后只要一路往下next就可以了.安装完毕之
后,重启, 就会自动生效了.

[卸载 plugin]

1. 对于安装方法1装上去的plugin, 呵呵, 没有很好的办法, 自己慢慢挑出来属于那个
插件的plugins和features,手动删除

2. 对于安装方法2装上去的plugin, 那就简单了, 直接把links里面对应的那个文件给删
了,就ok了.而且这样还有一个好处, 哪天你还想再装回去, 只要你plugin没删,把links
的那个文件在加进去就又可以用了.

3.其实对于1,3两种安装方法来说, 要卸载plugin的一个好方法是采用禁用方式较好. 具
体就是在install/upate perspective中的install configuration视图里,选择eclipse
platform 节点, 展开后会看到所有安装在当前eclipse平台下的features, 点击你要卸
的feature, 然后在preview视图中,按disable按钮即可. 这样其实就是不让这个plugin
在平台中启动,并没有真正的删掉. 这样你以后想要重新用的话,还是按照前面的步骤,然
后enable就可以了


[其他]
关于插件这个概念的一点澄清: 比如我说的eclipse example这个plug-in(1) 还有 这个
plugin目录下16个plugin(2)
这(1)和(2)不一样的, 其实plug-in是plugins和features的集合, 一个plug-in往往有多
个plugins和features
但是我们平时叫起名字来就会发生这个概念上的混淆, 所有以后请一定区分开plug-in
和plugin