Posts Tagged ‘cl’

组合CLASS来完成网页布局风格

星期三, 06月 4th, 2008

我是这样来做DIV布局相关代码de.不知道说de清楚不清楚,凑和看吧
我de想法是未来能这样:用标准件de方式来组装网页DIV布局

我把class分为2种,布局class,风格class,布局class是骨架,风格class是衣服
举个例子:
比如布局中de左栏
首先它de属性有:是左栏,宽度,背景颜色,字体颜色等
1.首先会定义一个class,比如:.layout,主要用来控制页面整个de大小
.layout{width:98%;margin:0 auto;text-align:left;}
2.然后会定义3个基本布局Class(l,m,r)
.l{float:left}
.m{width:auto}
.r{float:right}
我把2栏布局也归类于3栏布局,因为3栏布局中,左右栏de宽度分别为0de时候,3栏就变成了2栏.
我写基本布局相关代码de时候,最好还是写成3栏格式.
3.对应布局Class,定义需要de风格Class,比如宽度,高度,背景颜色等等这些都属于风格元素
.class_l{background:#ff0;margin-right: -150px;width:150px;}
.class_m{background:#f00;margin:0 140px 0 150px;}
.class_r{background:#00f;margin-left: -140px;width:140px;}
布局class只有一套,风格class可以定义很多.
比如,要中栏里面在做一个小de2栏布局
就可以再定义一个风格class
.mid_l{background:#ff0;margin-right: -100px;width:100px;}
.mid_m{background:#f00;margin:0 0 0 100px;}
4.将布局class和风格class结合起来,在相关代码这样引用
<div class=”l class_l”></div>
<div class=”l mid_l”></div>
将2个class都引用,中间用空格隔开,前面de是布局class,后面de是风格class,后面还可以继续空格引用,如果需要再特殊定义,您可以给这个div取一个id来定义.
其他de一些常用de风格class也可以写成通用de,比如隐含可以定义为
.hide{display:none}
然后需要de时候,class=”xxx hide”来引用,很方便.
点击运行可以看到效果:
[Ctrl A 全选 提示:您可先修改部分相关代码,再按运行]

ASPX中的用户控件与ASP中的INCLUDE方法对比

星期二, 06月 3rd, 2008

在ASPde年代里,为了避免经常性重复de劳动,对一些功能相似de区域或者相关代码,经常作成一个文件,然后通过连接(直接连接或者虚拟连接)de方法引入到ASP网页文件之中,对于一个很大de引用了很多ASP文件就相当于一个文件被分成了很多块,彼此文件之间de数据是可以自由共享de(除了函数之中de数据).
ASPXde用户控件就与INCLUDE有很大de不同了,它de最大特点就是在于他是以包装好de对象de形式呈现,通过我de编程,可以将一个公用de事例抽象出来,将一些功能和方法总结出来,作成相应de函数和属性供外部de事件调用,实现完全de类化,最大程度de封装外层用户不需要了解de内部事实,使得其更加具有可维护性,也提高了数据de安全性,更利于程序de发布.
简单de东西好用,但自然也功能比较少,或者是很不完善de,我可以自由de将某个文件de包含到指定deASP文件之中,但是,由于彼此de数据是可以共享de,所以导致,文件de相互依赖性很高,降低了相关代码可读性,不利于系统de维护,同时,如果被INCLUDEde文件中如果有一个图片,并且被包含de文件与需要包含文件de文件不在同一个目录之下时,被包含de文件de图片de地址必须以需要包含文件de文件中能显示图片de地址为准,如/FILE/INDEX.ASP是主文件(需要包含文件de文件),而/FILE/INCLUDE/TITLE.ASP是被包含文件,而图片地址为/file/title.jpg此时就需要将TITLE.ASP中de图片地址改为title.jpg而不是../title.jpg,否则将无法在主文件中显示图片,超级链接也是同样de问题.
复杂deASPX固然解决了很多de问题,如能够在控件文件中自由de设置图片de地址,无须理会引用他de文件在什么目录之下,只要在控件文件中能显示出来de东西,被引用之后依然可以被使用,这使得用户制作de控件具有更多de可重用性,而不象ASP中,对于不同目录下de文件引用相同功能de文件时需要设置两个事实上相同但是图片或者超级链接de地址不同de文件.浪费资源,也使得维护费用更高.当然这些优点也使得文件编写起来也比较麻烦,对于分析上要求有更高de归纳能力,使得控件能更广泛de使用.

Eclipse中自动重构实现探索

星期一, 06月 2nd, 2008

  本文用eclipsede自动重构功能对一个程序实例进行重构,目de是探索Eclipse自动重构可以在多大程度上辅助重构这个过程.程序实例使用《Refactoring:Improving the Design of Existing Code》一书中de例子.
  Eclipsede自动重构功能能够很好地支持各种程序元素de重命名,并自动更新相关de引用.Eclipse能够支持方法、字段在类之间移动,并自动更新引用.Eclipse较好地支持内联字段、函数de更新替换.Eclipse较好地支持抽取方法、变量等程序元素.
  重构de过程是一个不断尝试和探索de过程.Eclipsede重构支持撤销和重做,并且能够预览重构结果,这些是很实用de功能.
  Eclipsede重命名、抽取方法、移动、内联功能、更改方法特征符等相关代码结构级别de重构方法,是比较成熟同时也值得使用de功能.至于设计结构上de重构,eclipse还不能很好地支持.但是作者相信,自动重构de理念应该是”工具辅助下de重构工作”,人仍然承担大部分重构工作.
  一、预备工作

  本文使用《Refactoring:Improving the Design of Existing Code》一书第一章de例子.重构前de相关代码及每一步重构后de相关代码见附件.读者最好配合《Refactoring:Improving the Design of Existing Code》一书阅读本文.
  Eclipse使用如下版本:


  同时安装了中文语言包.
  二、重构第一步:分解并重组statement()
  目de:
  1、 把statement()函数中deswich语句提炼到独立de函数amountFor()中.
  2、 修改amountFor()参数命名
  重构方法:
  Extract Method
  Rename Method
  方法:
  1、选中swich语句de相关代码块,在右键菜单中选择”重构/抽取方法”,出现参数对话框.Eclipse自动分析相关代码块中de局部变量,找到了两个局部变量:each和thisAmount.其中,each只是在相关代码块中被读取,但thisAmount会在相关代码块中被修改.按照重构Extract Method总结出来de规则,应该把each当作抽取函数de参数、thisAmount当作抽取函数de返回值.然而Eclipse并不做区分,直接把这两个变量当作抽取新方法de参数,如图.


  我de目de是把在抽取函数中不会被修改deeach作为参数;会被修改dethisAmount作为返回值.解决de办法是,把 double thisAmount = 0; 这行相关代码移到switch语句de上面,变成这样:
  double thisAmount = 0;
  switch(each.getMovie().getPriceCode()){
  case Movie.REGULAR:
  thisAmount = 2;
  if(each.getDaysRented()>2)
  thisAmount = (each.getDaysRented()-2)*1.5;
  break;
  case Movie.NEW_RELEASE:
  thisAmount = each.getDaysRented()*3;
  break;
  case Movie.CHILDRENS:
  thisAmount = 1.5;
  if(each.getDaysRented()>3)
   thisAmount = (each.getDaysRented()-3)*1.5;
  break;
  }
  选中这段相关代码,在右键菜单中选择”重构/抽取方法”,eclipse这次变得聪明点了,如图.

  选择”预览”按钮预先查看重构后de结果,符合我最初de目de.


  选择”确定”按钮,重构后de相关代码片断如下:
  public String statement() {
  double totalAmount = 0;
  int frequentRenterPoints = 0;
  Enumeration rentals = _rentals.elements();
  String result = “Rental Record for ” getName() ” “;
  while(rentals.hasMoreElements()){
  Rental each = (Rental)rentals.nextElement();
  double thisAmount = amountFor(each);
  frequentRenterPoints ;
  if((each.getMovie().getPriceCode())==Movie.NEW_RELEASE &&each.getDaysRented()>1)
   frequentRenterPoints ;
   result = ” ” each.getMovie().getTitle() ” ” String.valueOf(thisAmount) ” “;
   totalAmount = thisAmount;
  }
  result = “Amount owed is ” String.valueOf(totalAmount) ” “;
  result = “You earned ” String.valueOf(frequentRenterPoints) ” frequent renter points”;
  return result;
  }
  /**
  * @param each
  * @return
  */
  private double amountFor(Rental each) {
  double thisAmount = 0;
  switch(each.getMovie().getPriceCode()){
  case Movie.REGULAR:
   thisAmount = 2;
   if(each.getDaysRented()>2)
    thisAmount = (each.getDaysRented()-2)*1.5;
   break;
  case Movie.NEW_RELEASE:
   thisAmount = each.getDaysRented()*3;
   break;
  case Movie.CHILDRENS:
   thisAmount = 1.5;
   if(each.getDaysRented()>3)
    thisAmount = (each.getDaysRented()-3)*1.5;
   break;
  }
  return thisAmount;
  }
  2、选中amountFor()de参数each,在右键菜单中选择”重构/重命名”,在对话框中输入新de名称:aRental,选择确定,amountFor()中所有eachde引用全部被替换成新de名称.用同样de办法修改amountFor()中de局部变量thisAmount为result.重构后deamountFor()相关代码如下:
  /**
  * @param aRental
  * @return
  */
  private double amountFor(Rental aRental) {
  double result = 0;
  switch(aRental.getMovie().getPriceCode()){
  case Movie.REGULAR:
   result = 2;
   if(aRental.getDaysRented()>2)
    result = (aRental.getDaysRented()-2)*1.5;
   break;
  case Movie.NEW_RELEASE:
   result = aRental.getDaysRented()*3;
   break;
  case Movie.CHILDRENS:
   result = 1.5;
   if(aRental.getDaysRented()>3)
    result = (aRental.getDaysRented()-3)*1.5;
   break;
  }
  return result;
  }
  三、重构第二步:搬移”金额计算”相关代码

  目de:
  1、 将函数amountFor()转移到Rental类中,并更名为getCharge().
  2、 更新并替换所有对amountFor()de引用.
  重构方法:
  Move Method
  Change Method signatrue
  Inline Method
  Inline Temp
  方法:
  1、选中函数amountFor()de定义,在右键菜单中选择”重构/移动”,显示参数设置对话框.把新方法名改成getCharge.按下”确定”按钮,Customer Class中deamountFor()函数被移动到Rental Class中,并更名为:getCharge().

  同时eclipse自动在CustomerdeamountFor()函数中添加一行对新函数de”委托”相关代码:
  private double amountFor(Rental aRental) {
  return aRental.getCharge();
  }
  这行相关代码会产生编译错误,原因是amountFor()deprivate型被传递到了新de方法中:
  /**
  * @param this
  * @return
  */
  private double getCharge() {
  ……
  }
  2、继续重构!选中getCharge()方法,在右键菜单中选择”重构/更改方法特征符”,弹出参数选择对话框,把访问修饰符从private改成public.Eclipsede编译错误提示自动消失.

  3、回到Customer类,把所有对amountFor()引用de地方替换成直接对getCharge()de引用.选中Customer类de函数amountFor(Rental aRental),在右键菜单中选择”重构/内联”,出现参数选择对话框.

  选择”确认”按钮,引用amountFor()de地方被替换成对getCharge()de引用.
  public String statement() {
  ……
  double thisAmount = each.getCharge();
  ……
  }
  4、除去临时变量thisAmount.
  选中变量thisAmount,在右键菜单中选择”重构/内联”,重构预览窗口如下,可见达到了重构de目de.按下”确认”按钮重构相关代码.

  statement()相关代码:
  public String statement() {
  double totalAmount = 0; // 总消费金额
  int frequentRenterPoints = 0; // 常客积点
  Enumeration rentals = _rentals.elements();
  String result = “Rental Record for ” getName() ” “;
  while(rentals.hasMoreElements()){
  Rental each = (Rental)rentals.nextElement(); //取得一笔租借记录
  // add frequent renter points(累加 常客积点)
  frequentRenterPoints ;
  // add bouns for a two day new release rental
  if((each.getMovie().getPriceCode())==Movie.NEW_RELEASE && each.getDaysRented()>1)
   frequentRenterPoints ;
  // show figures for this rental(显示此笔租借数据)
  result = ” ” each.getMovie().getTitle() ” ”
  String.valueOf(each.getCharge()) ” “;
  totalAmount = each.getCharge();
  }
  // add footer lines(结尾打印)
  result = “Amount owed is ” String.valueOf(totalAmount) ” “;
  result = “You earned ” String.valueOf(frequentRenterPoints) ” frequent renter points”;
  return result;
  }
  
  四、重构第三步:提炼”常客积点计算”相关代码
  目de:提取”常客积点计算”相关代码并放在Rental类中,”常客积点计算”相关代码如下.
  public String statement() {
  ……
  // add frequent renter points
  frequentRenterPoints ;
  // add bouns for a two day new release rental
  if((each.getMovie().getPriceCode())==Movie.NEW_RELEASE && each.getDaysRented()>1)
  frequentRenterPoints ;
  ……
  }
  重构后de相关代码如下:
  frequentRenterPoints = each.getFrequentRenterPoints();
  重构方法:
  Extract Method
  Move Method
  Change Method signatrue
  Inline Method
  方法:
  1、 首先,抽取相关代码到独立de函数中.
  用”抽取方法”重构相关代码,函数名:getFrequentRenterPoints.很遗憾,eclipsede不能生成诸如:frequentRenterPoints = getFrequentRenterPoints(Rental aRental); de相关代码.原因是执行自增操作de局部变量frequentRenterPoints要出现在等式右边,因此抽取函数getFrequentRenterPoints()一定要把frequentRenterPoints作为参数.手工修改函数和对函数de引用,重构后de相关代码如下:
  public String statement() {
  ……
  while(rentals.hasMoreElements()){
  ……
  frequentRenterPoints = getFrequentRenterPoints(each);
  ……
  }
  ……
  }
  /**
  * @param each
  * @return
  */
  private int getFrequentRenterPoints(Rental each) {
  if((each.getMovie().getPriceCode())==Movie.NEW_RELEASE && each.getDaysRented()>1)
  return 2;
  else
  return 1;
  }
  2、 把getFrequentRenterPoints()移动到Rental类中.
  3、 对getFrequentRenterPoints()”更改方法特征符”为public.
  4、 对Customerde函数getFrequentRenterPoints()执行内联操作,重构目标完成.
  五、重构第四步:去除临时变量(totalAmount和frequentRenterPoints)

  目de:去除临时变量(totalAmount和frequentRenterPoints)
  方法:
  1、 分析totalAmount和frequentRenterPointsde定义和引用结构如下:
  // 声明和定义
  double totalAmount = 0;
  int frequentRenterPoints = 0;
  ……
  // 在循环中修改
  while(rentals.hasMoreElements()){
  ……
  frequentRenterPoints = each.getFrequentRenterPoints();
  ……
  totalAmount = each.getCharge();
  ……
  }
  ……
  // 在循环外使用
  result = “Amount owed is ” String.valueOf(totalAmount) ” “;
  result = “You earned ” String.valueOf(frequentRenterPoints) ” frequent renter points”;
  ……
  上述两个变量在循环体外面定义和使用,在循环中被修改,运用Replace Temp with Query方法去除这两个临时变量是一项稍微复杂de重构.很遗憾,eclipse目前不支持这样de重构.
  2、手工修改相关代码.
  六、重构第五步:运用多态取代与价格相关de条件逻辑

  目de:
  1、 把Rental类中de函数getCharge()移动到Movie类中.
  2、 把Rental类中de函数getFrequentRenterPoints()移动到Movie类中.
  重构方法:
  Move Method
  Inline Method
  方法:
  1、 选中Rental类中de函数getCharge(),右键菜单选中”重构/移动”,eclipse提示找不到接收者,不能移动.原因在于这行语句:
  switch(getMovie().getPriceCode()){//取得影片出租价格
  选中getMovie(),右键菜单选中”重构/内联”,确定后相关代码成为:
  switch(_movie.getPriceCode()){ //取得影片出租价格
  选中getCharge(),执行”重构/移动”后,函数被移动到Movie类中.然而这只是部分达成了重构目de,我发现,移动后de相关代码把Rental作为参数传给了getCharge(),手工修改一下,相关代码变成:
  class Movie ……
  /**
  * @param this
  * @return
  */
  public double getCharge(int _daysRented) {
  double result = 0;
  switch(getPriceCode()){ //取得影片出租价格
  case Movie.REGULAR: // 普通片
   result = 2;
   if(_daysRented>2)
    result = (_daysRented-2)*1.5;
   break;
  case Movie.NEW_RELEASE: // 新片
   result = _daysRented*3;
   break;
  case Movie.CHILDRENS: // 儿童片
   result = 1.5;
   if(_daysRented>3)
    result = (_daysRented-3)*1.5;
   break;
  }
  return result;
  }
  class Rental……
  /**
  * @param this
  * @return
  */
  public double getCharge() {
  return _movie.getCharge(_daysRented);
  }
  2、用同样de步骤处理getFrequentRenterPoints(),重构后de相关代码:
  class Movie ……
  /**
  * @param frequentRenterPoints
  * @param this
  * @return
  */
  public int getFrequentRenterPoints(int daysRented) {
  if((getPriceCode())==Movie.NEW_RELEASE && daysRented>1)
  return 2;
  else
  return 1;
  }
  class Rental……
  /**
  * @param frequentRenterPoints
  * @param this
  * @return
  */
  public int getFrequentRenterPoints(int daysRented) {
  if((getPriceCode())==Movie.NEW_RELEASE && daysRented>1)
  return 2;
  else
  return 1;
  }
  七、重构第六步:终于……我来到继承

  目de:对switch语句引入state模式.
  方法:
  很遗憾,不得不在这里提前结束eclipsede自动重构之旅.Eclipse几乎不能做结构上de重构.也许Martin Fowler在书中呼唤de自动重构工具止于”工具辅助下de重构工作”这一理念.艺术是人类de专利,编程艺术de梦想将持续下去.
  感兴趣de读者可以查看手工重构de最后一步相关代码.将重构进行到底!
  附录:eclipse支持de重构方法(摘自eclipse中文帮助)
  名称功能
  撤销执行上一次重构de”撤销”.只要除了重构之外尚未执行任何其它源更改,重构撤销缓冲区就有效.
  重做执行上一次撤销重构de”重做”.只要除了重构之外尚未执行任何其它源更改,重构撤销/重做缓冲区就有效.
  重命名 启动”重命名”重构对话框:重命名所选择de元素,并更正对元素de所有引用(如果启用了de话)(还在其它文件中).可用于:方法、字段、局部变量、方法参数、类型、编译单元、包、源文件夹和项目,以及解析为这些元素类型中de其中一种de文本选择部分.
  移动 启动”移动”重构对话框:移动所选择de元素,并更正对元素de所有引用(如果启用了de话)(还在其它文件中).适用于:一个实例方法(可以将它移至某个组件)、一个或多个静态方法、静态字段、类型、编译单元、包、源文件夹和项目,以及解析为这些元素类型中de其中一种de文本选择部分.
  更改方法特征符启动”更改方法特征符”重构对话框.更改参数名称、参数类型和参数顺序,并更新对相应方法de所有引用.此外,可以除去或添加参数,并且可以更改方法返回类型和它de可视性.可以将此重构应用于方法或解析为方法de文本选择.
  将匿名类转换为嵌套类启动”将匿名类转换为嵌套类”重构对话框.帮助您将匿名内部类转换为成员类.可以将此重构应用于匿名内部类.
  将嵌套类型转换成顶层启动”将嵌套类型转换为顶层类型”重构对话框.为所选成员类型创建新de Java 编译单元,并根据需要更新所有引用.对于非静态成员类型,将添加字段以允许访问先前de外围实例.可以将此重构应用于成员类型或解析为成员类型de文本.
  下推启动”下推”重构对话框.将一组方法和字段从一个类移至它de子类.可以将此重构应用于在同一个类型中声明de一个或多个方法和字段或者字段或方法内de文本选择.
  上拉启动”上拉”重构型中声明de一个或多个方法、字段和成员类型,也可以应用于字段、方法或成员类型内de文本选择.向导.将字段或方法移至其声明类de超类或者(对于方法)将方法声明为超类中de抽象类.可以将此重构应用于在同一个类
  抽取接口启动”抽取接口”重构对话框.使用一组方法创建新接口并使选择de类实现该接口,并尽可能地将对该类de引用更改为对新接口de引用(可选).可以将此重构应用于类型.
  尽可能使用超类型启动”尽可能使用超类型”对话框.将某个类型de出现替换为它de其中一个超类型,在执行此替换之前,需要标识所有有可能进行此替换de位置.此重构可用于类型.
  内联启动”内联”重构对话框.内联局部变量、方法或常量.此重构可用于方法、静态终态字段和解析为方法、静态终态字段或局部变量de文本选择.
  抽取方法启动”抽取方法”重构对话框.创建一个包含当前所选择de语句或表达式de新方法,并将选择替换为对新方法de引用.可以使用编辑菜单中de扩大选择至以获取有效de选择范围.此功能对于清理冗长、杂乱或过于复杂de方法是很有用de.
  抽取局部变量启动”抽取变量”重构对话框.创建为当前所选择de表达式指定de新变量,并将选择替换为对新变量de引用.此重构可用于解析为局部变量de文本选择.可以使用编辑菜单中de扩大选择至以获取有效de选择范围.
  抽取常量启动”抽取常量”重构对话框.从所选表达式创建静态终态字段并替换字段引用,并且可以选择重写同一表达式de其它出现位置.此重构可用于静态终态字段和解析为静态终态字段de文本选择.
  将局部变量转换为字段启动”将局部变量转换为字段”重构对话框.将局部变量转换为字段.如果该变量是在创建时初始化de,则此操作将把初始化移至新字段de声明或类de构造函数.此重构可用于解析为局部变量de文本选择.
  封装字段启动”自封装字段”重构对话框.将对字段de所有引用替换为 getting 和 setting 方法.它适用于所选择de字段或解析为字段de文本选择.

轻松玩转Java配置的Classpath

星期一, 06月 2nd, 2008

  和Java类路径(classpath)打交道de过程中,开发者偶尔会遇到麻烦.这是因为,类装载器实际装入de是哪一个类有时并不显而易见,当应用程序declasspath包含大量de类和目录时,情况尤其严重.本文将提供一个工具,它能够显示出被装入类文件de绝对路径名.
  一、Classpath基础

  Java虚拟机(JVM)借助类装载器装入应用程序使用de类,具体装入哪些类根据当时de需要决定.CLASSPATH环境变量告诉类装载器到哪里去寻找第三方提供de类和用户定义de类.另外,您也可以使用JVM命令行参数-classpath分别为应用程序指定类路径,在-classpath中指定de类路径覆盖CLASSPATH环境变量中指定de值.
  类路径中de内容可以是:文件de目录(包含不在包里面de类),包de根目录(包含已打包de类),包含类de档案文件(比如.zip文件或者.jar文件).在Unix家族de系统上,类路径de各个项目由冒号分隔,在MS Windows系统上,它们由分号分隔.
  类装载器以委托层次de形式组织,每一个类装载器有一个父类装载器.当一个类装载器被要求装载某个类时,它在尝试自己寻找类之前会把请求先委托给它de父类装载器.系统类装载器,即由安装在系统上deJDK或JRE提供de默认类装载器,通过CLASSPATH环境变量或者-classpath这个JVM命令行参数装入第三方提供de类或者用户定义de类.系统类装载器委托扩展类装载器装入使用Java Extension机制de类.扩展类装载器委托自举类装载器(bootstrap class loader)装入核心JDK类.
  您可以自己开发特殊de类装载器,定制JVM如何动态地装入类.例如,大多数Servlet引擎使用定制de类装载器,动态地装入那些在classpath指定de目录内发生变化de类.
  必须特别注意de是(也是令人吃惊de是),类装载器装入类de次序就是类在classpath中出现de次序.类装载器从classpathde第一项开始,依次检查每一个设定de目录和压缩文件,尝试找出待装入de类文件.当类装载器第一次找到具有指定名字de类时,它就把该类装入,classpath中所有余下de项目都被忽略.
  看起来很简单,对吧?
  二、可能出现de问题

  不管他们是否愿意承认,初学者和富有经验deJava开发者都一样,他们都曾经在某些时候(通常是在那些最糟糕de情形下)被冗长、复杂declasspath欺骗.应用程序所依赖de第三方类和用户定义类de数量逐渐增长,classpath也逐渐成了一个堆积所有可能de目录和档案文件名de地方.此时,类装载器首先装载de究竟是哪一个类也就不再显而易见.如果classpath中包含重复de类入口,这个问题尤其突出.前面已经提到,类装载器总是装载第一个它在classpath中找到de具有合适名字de类,从实际效果看,它“隐藏”了其他具有合适名字但在classpath中优先级较低de类.
  如果不小心,您很容易掉进这个classpathde陷阱.当您结束了一天漫长de工作,最后为了让应用程序使用最好、最新de类,您把一个目录加入到了classpath,但与此同时,您却忘记了:在classpathde另一个具有更高优先级de目录下,存放着该类de另一个版本!

   三、一个简单declasspath工具

  优先级问题是扁平路径声明方法与生俱来固有de问题,但它不是只有Javadeclasspath才有de问题.要解决这个问题,您只需站到富有传奇色彩de软件巨构de肩膀上:Unix操作系统有一个which命令,在命令参数中指定一个名字,which就会显示出当这个名字作为命令执行时执行文件de路径名.实际上,which命令是分析PATH变量,然后找出命令第一次出现de位置.对于Javade类路径管理来说,这应该也是一个好工具.在它de启发之下,我着手设计了一个Java工具JWhich.这个工具要求指定一个Java类de名字,然后根据classpathde指引,找出类装载器即将装载de类所在位置de绝对路径.
  下面是一个JWhichde使用实例.它显示出当Java类装载器装载com.clarkware.ejb.ShoppingCartBean类时,该类第一次出现位置de绝对路径名,查找结果显示该类在某个目录下:
  > java JWhich com.clarkware.ejb.ShoppingCartBean
  Class ‘com.clarkware.ejb.ShoppingCartBean’ found in
  ’/home/mclark/classes/com/clarkware/ejb/ShoppingCartBean.class’
  下面是第二个JWhichde使用实例.它显示出当Java类装载器装载javax.servlet.http.HttpServlet类时,该类第一次出现位置de绝对路径名,查找结果显示该类在某个档案文件中:
  > java JWhich javax.servlet.http.HttpServlet
  Class ‘javax.servlet.http.HttpServlet’ found in
  ’file:/home/mclark/lib/servlet.jar!/javax/servlet/http/HttpServlet.class’
  四、JWhichde工作过程
  要精确地测定classpath中哪一个类先被装载,您必须深入到类装载器de思考方法.事实上,具体实现de时候并没有听起来这么复杂——您只需直接询问类装载器就可以了!
  1: public class JWhich {
  2:
  3: /**
  4: * 根据当前declasspath设置,
  5: * 显示出包含指定类de类文件所在
  6: * 位置de绝对路径
  7: *
  8: * @param className <类de名字>
  9: */
  10: public static void which(String className) {
  11:
  12: if (!className.startsWith(”/”)) {
  13: className = “/” className;
  14: }
  15: className = className.replace(’.', ‘/’);
  16: className = className “.class”;
  17:
  18: java.net.URL classUrl =
  19: new JWhich().getClass().getResource(className);
  20:
  21: if (classUrl != null) {
  22: System.out.println(” Class ‘” className
  23: “‘ found in ‘” classUrl.getFile() “‘”);
  24: } else {
  25: System.out.println(” Class ‘” className
  26: “‘ not found in ‘”
  27: System.getProperty(”java.class.path”) “‘”);
  28: }
  29: }
  30:
  31: public static void main(String args[]) {
  32: if (args.length > 0) {
  33: JWhich.which(args[0]);
  34: } else {
  35: System.err.println(”Usage: java JWhich “);
  36: }
  37: }
  38: }
  首先,您必须稍微调整一下类de名字以便类装载器能够接受(12-16行).在类de名字前面加上一个“/”表示要求类装载器对classpath中de类名字进行逐字精确匹配,而不是尝试隐含地加上调用类de包名字前缀.把所有“.”转换为“/”de目de是,按照类装载器de要求,把类名字格式化成一个合法deURL资源名.
  接下来,程序向类装载器查询资源,这个资源de名字必须和经过适当格式化de类名字匹配(18-19行).每一个Class对象维护着一个对装载它deClassLoader对象de引用,所以这里是向装载JWhich类de类装载器查询.Class.getResource()方法实际上委托装入该类de类装载器,返回一个用于读取类文件资源deURL;或者,当指定de类名字不能在当前declasspath中找到时,Class.getResource()方法返回null.
  最后,如果当前declasspath中能够找到指定de类,则程序显示包含该类de类文件所在位置de绝对路径名(21-24行).作为一种调试辅助手段,如果当前classpath中不能找到指定de类,则程序获取java.class.path系统属性并显示当前declasspath(24-28行).
  很容易想象,在使用Servlet引擎classpathdeJava Servlet中,或者在使用EJB服务器classpathdeEJB组件中,上面这段简单de相关代码是如何运作.例如,如果JWhich类是由Servlet引擎de定制类装载器装入,那么程序将用Servlet引擎de类装载器去寻找指定de类.如果Servlet引擎de类装载器不能找到类文件,它将委托它de父类装载器.一般地,当JWhich被某个类装载器装入时,它能够找出当前类装载器以及所有其父类装载器所装入de所有类.
  【结束语】

  如果需要是所有发明之母,那么帮助我管理Java类路径de工具可以说迟到了很长时间.Java新闻组和邮件列表中充塞着许多有关classpathde问题,现在JWhich为我提供了一个简单却强大de工具,帮助我在任何环境中彻底玩转Java类路径.

Eclipse插件开发之新手入门

星期一, 06月 2nd, 2008

现在在Internet上已经可以见到不少deEclipse插件开发de入门文章,这里我写本文de目de主要是将我自己de体会和最开始de学习告诉给大家. 同时也希望本文能使用最为简单de方法来让大家了解开发Eclipse插件de基础.需要注意de是,要学习Eclipsede插件开发,您需要:
  会使用Eclipse来开发Java应用程序
  了解插件这个词de概念
  了解一些XMLde知识 本文是一个入门de文章,只是向大家说明开发一个插件de简单步骤,同时了解在开发插件时涉及到de技术面会有哪些.
  Eclipse SDK概述
  我通常使用deEclipse也就是我这里所说deEclipse SDK,这个SDK中包括了很多de内容,如下图所示:



  运行时核心(Eclipse Platform) - SDK必须一个Eclipse Platform,它自身不具有任何对最终用户有意义de功能, 它是一个加载所有插件de基础平台.也就是Eclipsede运行时最小集合了.
  Java 开发工具(JDT) - 我所有de有关Javade开发部分都是由这个插件来完成了,它形成了对于Java最为基础de编辑、 编译、运行、调试、发布de环境.
  插件开发者环境(PDE) - 开发插件de插件,我如果要开发插件哪么我就会发现所有de工作环境都是由它来提供de. 它提供了用来自动创建、处理、调试和部署插件de工具.
  我将来要开发de插件都是由平台来加载和运行,而PDE则是开发插件de开发环境,JDT则是开发插件时deJava相关代码de开发环境.
  创建插件项目
  设置引用项目
  开发插件时需要大量de外部库,这些外部库主要是现有deEclipse中各个插件所提供de库. 为了开发方便,我先将这些外部库由一个项目统一引用.
  从资源透视图中,使用文件>导入…>外部插件和段.
  在下一步中选择抽取源归档并在项目中创建源文件夹.
  到显示称为选择de屏幕,选择 org.eclipse.ui,然后单击完成按钮.
  创建项目
  在Eclipse需要创建一个空de插件项目,为了让我更好de理解插件中各个文件de来源,我从一个空白de插件项目开始:
  1) 打开新建项目…向导(文件>新建>项目…)并从插件开发类别中选择插件项目.
  2) 将com.huangdong.examples.helloworld用作项目de名称.缺省情况下,向导还会将com.huangdong.examples.helloworld设置为标识.
  3) 最终,确保在插件相关代码生成器页面上选择了创建空白插件项目.
  4) 当询问您是否想切换到“插件开发”透视图时,回答是.
  5) 选择com.huangdong.examples.helloWorld项目并打开属性对话框.
  6) 在Java构建路径属性中,选择项目选项卡,并选择项目org.eclipse.ui.这些包含了项目需要de导入类.
  7) 重建项目.
   创建一个插件内容
  创建一个新de小视图
  下面我为该项目加入一个很简单de视图:
  1) 在项目desrc目录下创建包com.huangdong.examples.helloworld.
  2) 在此包中创建称为HelloWorldViewde新类其超类为org.eclipse.ui.part.ViewPart.
  在HelloWorldView中加入以下相关代码:
  package com.huangdong.examples.helloworld;
  import org.eclipse.swt.SWT;
  import org.eclipse.swt.widgets.Composite;
  import org.eclipse.swt.widgets.Label;
  import org.eclipse.ui.part.ViewPart;
  public class HelloWorldView extends ViewPart {
  Label label;
  public void createPartControl(Composite parent) {
  label = new Label(parent, SWT.WRAP);
  label.setText(”Hello World”);
  }
  public void setFocus() {}
  }
  我为该类定义了一个变量lable,在createPartControl方法中初始化并设置了一个显示de字符串.
  护展扩展点
  让Eclipse添加这个视图,需要扩展org.eclipse.ui.views扩展点.所有de这些需要在plugin.xml中进行描述.该清单文件描述插件,包括插件de相关代码所在de位置以及正在添加de扩展.
  将以下内容复制到plugin.xml中:
  <?xml version=”1.0″ encoding=”UTF-8″?>
  <plugin id=”com.huangdong.examples.helloworld”
  name=”com.huangdong.examples.helloworld”
  version=”1.0.0″
  provider-name=”HuangDong”>
  <runtime>
  <library name=”helloworld.jar”/>
  </runtime>
  <requires>
  <import plugin=”org.eclipse.ui”/>
  </requires>
  <extension point=”org.eclipse.ui.views”>
  <category
  name=”Hello”
  id=”com.huangdong.examples.helloworld.hello”>
  </category>
  <view
  name=”Hello Greetings”
  category=”com.huangdong.examples.helloworld.hello”
  class=”com.huangdong.examples.helloworld.HelloWorldView”
  id=”com.huangdong.examples.helloworld.helloworldview”>
  </view>
  </extension>
  </plugin>

  在plugin域中定义了插件de名称、标识和版本. 同时在runtime域中定义了插件相关代码将打包于helloworld.jar文件中. 在requires域中定义了该插件所要使用de依赖插件,由于我要使用SWT API和工作台所以列示了org.eclipse.ui. 最后,在extension中说明了要们要扩展org.eclipse.ui.views扩展点. 首先我在category中定义了视图de类别,在工作台de显示视图对话框中,可以使用类别来将相关de视图集中在一起.我定义de类别名为“Hello”. 同时也定义了我de视图,名为“Hello Greetings”,这个视图将会显示在“显示视图”对话框和视图de标题栏中,这里我还通过class标识来说明了实现这个视图de最终类.
  通过plugin.xmlde定义,Eclipse才会真正de找到插件可以做de行为,以及这些行为最终实现de具体Java类.
  在插件清单文件中使用了许多标识. 个别扩展点通常会定义需要标识de配置参数(例如,以上用于视图扩展点de类别标识). 我还要定义插件标识.通常,应该对所有标识都使用 Java 包名前缀,以便确保所有已安装de插件都是唯一de.
  在前缀后面使用de特定名称完全由您自己决定. 然而,如果插件标识前缀刚好与其中一个包de名称相同,则应该避免在该包中使用类名. 否则,将很难分辨您正在查看标识名还是类名.
  还应该避免对不同de扩展配置参数使用相同de标识. 在上述清单中,已经使用了公共标识前缀(com.huangdong.examples.helloworld),但是,我de所有标识都是唯一de. 此命名方法可以帮助我阅读文件并了解哪些标识是相关de.
  运行和测试插件
  运行插件是一件很简单de事,这些在PDE中给我提供了很好de支持. 只需要在菜单中选择运行>运行为>运行时工作台,在运行时会弹出一个重复插件de提示框,可以按确定跳过,不必在意. 这样会启动一个已经安装好插件deEclipse.
  启动后在菜单中选择窗口>显示视图>其它,在显示视图对话框中会有一个分类为Hello,点开Hello分类会看到Hello Greetings,选择后点确定按钮.在最下面de视图中可以见到以下界面:

  到这里,如果您看到了这个图,哪么恭喜您,您de第一个Eclipse插件成功运行了.

Eclipse开发Hibernate应用程序

星期一, 06月 2nd, 2008


  Hibernate是对JDBCde轻量级对象封装,Hibernate本身是不具备Transaction处理功能de,HibernatedeTransaction实际上是底层deJDBC Transactionde封装,或者是JTA Transactionde封装,下面我详细de分析:
  Hibernate可以配置为JDBCTransaction或者是JTATransaction,这取决于您在hibernate.properties中de配置:
  #hibernate.transaction.factory_class
  net.sf.hibernate.transaction.JTATransactionFactory
  #hibernate.transaction.factory_class
  net.sf.hibernate.transaction.JDBCTransactionFactory
  如果您什么都不配置,默认情况下使用JDBCTransaction,如果您配置为:
  hibernate.transaction.factory_class
  net.sf.hibernate.transaction.JTATransactionFactory
  将使用JTATransaction,不管您准备让Hibernate使用JDBCTransaction,还是JTATransaction,我de忠告就是什么都不配,将让它保持默认状态,如下:
  #hibernate.transaction.factory_class
  net.sf.hibernate.transaction.JTATransactionFactory
  #hibernate.transaction.factory_class
  net.sf.hibernate.transaction.JDBCTransactionFactory
  在下面de分析中我会给出原因.
  一、JDBC Transaction

  看看使用JDBC Transactionde时候我de相关代码例子:
  Session session = sf.openSession();
  Transaction tx = session.beginTransactioin();
  …
  session.flush();
  tx.commit();
  session.close();
  这是默认de情况,当您在相关代码中使用HibernatedeTransactionde时候实际上就是JDBCTransaction.那么JDBCTransaction究竟是什么东西呢?来看看源相关代码就清楚了:
  Hibernate2.0.3源相关代码中de类
  net.sf.hibernate.transaction.JDBCTransaction:
  public void begin() throws HibernateException {
  …
  if (toggleAutoCommit) session.connection().setAutoCommit(false);
  …
  }
  这是启动Transactionde方法,看到 connection().setAutoCommit(false) 了吗?是不是很熟悉?
  再来看
  public void commit() throws HibernateException {
  …
  try {
  if ( session.getFlushMode()!=FlushMode.NEVER ) session.flush();
  try {
  session.connection().commit();
  committed = true;
  }
  …
  toggleAutoCommit();
  }
  这是提交方法,看到connection().commit() 了吗?下面就不用我多说了,这个类相关代码非常简单易懂,通过阅读使我明白HibernatedeTransaction都在干了些什么?我现在把用Hibernate写de例子翻译成JDBC,大家就一目了然了:
  Connection conn = …; <— session = sf.openSession();
  conn.setAutoCommit(false); <— tx = session.beginTransactioin();
  … <— …
  conn.commit(); <— tx.commit(); (对应左边de两句)
  conn.setAutoCommit(true);
  conn.close(); <— session.close();
  看明白了吧,HibernatedeJDBCTransaction根本就是conn.commit而已,根本毫无神秘可言,只不过在Hibernate中,Session打开de时候,就会自动conn.setAutoCommit(false),不像一般deJDBC,默认都是true,所以您最后不写commit也没有关系,由于Hibernate已经把AutoCommit给关掉了,所以用Hibernatede时候,您在程序中不写Transactionde话,数据库根本就没有反应.
  二、JTATransaction

  如果您在EJB中使用Hibernate,或者准备用JTA来管理跨Sessionde长事务,那么就需要使用JTATransaction,先看一个例子:
  javax.transaction.UserTransaction tx = new
  InitialContext().lookup(”javax.transaction.UserTransaction”);
  Session s1 = sf.openSession();
  …
  s1.flush();
  s1.close();
  …
  Session s2 = sf.openSession();
  …
  s2.flush();
  s2.close();
  tx.commit();
  这是标准de使用JTAde相关代码片断,Transaction是跨Sessionde,它de生命周期比Session要长.如果您在EJB中使用Hibernate,那么是最简单不过de了,您什么Transaction相关代码统统都不要写了,直接在EJBde部署描述符上配置某某方法是否使用事务就可以了.
  现在我来分析一下JTATransactionde源相关代码, net.sf.hibernate.transaction.JTATransaction:
  public void begin(InitialContext context, …
  …
  ut = (UserTransaction) context.lookup(utName);
  …
  看清楚了吗? 和我上面写de相关代码 tx = new Initial Context?().lookup(”javax.transaction.UserTransaction”); 是不是完全一样?
  public void commit() …
  …
  if (newTransaction) ut.commit();
  …
  JTATransactionde控制稍微复杂,不过仍然可以很清楚de看出来Hibernate是如何封装JTAdeTransaction相关代码de.
  但是您现在是否看到了什么问题? 仔细想一下,Hibernate Transaction是从Session中获得de,tx = session.beginTransaction(),最后要先提交tx,然后再session.close,这完全符合JDBCdeTransactionde操作顺序,但是这个顺序是和JTAdeTransactioin操作顺序彻底矛盾de!!! JTA是先启动Transaction,然后启动Session,关闭Session,最后提交Transaction,因此当您使用JTAdeTransactionde时候,那么就千万不要使用HibernatedeTransaction,而是应该像我上面deJTAde相关代码片断那样使用才行.
  总结:
  1、在JDBC上使用Hibernate 必须写上Hibernate Transaction相关代码,否则数据库没有反应.此时HibernatedeTransaction就是Connection.commit而已
  2、在JTA上使用Hibernate 写JTAdeTransaction相关代码,不要写HibernatedeTransaction相关代码,否则程序会报错
  3、在EJB上使用Hibernate 什么Transactioin相关代码都不要写,在EJBde部署描述符里面配置
  |—CMT(Container Managed Transaction)
  |
  |—BMT(Bean Managed Transaction)
  |
  |—-JDBC Transaction
  |
  |—-JTA Transaction

Eclipse中使用ANT

星期一, 06月 2nd, 2008

前言

  ant是java开发者工具箱de重要一环,junit,xdoclet等都与它紧密关联,程序员可能习惯了IDE提供de自动构建,甚至部署de功能,从而忽略了ant本身,其实,主流deIDE通常是内置ant任务来完成这些工作de,熟悉ant内在de机理,可以阅读或简单修改build.xml无疑可以帮助您更灵活地集成、管理应用项目,如果需要学习maven这种开源项目管理解决方案,也是要以理解ant为基础de哟.另外,使用antde过程实际上对构建进行了文档化,它是无关于IDEde,想象一下,您de同事中可能三分之一在用JbuilderX,三分之一用eclipse,还有一些是别de.
  本人使用eclipse3.0.1,以前de构建和发布工作都由myeclipse插件作了,趁周末实践了一下手动构建,记此备忘.
  实践

  准备工作:这是我de个人习惯,把所有公用de类库jar置于一个固定目录,分好类,不要丢在一个文件夹下,如jakarta-commons、hibernate、spring、struts等,这些是源码构建时需要用到de,在部署时可能有一些不用再打进去了,比如servlet.jar.如果您们有自己deframework,也一并放在这里.然后,打开eclipse,进入Windows->Preferences->Java->User Libraries,增加一个自己de库,比如说mylib,把刚才那些公共dejar全部添入,这样有个好处,在eclipse项目中,不用再看到烦人de长长dejar列表了,比较整洁.
  下来正式进行:
  1.新建一个Java Project,此时就不要再选您dej2ee插件内置de一些选项了,至简即可.
  2.在root下建几个文件夹,我在网上下载de开源项目中经常可以看到这些,比如:
  src - 源码
  classes - 编译
  web - jsp等
  lib - 库,这里可以简单地把mylib下de东东copy过来,便于将来发布源码.
  dlist - 输出dejar或war
  当然,我要建一个build.xml,eclipse中会出现一个蚂蚁de小图标,一般这个文件建立后,下一个项目简单decopy过去,稍加改动就可以了.
  3.打开项目de属性页,在Java Build Pathde库选项中,加入我自定义de公共库mylib.至于Builders方式就不用改了,使用默认deJava Builer即可,我只是项目部署时使用ant,平常de排错工作就交给IDE吧.
  4.重中之重,写您debuild.xml,网上文章很海,我这里就不再啰嗦了,基本上就分那几个任务:
  4.1 先要声明一些路径变量,如
  <property name=”war.dir” value=”dlist” />
  也可以将其写至properties文件中,在这里引用;
  4.2 声明编译de类路径,如下:
  <path id=”master-classpath”>
  <fileset dir=”${lib.root}/struts”>
  <include name=”struts-menu-2.3.jar” />
  <include name=”struts.jar” />
  </fileset>
  <fileset dir=”${lib.root}/jakarta-commons”>
  <include name=”commons-*.jar” />
  </fileset>
  <fileset dir=”${lib.root}/ibatis2.0.9″>
  <include name=”ibatis-*.jar” />
  </fileset>
  <fileset dir=”${lib.root}/jdbcdriver”>
  <include name=”jtds-0.9-rc2.jar” />
  </fileset>s
  ……
  </path>
  4.3 清空输出目录,如web,dlist等.
  4.4 编译构建:
  <target name=”build” description=”Compile main source tree java files into class files, generate jar files”>
  <mkdir dir=”${build.dir}” />
  <javac destdir=”${build.dir}” source=”1.3″ target=”1.3″ debug=”true” deprecation=”false” optimize=”false” failonerror=”true”>
  <src path=”${src.dir}” />
  <classpath refid=”master-classpath” />
  </javac>
  <copy todir=”${build.dir}” preservelastmodified=”true”>
  <fileset dir=”${src.dir}”>
  <include name=”**/*.xml” />
  <include name=”**/*.properties” />
  </fileset>
  </copy>
  <!– ============================================= –>
  <!– 据测试,资源文件不能被打到jar文件中,其余均可 –>
  <!– ============================================= –>
  <copy todir=”${webclasses.dir}/conf” preservelastmodified=”true”>
  <fileset dir=”${src.dir}/conf”>
  <include name=”springResources*.properties” />
  </fileset>
  </copy>
  <mkdir dir=”${weblib.dir}” />
  <jar jarfile=”${weblib.dir}/${name}.jar” compress=”true”>
  <fileset dir=”${build.dir}”>
  <include name=”**” />
  </fileset>
  </jar>
  <copy todir=”${weblib.dir}” preservelastmodified=”true”>
  <fileset dir=”${lib.root}”>
  <include name=”log4j-1.2.8.jar” />
  </fileset>
  <fileset dir=”${lib.root}/struts”>
  <include name=”struts-menu-2.3.jar” />
  <include name=”struts.jar” />
  </fileset>
  <fileset dir=”${lib.root}/jakarta-commons”>
  <include name=”commons-*.jar” />
  </fileset>
  <fileset dir=”${lib.root}/spring-1.1.3″>
  <include name=”spring.jar” />
  <include name=”aopalliance.jar” />
  </fileset>
  ……
  </copy>
  </target>
  <!– ============================================= –>
  <!– Compile main Java sources and copy libraries –>
  <!– ============================================= –>
  <target name=”warfile” description=”Build the web application archive”>
  <mkdir dir=”${dist.dir}” />
  <war warfile=”${dist.dir}/${name}.war” basedir=”${war.dir}” webxml=”${war.dir}/WEB-INF/web.xml”>
  <include name=”*” />
  <include name=”WEB-INF/*.*” />
  <exclude name=”WEB-INF/web.xml” />
  <include name=”WEB-INF/classes/*.*” />
  <include name=”WEB-INF/lib/**” />
  <exclude name=”**/.*” />
  </war>
  </target>

  4.5 打成war
  <target name=”warfile” description=”Build the web application archive”>
  <mkdir dir=”${dist.dir}” />
  <war warfile=”${dist.dir}/${name}.war” basedir=”${war.dir}” webxml=”${war.dir}/WEB-INF/web.xml”>
  <include name=”*” />
  <include name=”WEB-INF/*.*” />
  <exclude name=”WEB-INF/web.xml” />
  <include name=”WEB-INF/classes/*.*” />
  <include name=”WEB-INF/lib/**” />
  <exclude name=”**/.*” />
  </war>
  </target>
  4.6 把几个任务串起来,弄一个default target
  <target name=”all”>
  <antcall target=”clean” />
  <antcall target=”build” />
  <antcall target=”warfile” />
  </target>
  打完收功.在实践中发现,一些配置文件,如struts-config.xml ibatis和springdexml都可以打进jar文件,spring资源文件好象不行,得单独copy至WEB-INFclasses下,另外,您deweb文件夹下,事先得放好web.xml,以及一些tld文件哟.

jsp读取大对象CLOB并生成xml文件示例

星期一, 06月 2nd, 2008

<%@ page contentType=”text/html; charset=gb2312″ %>
<%@ page info=”database handler”%>
<%@ page import=”java.io.*”%>
<%@ page import=”java.net.*”%>
<%@ page import=”java.lang.*”%>
<%@ page import=”java.util.*”%>
<%@ page import=”java.sql.*”%>
<%@ page import=”javax.servlet.*”%>
<%@ page import=”javax.servlet.http.*”%>
<%@ page import=”oracle.sql.CLOB”%>
<%@ page import=”oracle.jdbc.driver.OracleResultSet”%>
<html>
<head>
<meta content=”text/html; charset=gb2312″ http-equiv=”content-type”>
</head>
<body>
<%
int i=0;
String parID = request.getParameter(”id_no”);
String strSql;
String content=”";
try{
String xmlFile = “/usr/local/tomcat/webapps/vehicles/test.xml”;
Class.forName(”sun.jdbc.odbc.JdbcOdbcDriver”);
String dburl=”jdbc:oracle:thin:@192.168.15.250:1521:ycdb”;
Connection con=DriverManager.getConnection(dburl,”training”,”deep1704sea”);
Statement stmt=con.createStatement();
//使用流读取CLOB或BLOB列
strSql = “select xmlgen.getxml(’select * from account_holder where id_no=”0001”’) from dual “;
ResultSet rs=stmt.executeQuery(strSql);
if(rs.next()){
CLOB clob = ((OracleResultSet)rs).getCLOB(1);
if(clob!=null){
Reader is = clob.getCharacterStream();
BufferedReader br = new BufferedReader(is);
String s = br.readLine();
while(s!=null){
//byte[] temp = s.getBytes(”iso-8859-1″);
//s = new String(temp);
content = s;
s=br.readLine();
}
}
}
//out.println(content);
//将从数据库中读出de内容写到文件中
FileOutputStream fo = new FileOutputStream(xmlFile);
PrintStream so = new PrintStream(fo);
so.println(content);
so.close();
rs.close();
stmt.close();
con.close();
}catch(Exception e){
out.println(e);
}
%>
</body>
</html>

在JSP中访问Oracle数据库

星期一, 06月 2nd, 2008

写第一个连接OracledeJSP程序test.jsp
1、连入SQL*Plus
以system/manager用户登录,
SQL> conn system/manager
创建新de用户:如user1/pass1,赋予connect,resource权限.
SQL> grant connect,resource to user1 identified by pass1;
SQL> conn user1/pass1
SQL> create table test(a number,b char(10));
SQL> insert into test values(1,”一”);
SQL> insert into test values(2,”二”);
SQL> insert into test values(3,”三”);
SQL> commit;
SQL> select * from test;
A B
———- ———-
1 一
2 二
3 三
2、配置ODBC(在服务器端设置)
开始->设置->控制面板->管理工具->数据源 (ODBC)->系统DSN->添加->
选择“Oracle ODBC Driver”->完成->
Data Source Name:test名字随便取,好记就行,如:test
Service Name:oradb Oracle数据库de实例名,本例:oradb,一般Oracle默认安装为ORCL
UserID:user1 Oracle用户名,本例:user1
按“OK”->按“确定”退出
3、在C:\JBuilder4\tomcat\webapps\test下创建test.jsp,用notebook编辑,输入以下相关代码
<!–首先导入一些必要depackages–>
<%@ page info=”database handler”%>
<%@ page import=”java.io.*”%>
<%@ page import=”java.util.*”%>
<!–告诉编译器使用SQL包–>
<%@ page import=”java.sql.*”%>
<%@ page import=”javax.servlet.*”%>
<%@ page import=”javax.servlet.http.*”%>
<%
//以try开始
try
{
Connection con;
Statement stmt;
ResultSet rs;
//加载驱动程序,下面de相关代码为加载JDBD-ODBC驱动程序
Class.forName(”sun.jdbc.odbc.JdbcOdbcDriver”);
//用适当de驱动程序连接到数据库,test”是系统dsn名
String url=”jdbc:odbc:test”;
//建立连接,类似于ASP中de创建数据库联接
con=DriverManager.getConnection(url, “user1″, “pass1″);
//创建一个JDBC声明
stmt = con.createStatement();
//增加新记录
stmt.executeUpdate(”INSERT INTO test (a,b) VALUES (1,”2″)”);
//查询记录
rs = stmt.executeQuery(”SELECT a,b from test”);
//输出查询结果
out.println(”<table border=1 width=400>”);
while (rs.next())
{
String col1 = rs.getString(1);
String col2 = rs.getString(2);
//打印所显示de数据
out.println(”<tr><td>” col1 “</td><td>” col2 “</td></tr>”);
}
out.println(”</table>”);
}
//如果加载时出错,给出相应de错误信息
catch (Exception e) {}
%>
4、打开一个浏览器窗口,输入以下地址来查看运行结果
http://localhost:8080/test/test.jsp

通过JDBC连接oracle数据库的十大技巧

星期一, 06月 2nd, 2008

Java数据库连接(JDBC)API是一系列能够让Java编程人员访问数据库de接口,各个开发商de接口并不完全相同.在使用多年deOracle公司deJDBC后,我积累了许多技巧,这些技巧能够使我更好地发挥系统de性能和实现更多de功能.
  1、在客户端软件开发中使用Thin驱动程序
  在开发Java软件方面,Oraclede数据库提供了四种类型de驱动程序,二种用于应用软件、applets、servlets等客户端软件,另外二种用于数据库中deJava存储过程等服务器端软件.在客户机端软件de开发中,我可以选择OCI驱动程序或Thin驱动程序.OCI驱动程序利用Java本地化接口(JNI),通过Oracle客户端软件与数据库进行通讯.Thin驱动程序是纯Java驱动程序,它直接与数据库进行通讯.为了获得最高de性能,Oracle建议在客户端软件de开发中使用OCI驱动程序,这似乎是正确de.但我建议使用Thin驱动程序,因为通过多次测试发现,在通常情况下,Thin驱动程序de性能都超过了OCI驱动程序.
  2、关闭自动提交功能,提高系统性能
  在第一次建立与数据库de连接时,在缺省情况下,连接是在自动提交模式下de.为了获得更好de性能,可以通过调用带布尔值false参数deConnection类desetAutoCommit()方法关闭自动提交功能,如下所示:
  conn.setAutoCommit(false);
  值得注意de是,一旦关闭了自动提交功能,我就需要通过调用Connection类decommit()和rollback()方法来人工de方式对事务进行管理.
  3、在动态SQL或有时间限制de命令中使用Statement对象
  在执行SQL命令时,我有二种选择:可以使用PreparedStatement对象,也可以使用Statement对象.无论多少次地使用同一个SQL命令,PreparedStatement都只对它解析和编译一次.当使用Statement对象时,每次执行一个SQL命令时,都会对它进行解析和编译.这可能会使您认为,使用PreparedStatement对象比使用Statement对象de速度更快.然而,我进行de测试表明,在客户端软件中,情况并非如此.因此,在有时间限制deSQL操作中,除非成批地处理SQL命令,我应当考虑使用Statement对象.
  此外,使用Statement对象也使得编写动态SQL命令更加简单,因为我可以将字符串连接在一起,建立一个有效deSQL命令.因此,我认为,Statement对象可以使动态SQL命令de创建和执行变得更加简单.
  4、利用helper函数对动态SQL命令进行格式化
  在创建使用Statement对象执行de动态SQL命令时,我需要处理一些格式化方面de问题.例如,如果我想创建一个将名字O’Reilly插入表中deSQL命令,则必须使用二个相连de“””号替换O’Reilly中de“’”号.完成这些工作de最好de方法是创建一个完成替换操作dehelper方法,然后在连接字符串心服用公式表达一个SQL命令时,使用创建dehelper方法.与此类似de是,我可以让helper方法接受一个Date型de值,然后让它输出基于Oracledeto_date()函数de字符串表达式.
  5、利用PreparedStatement对象提高数据库de总体效率
  在使用PreparedStatement对象执行SQL命令时,命令被数据库进行解析和编译,然后被放到命令缓冲区.然后,每当执行同一个PreparedStatement对象时,它就会被再解析一次,但不会被再次编译.在缓冲区中可以发现预编译de命令,并且可以重新使用.在有大量用户de企业级应用软件中,经常会重复执行相同deSQL命令,使用PreparedStatement对象带来de编译次数de减少能够提高数据库de总体性能.如果不是在客户端创建、预备、执行PreparedStatement任务需要de时间长于Statement任务,我会建议在除动态SQL命令之外de所有情况下使用PreparedStatement对象.
  6、在成批处理重复de插入或更新操作中使用PreparedStatement对象
  如果成批地处理插入和更新操作,就能够显著地减少它们所需要de时间.Oracle提供deStatement和 CallableStatement并不真正地支持批处理,只有PreparedStatement对象才真正地支持批处理.我可以使用addBatch()和executeBatch()方法选择标准deJDBC批处理,或者通过利用PreparedStatement对象desetExecuteBatch()方法和标准deexecuteUpdate()方法选择速度更快deOracle专有de方法.要使用Oracle专有de批处理机制,可以以如下所示de方式调用setExecuteBatch():
PreparedStatement pstmt3D null;
try {
((OraclePreparedStatement)
pstmt).setExecuteBatch(30);

pstmt.executeUpdate();
}

  调用setExecuteBatch()时指定de值是一个上限,当达到该值时,就会自动地引发SQL命令执行,标准deexecuteUpdate()方法就会被作为批处理送到数据库中.我可以通过调用PreparedStatement类desendBatch()方法随时传输批处理任务.
  7、使用Oracle locator方法插入、更新大对象(LOB)
  OracledePreparedStatement类不完全支持BLOB和CLOB等大对象de处理,尤其是Thin驱动程序不支持利用PreparedStatement对象desetObject()和setBinaryStream()方法设置BLOBde值,也不支持利用setCharacterStream()方法设置CLOBde值.只有locator本身中de方法才能够从数据库中获取LOB类型de值.可以使用PreparedStatement对象插入或更新LOB,但需要使用locator才能获取LOBde值.由于存在这二个问题,因此,我建议使用locatorde方法来插入、更新或获取LOBde值.
  8、使用SQL92语法调用存储过程
  在调用存储过程时,我可以使用SQL92或Oracle PL/SQL,由于使用Oracle PL/SQL并没有什么实际de好处,而且会给以后维护您de应用程序de开发人员带来麻烦,因此,我建议在调用存储过程时使用SQL92.
  9、使用Object SQL将对象模式转移到数据库中
  既然可以将Oraclede数据库作为一种面向对象de数据库来使用,就可以考虑将应用程序中de面向对象模式转到数据库中.目前de方法是创建Java bean作为伪装de数据库对象,将它们de属性映射到关系表中,然后在这些bean中添加方法.尽管这样作在Java中没有什么问题,但由于操作都是在数据库之外进行de,因此其他访问数据库de应用软件无法利用对象模式.如果利用Oraclede面向对象de技术,可以通过创建一个新de数据库对象类型在数据库中模仿其数据和操作,然后使用JPublisher等工具生成自己deJava bean类.如果使用这种方式,不但Java应用程序可以使用应用软件de对象模式,其他需要共享您de应用中de数据和操作de应用软件也可以使用应用软件中de对象模式.
  10、利用SQL完成数据库内de操作
  我要向大家介绍de最重要de经验是充分利用SQLde面向集合de方法来解决数据库处理需求,而不是使用Java等过程化de编程语言.
  如果编程人员要在一个表中查找许多行,结果中de每个行都会查找其他表中de数据,最后,编程人员创建了独立deUPDATE命令来成批地更新第一个表中de数据.与此类似de任务可以通过在set子句中使用多列子查询而在一个UPDATE命令中完成.当能够在单一deSQL命令中完成任务,何必要让数据在网上流来流去de?我建议用户认真学习如何最大限度地发挥SQLde功能.