(转贴) jqGrid整理

原帖地址:
http://www.cnblogs.com/mycoding/archive/2011/07/07/2099878.html

一、 jqGrid的加载。

1.引用相关头文件

引入CSS:

<link href="Scripts/jquery-ui-1.8.1.custom.css" rel="stylesheet" type="text/css" />

<link href="Scripts/ui.jqgrid.css" rel="stylesheet" type="text/css" />

引入JS:

<script src="Scripts/jquery-1.5.1.js" type="text/javascript"></script>

<script src="Scripts/jquery-ui.min.js" type="text/javascript"></script>

<script src="Scripts/grid.locale-en.js" type="text/javascript"></script>

<script src="Scripts/jquery.jqGrid.min.js" type="text/javascript"></script>

因为jqGrid3.6及以后的版本集成了jQuery UI,所以,此处需要导入UI相关js和css。另外grid.locale-en.js这个语言文件必须在jquery.jqGrid.min.js之前加载,否则会出问题。

2.将jqgrid加入页面中

根据jqGrid的文档,要想生成一个jqGrid,最直接的方法就是:

$("#list").jqGrid(options);

其中list是页面上的一个table:<table id="list"></table>

下面是一个简单的例子:

<script type="text/javascript">
 
$(document).ready(function () {
 
jQuery("#list").jqGrid({
 
url: 'Handler.ashx',
 
datatype: "json",
 
mtype: 'GET',
 
colNames: ['SalesReasonID', 'Name', 'ReasonType', 'ModifiedDate'],
 
colModel: [
 
{ name: 'SalesReasonID', index: 'SalesReasonID', width: 40, align: "left", editable: true },
 
{ name: 'Name', index: 'Name', width: 100, align: "center" },
 
{ name: 'ReasonType', index: 'ReasonType', width: 100, align: "center" },
 
{ name: 'ModifiedDate', index: 'ModifiedDate', width: 150, align: "center", search: false }
 
],
 
rowList: [10, 20, 30],
 
sortname: 'SalesReasonID',
 
viewrecords: true,
 
sortorder: "desc",
 
jsonReader: {
 
root: "griddata",
 
total: "totalpages",
 
page: "currpage",
 
records: "totalrecords",
 
repeatitems: false
 
},
 
pager: jQuery('#pager'),
 
rowNum: 5,
 
altclass: 'altRowsColour',
 
//width: 'auto',
 
width: '500',
 
height: 'auto',
 
caption: "DemoGrid"
 
}).navGrid('#pager', { add: true, edit: true, del: true,search:false,refresh:false }); ;
 
})

二、 jqgrid的重要选项

具体的options参考,可以访问jqGrid文档关于option的章节(http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options)。其中有几个是比较常用的,重点介绍一下:

  • url :jqGrid控件通过这个参数得到需要显示的数据,具体的返回值可以使XML也可以是Json。
  • datatype :这个参数用于设定将要得到的数据类型。类型包括:json 、xml、xmlstring、local、javascript、function。
  • mtype : 定义使用哪种方法发起请求,GET或者POST。
  • height :Grid的高度,可以接受数字、%值、auto,默认值为150。
  • width :Grid的宽度,如果未设置,则宽度应为所有列宽的之和;如果设置了宽度,则每列的宽度将会根据shrinkToFit选项的设置,进行设置。
  • shrinkToFit :此选项用于根据width计算每列宽度的算法。默认值为true。如果shrinkToFit为true且设置了width值,则每列宽度会根据 width成比例缩放;如果shrinkToFit为false且设置了width值,则每列的宽度不会成比例缩放,而是保持原有设置,而Grid将会有 水平滚动条。
  • autowidth :默认值为false。如果设为true,则Grid的宽度会根据父容器的宽度自动重算。重算仅发生在Grid初始化的阶段;如果当父容器尺寸变化了,同时也需要变化Grid的尺寸的话,则需要在自己的代码中调用setGridWidth方法来完成。
  • pager :定义页码控制条Page Bar,在上面的例子中是用一个div(<div id=”pager”></div>)来放置的。
  • sortname :指定默认的排序列,可以是列名也可以是数字。此参数会在被传递到Server端。
  • viewrecords :设置是否在Pager Bar显示所有记录的总数。
  • caption :设置Grid表格的标题,如果未设置,则标题区域不显示。
  • rowNum :用于设置Grid中一次显示的行数,默认值为20。正是这个选项将参数rows(prmNames中设置的)通过url选项设置的链接传递到Server。注意如果Server返回的数据行数超过了rowNum的设定,则Grid也只显示rowNum设定的行数。
  • rowList :一个数组,用于设置Grid可以接受的rowNum值。例如[10,20,30]。
  • colNames :字符串数组,用于指定各列的题头文本,与列的顺序是对应的。
  • colModel :最重要的数组之一,用于设定各列的参数。(稍后详述)
  • prmNames :这是一个数组,用于设置jqGrid将要向Server传递的参数名称。(稍后详述)
  • jsonReader :这又是一个数组,用来设定如何解析从Server端发回来的json数据。(稍后详述)

2.1 prmNames选项

prmNames是jqGrid的一个重要选项,用于设置jqGrid将要向Server传递的参数名称。其默认值为:

prmNames : {

page:"page", // 表示请求页码的参数名称

rows:"rows", // 表示请求行数的参数名称

sort: "sidx", // 表示用于排序的列名的参数名称

order: "sord", // 表示采用的排序方式的参数名称

search:"_search", // 表示是否是搜索请求的参数名称

nd:"nd", // 表示已经发送请求的次数的参数名称

id:"id", // 表示当在编辑数据模块中发送数据时,使用的id的名称

oper:"oper", // operation参数名称

editoper:"edit", // 当在edit模式中提交数据时,操作的名称

addoper:"add", // 当在add模式中提交数据时,操作的名称

deloper:"del", // 当在delete模式中提交数据时,操作的名称

subgridid:"id", // 当点击以载入数据到子表时,传递的数据名称

npage: null,

totalrows:"totalrows" // 表示需从Server得到总共多少行数据的参数名称,参见jqGrid选项中的rowTotal

}

2.2 jsonReader选项

jsonReader是jqGrid的一个重要选项,用于设置如何解析从Server端发回来的json数据,如果Server返回的是xml数据,则对应的使用xmlReader来解析。jsonReader的默认值为:

jsonReader : {

root: "rows", // json中代表实际模型数据的入口

page: "page", // json中代表当前页码的数据

total: "total", // json中代表页码总数的数据

records: "records", // json中代表数据行总数的数据

repeatitems: true, // 如果设为false,则jqGrid在解析json时,会根据name来搜索对应的数据元素(即可以json中元素可以不按顺序);而所使用的name是来自于colModel中的name设定。

cell: "cell",

id: "id",

userdata: "userdata",

subgrid: {

root:"rows",

repeatitems: true,

cell:"cell"

}

}

假如有下面一个json字符串:

{"totalpages":"3","currpage":"1","totalrecords":"11","griddata": [{"SalesReasonID":"1","Name":"Price","ReasonType":"Other","ModifiedDate":"1998 年6月1日"},{"SalesReasonID":"2","Name":"On Promotion","ReasonType":"Promotion","ModifiedDate":"1998年6月1日"}, {"SalesReasonID":"3","Name":"Magazine Advertisement","ReasonType":"Marketing","ModifiedDate":"1998年6月1日"}, {"SalesReasonID":"4","Name":"Television Advertisement","ReasonType":"Marketing","ModifiedDate":"1998年6月1日"}, {"SalesReasonID":"5","Name":"Manufacturer","ReasonType":"Other","ModifiedDate":"1998 年6月1日"}]}

其对应的jsonReader为:jsonReader: {

root: "griddata",

total: "totalpages",

page: "currpage",

records: "totalrecords",

repeatitems: false

}

注:cell、id在repeatitems为true时可以用到,即每一个记录是由一对id和cell组合而成,即可以适用另一种json结构。援引文档中的例子:

repeatitems为true时:

jQuery("#gridid").jqGrid({  

     ...  

     jsonReader : {  

         root:"invdata",  

         page: "currpage",  

         total: "totalpages",  

         records: "totalrecords"

     },  

     ...  

});  

json结构为:

{   

"totalpages": "xxx",   

"currpage": "yyy",  

"totalrecords": "zzz",  

"invdata" : [  

                  {"id" :"1", "cell" :["cell11", "cell12", "cell13"]},   // cell中不需要各列的name,只要值就OK了,但是需要保持对应

                  {"id" :"2", "cell" :["cell21", "cell22", "cell23"]},  

                  ...  

     ]  

}  

repeatitems为false时:

jQuery("#gridid").jqGrid({  

     ...  

     jsonReader : {  

         root:"invdata",  

         page: "currpage",  

         total: "totalpages",  

         records: "totalrecords",  

         repeatitems: false,  

         id: "0"

     },  

     ...  

});  

json结构为:

{   

"totalpages" : "xxx",   

"currpage" : "yyy",  

"totalrecords" : "zzz",  

"invdata" : [  

                 {"invid" : "1","invdate":"cell11", "amount" :"cell12", "tax" :"cell13", "total" :"1234", "note" :"somenote"}, // 数据中需要各列的name,但是可以不按列的顺序

                  {"invid" : "2","invdate":"cell21", "amount" :"cell22", "tax" :"cell23", "total" :"2345", "note" :"some note"},  

                  ...  

     ]  

}  

2.3 colModel的重要选项

colModel也有许多非常重要的选项,在使用搜索、排序等方面都会用到。这里先只说说最基本的。

  • name :为Grid中的每个列设置唯一的名称,这是一个必需选项,其中保留字包括subgrid、cb、rn。
  • index :设置排序时所使用的索引名称,这个index名称会作为sidx参数(prmNames中设置的)传递到Server。
  • label :当jqGrid的colNames选项数组为空时,为各列指定题头。如果colNames和此项都为空时,则name选项值会成为题头。
  • width :设置列的宽度,目前只能接受以px为单位的数值,默认为150。
  • sortable :设置该列是否可以排序,默认为true。
  • search :设置该列是否可以被列为搜索条件,默认为true。
  • resizable :设置列是否可以变更尺寸,默认为true。
  • hidden :设置此列初始化时是否为隐藏状态,默认为false。
  • formatter :预设类型或用来格式化该列的自定义函数名。常用预设格式有:integer、date、currency、number等(具体参见文档 )。

三、 注意事项

1. 动态改变Add Form或者Edit Form中的select的内容,如:改变下图中的Comparator下拉中的内容。

clip_image002

$("#list_d").navGrid('#pager_d',{add:true,edit:true,del:true,search:false,refresh:false},

{

checkOnSubmit:false, closeAfterEdit: true,recreateForm:true,

beforeInitData:function(formid){

initComparator();

},

beforeShowForm: function(formid){

$("#list_d").jqGrid('setColProp', 'Name', { editrules:{required:false},});

$('#tr_Name', formid).hide();

}

},//edit

{},//add

{}//del

beforeInitData, beforeShowForm在每次点击编辑的时候都会执行。initComparator的作用是通过ajax获取数据,然后利 用$("#list_d").jqGrid('setColProp', 'Comparator', { editoptions: { value: valueString} });来设置Comparator下拉中的内容。其中valueString的格式如下’ equal to: equal to; not equal to: not equal to’。键值之间用冒号隔开,2项之间用分号隔开。注意:把recreateForm设为true,否则'setColProp'只在第一次调用时有效。

2. var rowNum = parseInt($(this).getGridParam("records"), 10); 得到数据条数。

3. jQuery("#list_d").clearGridData();清空数据。

4. jQuery("#list").getCell(ids,"Key");获取第ids行的key列。

5. $("#list").jqGrid('setSelection', "1");选中第一行。放在loadComplete:中在gird加载完成的时候自动选中第一行。 loadComplete:function(data){$("#list").jqGrid('setSelection', "1");

}

6. 对于像1中的可编辑的字段,可以设定rule,参见http://www.trirand.com/jqgridwiki/doku.php?id=wiki:common_rules#editrules

7. 修改Option,以URL为例

jQuery("#list_d").jqGrid('setGridParam',{url:"xxx.aspx",page:1}).trigger('reloadGrid');


复杂的表格可以参考jquery grid demo网站 :




posted @ 2011-11-01 14:23 AK47 阅读(2309) | 评论 (0)编辑 收藏

(转载)Spring 注解@Component,@Service,@Controller,@Repository

Spring 2.5 中除了提供 @Component 注释外,还定义了几个拥有特殊语义的注释,它们分别是:@Repository、@Service 和 @Controller。在目前的 Spring 版本中,这 3 个注释和 @Component 是等效的,但是从注释类的命名上,很容易看出这 3 个注释分别和持久层、业务层和控制层(Web 层)相对应。虽然目前这 3 个注释和 @Component 相比没有什么新意,但 Spring 将在以后的版本中为它们添加特殊的功能。所以,如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释,而用 @Component 对那些比较中立的类进行注释。

在 一个稍大的项目中,通常会有上百个组件,如果这些组件采用xml的bean定义来配置,显然会增加配置文件的体积,查找以及维护起来也不太方便。 Spring2.5为我们引入了组件自动扫描机制,他可以在类路径底下寻找标注了 @Component,@Service,@Controller,@Repository注解的类,并把这些类纳入进spring容器中管理。它的作用 和在xml文件中使用bean节点配置组件时一样的。要使用自动扫描机制,我们需要打开以下配置信息: 
Java代码

1. <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   http://www.springframework.org/schema/context   http://www.springframework.org/schema/context/spring-context-2.5.xsd"  
2. >  
3.   
4. <context:component-scan base-package=”com.eric.spring”>   
5. </beans>   
   /*其中base-package为需要扫描的包(含所有子包)

     @Service用于标注业务层组件,

     @Controller用于标注控制层组件(如struts中的action),

     @Repository用于标注数据访问组件,即DAO组件,

     @Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

    */   


6. @Service public class VentorServiceImpl implements iVentorService {   
7. } @Repository public class VentorDaoImpl implements iVentorDao {  
8. }

/*getBean的默认名称是类名(头字母小 写),如果想自定义,可以@Service(“aaaaa”)这样来指定,这种bean默认是单例的,如果想改变,可以使用 @Service(“beanName”) @Scope(“prototype”)来改变。可以使用以下方式指定初始化方法和销毁方法(方法名任意): @PostConstruct public void init() {  

*/
9. }  
10. @PreDestroy public void destory() {  
11. } 

注入方式:

把 DAO实现类注入到service实现类中,把service的接口(注意不要是service的实现类)注入到action中,注入时不要new 这个注入的类,因为spring会自动注入,如果手动再new的话会出现错误,然后属性加上@Autowired后不需要getter()和 setter()方法,Spring也会自动注入。至于更具体的内容,等对注入的方式更加熟练后会做个完整的例子上来。

注解:

在 spring的配置文件里面只需要加上<context:annotation-config/> 和<context:component-scan base-package="需要实现注入的类所在包"/>,可以使用base-package="*"表示全部的类。   

<context:component-scan base-package=”com.eric.spring”> 

其中base-package为需要扫描的包(含所有子包)

在接口前面标上@Autowired和@Qualifier注释使得接口可以被容器注入,当接口存在两个实现类的时候必须指定其中一个来注入,使用实现类首字母小写的字符串来注入,如:

  1.     @Autowired     
  2.   
  3.     @Qualifier("chinese")      
  4.   
  5.     private Man man;   

否则可以省略,只写@Autowired   。 

@Service服务层组件,用于标注业务层组件,表示定义一个bean,自动根据bean的类名实例化一个首写字母为小写的bean,例如Chinese实例化为chinese,如果需要自己改名字则:@Service("你自己改的bean名")。   

@Controller用于标注控制层组件(如struts中的action)

@Repository持久层组件,用于标注数据访问组件,即DAO组件

@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。 


@Service 
public class VentorServiceImpl implements iVentorService { 
}

@Repository 
public class VentorDaoImpl implements iVentorDao { 


getBean 的默认名称是类名(头字母小写),如果想自定义,可以@Service(“aaaaa”) 这样来指定,这种

bean默认是单例的,如果想改变,可以使用@Service(“beanName”) @Scope(“prototype”)来改变。

可以使用以下方式指定初始化方法和销毁方法(方法名任意):

@PostConstruct

public void init() { 



@PreDestroy

public void destory() { 

}

posted @ 2011-10-10 16:46 AK47 阅读(49700) | 评论 (3)编辑 收藏

(转贴)使用 Spring 2.5 注释驱动的 IoC 功能

原帖地址
http://www.ibm.com/developerworks/cn/java/j-lo-spring25-ioc/

概述

注释配置相对于 XML 配置具有很多的优势:

  • 它可以充分利用 Java 的反射机制获取类结构信息,这些信息可以有效减少配置的工作。如使用 JPA 注释配置 ORM 映射时,我们就不需要指定 PO 的属性名、类型等信息,如果关系表字段和 PO 属性名、类型都一致,您甚至无需编写任务属性映射信息——因为这些信息都可以通过 Java 反射机制获取。
  • 注释和 Java 代码位于一个文件中,而 XML 配置采用独立的配置文件,大多数配置信息在程序开发完成后都不会调整,如果配置信息和 Java 代码放在一起,有助于增强程序的内聚性。而采用独立的 XML 配置文件,程序员在编写一个功能时,往往需要在程序文件和配置文件中不停切换,这种思维上的不连贯会降低开发效率。

因此在很多情况下,注释配置比 XML 配置更受欢迎,注释配置有进一步流行的趋势。Spring 2.5 的一大增强就是引入了很多注释类,现在您已经可以使用注释配置完成大部分 XML 配置的功能。在这篇文章里,我们将向您讲述使用注释进行 Bean 定义和依赖注入的内容。

 
原来我们是怎么做的      
在使用注释配置之前,先来回顾一下传统上是如何配置 Bean 并完成 Bean 之间依赖关系的建立。下面是 3 个类,它们分别是 Office、Car 和 Boss,这 3 个类需要在 Spring 容器中配置为 Bean:    
   
Office 仅有一个属性:    
     
清单 1. Office.java    
                    
package com.baobaotao;    
public class Office {    
    private String officeNo =”001”;    
   
    //省略 get/setter    
   
    @Override   
    public String toString() {    
        return "officeNo:" + officeNo;    
    }    
}    
       
Car 拥有两个属性:    
     
清单 2. Car.java 
                     
package com.baobaotao;    
   
public class Car {    
    private String brand;    
    private double price;    
   
    // 省略 get/setter    
   
    @Override   
    public String toString() {    
        return "brand:" + brand + "," + "price:" + price;    
    }    
}    
      
Boss 拥有 Office 和 Car 类型的两个属性:    
  
清单 3. Boss.java    
                    
package com.baobaotao;    
   
public class Boss {    
    private Car car;    
    private Office office;    
   
    // 省略 get/setter    
   
    @Override   
    public String toString() {    
        return "car:" + car + "\n" + "office:" + office;    
    }    
}    
    
我们在 Spring 容器中将 Office 和 Car 声明为 Bean,并注入到 Boss Bean 中:下面是使用传统 XML 完成这个工作的配置文件 beans.xml:    
    
清单 4. beans.xml 将以上三个类配置成 Bean    
                    
<?xml version="1.0" encoding="UTF-8" ?>    
<beans xmlns="http://www.springframework.org/schema/beans"   
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    xsi:schemaLocation="http://www.springframework.org/schema/beans     
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">    
    <bean id="boss" class="com.baobaotao.Boss">    
        <property name="car" ref="car"/>    
        <property name="office" ref="office" />    
    </bean>    
    <bean id="office" class="com.baobaotao.Office">    
        <property name="officeNo" value="002"/>    
    </bean>    
    <bean id="car" class="com.baobaotao.Car" scope="singleton">    
        <property name="brand" value=" 红旗 CA72"/>    
        <property name="price" value="2000"/>    
    </bean>    
</beans>    
     
当我们运行以下代码时,控制台将正确打出 boss 的信息:    
  
清单 5. 测试类:AnnoIoCTest.java    
                    
import org.springframework.context.ApplicationContext;    
import org.springframework.context.support.ClassPathXmlApplicationContext;    
public class AnnoIoCTest {    
   
    public static void main(String[] args) {    
        String[] locations = {"beans.xml"};    
        ApplicationContext ctx =     
            new ClassPathXmlApplicationContext(locations);    
        Boss boss = (Boss) ctx.getBean("boss");    
        System.out.println(boss);    
    }    
}    
    
这说明 Spring 容器已经正确完成了 Bean 创建和装配的工作。    
     
使用 @Autowired 注释    
   
Spring 2.5 引入了 @Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。来看一下使用 @Autowired 进行成员变量自动注入的代码:    
  
清单 6. 使用 @Autowired 注释的 Boss.java    
                    
package com.baobaotao;    
import org.springframework.beans.factory.annotation.Autowired;    
   
public class Boss {    
   
    @Autowired   
    private Car car;    
   
    @Autowired   
    private Office office;    
   
    …    
}    
       
Spring 通过一个 BeanPostProcessor 对 @Autowired 进行解析,所以要让 @Autowired 起作用必须事先在 Spring 容器中声明 AutowiredAnnotationBeanPostProcessor Bean。   

清单 7. 让 @Autowired 注释工作起来    
                    
<?xml version="1.0" encoding="UTF-8" ?>    
<beans xmlns="http://www.springframework.org/schema/beans"   
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    xsi:schemaLocation="http://www.springframework.org/schema/beans     
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">    
   
    <!-- 该 BeanPostProcessor 将自动起作用,对标注 @Autowired 的 Bean 进行自动注入 -->    
    <bean class="AutowiredAnnotationBeanPostProcessor  
        org.springframework.beans.factory.annotation.  "/>    
   
    <!-- 移除 boss Bean 的属性注入配置的信息 -->    
    <bean id="boss" class="com.baobaotao.Boss"/>    
     
    <bean id="office" class="com.baobaotao.Office">    
        <property name="officeNo" value="001"/>    
    </bean>    
    <bean id="car" class="com.baobaotao.Car" scope="singleton">    
        <property name="brand" value=" 红旗 CA72"/>    
        <property name="price" value="2000"/>    
    </bean>    
</beans>    
     
    
这 样,当 Spring 容器启动时,AutowiredAnnotationBeanPostProcessor 将扫描 Spring 容器中所有 Bean,当发现 Bean 中拥有 @Autowired 注释时就找到和其匹配(默认按类型匹配)的 Bean,并注入到对应的地方中去。    
   
按 照上面的配置,Spring 将直接采用 Java 反射机制对 Boss 中的 car 和 office 这两个私有成员变量进行自动注入。所以对成员变量使用 @Autowired 后,您大可将它们的 setter 方法(setCar() 和 setOffice())从 Boss 中删除。    
   
当然,您也可以通过 @Autowired 对方法或构造函数进行标注,来看下面的代码:    
    
清单 8. 将 @Autowired 注释标注在 Setter 方法上    
                    
package com.baobaotao;    
   
public class Boss {    
    private Car car;    
    private Office office;    
   
     @Autowired   
    public void setCar(Car car) {    
        this.car = car;    
    }    
     
    @Autowired   
    public void setOffice(Office office) {    
        this.office = office;    
    }    
    …    
}    
     
这时,@Autowired 将查找被标注的方法的入参类型的 Bean,并调用方法自动注入这些 Bean。而下面的使用方法则对构造函数进行标注:    
    
清单 9. 将 @Autowired 注释标注在构造函数上    
                    
package com.baobaotao;    
   
public class Boss {    
    private Car car;    
    private Office office;    
     
    @Autowired   
    public Boss(Car car ,Office office){    
        this.car = car;    
        this.office = office ;    
    }    
     
    …    
}    
       
由于 Boss() 构造函数有两个入参,分别是 car 和 office,@Autowired 将分别寻找和它们类型匹配的 Bean,将它们作为 Boss(Car car ,Office office) 的入参来创建 Boss Bean。    
     
当候选 Bean 数目不为 1 时的应对方法    
   
在 默认情况下使用 @Autowired 注释进行自动注入时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。当找不到一个匹配的 Bean 时,Spring 容器将抛出 BeanCreationException 异常,并指出必须至少拥有一个匹配的 Bean。我们可以来做一个实验:    
   
   
清单 10. 候选 Bean 数目为 0 时    
                    
<?xml version="1.0" encoding="UTF-8" ?>    
<beans xmlns="http://www.springframework.org/schema/beans"   
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
     xsi:schemaLocation="http://www.springframework.org/schema/beans     
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ">    
     
    <bean class="AutowiredAnnotationBeanPostProcessor  
        org.springframework.beans.factory.annotation.  "/>     
   
    <bean id="boss" class="com.baobaotao.Boss"/>    
   
    <!-- 将 office Bean 注释掉 -->    
    <!-- <bean id="office" class="com.baobaotao.Office">    
    <property name="officeNo" value="001"/>    
    </bean>-->    
   
    <bean id="car" class="com.baobaotao.Car" scope="singleton">    
        <property name="brand" value=" 红旗 CA72"/>    
        <property name="price" value="2000"/>    
    </bean>    
</beans>    
     
由于 office Bean 被注释掉了,所以 Spring 容器中将没有类型为 Office 的 Bean 了,而 Boss 的 office 属性标注了 @Autowired,当启动 Spring 容器时,异常就产生了。    
   
当 不能确定 Spring 容器中一定拥有某个类的 Bean 时,可以在需要自动注入该类 Bean 的地方可以使用 @Autowired(required = false),这等于告诉 Spring:在找不到匹配 Bean 时也不报错。来看一下具体的例子:    
   
   
清单 11. 使用 @Autowired(required = false)    
                    
package com.baobaotao;    
   
import org.springframework.beans.factory.annotation.Autowired;    
import org.springframework.beans.factory.annotation.Required;    
   
public class Boss {    
   
    private Car car;    
    private Office office;    
   
    @Autowired   
    public void setCar(Car car) {    
        this.car = car;    
    }    
    @Autowired(required = false)    
    public void setOffice(Office office) {    
        this.office = office;    
    }    
    …    
}    
    
当 然,一般情况下,使用 @Autowired 的地方都是需要注入 Bean 的,使用了自动注入而又允许不注入的情况一般仅会在开发期或测试期碰到(如为了快速启动 Spring 容器,仅引入一些模块的 Spring 配置文件),所以 @Autowired(required = false) 会很少用到。    
   
和找不到一个类型匹配 Bean 相反的一个错误是:如果 Spring 容器中拥有多个候选 Bean,Spring 容器在启动时也会抛出 BeanCreationException 异常。来看下面的例子:    
    
清单 12. 在 beans.xml 中配置两个 Office 类型的 Bean    
                    
…     
<bean id="office" class="com.baobaotao.Office">    
    <property name="officeNo" value="001"/>    
</bean>    
<bean id="office2" class="com.baobaotao.Office">    
    <property name="officeNo" value="001"/>    
</bean>    
…    
     
我们在 Spring 容器中配置了两个类型为 Office 类型的 Bean,当对 Boss 的 office 成员变量进行自动注入时,Spring 容器将无法确定到底要用哪一个 Bean,因此异常发生了。    
   
Spring 允许我们通过 @Qualifier 注释指定注入 Bean 的名称,这样歧义就消除了,可以通过下面的方法解决异常:    
  
清单 13. 使用 @Qualifier 注释指定注入 Bean 的名称    
                    
@Autowired   
public void setOffice(@Qualifier("office")Office office) {    
    this.office = office;    
}    
    
 
@Qualifier("office") 中的 office 是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。@Autowired 可以对成员变量、方法以及构造函数进行注释,而 @Qualifier 的标注对象是成员变量、方法入参、构造函数入参。正是由于注释对象的不同,所以 Spring 不将 @Autowired 和 @Qualifier 统一成一个注释类。下面是对成员变量和构造函数入参进行注释的代码:    
   
对成员变量进行注释:    
  
清单 14. 对成员变量使用 @Qualifier 注释    
                    
public class Boss {    
    @Autowired   
    private Car car;    
     
    @Autowired   
    @Qualifier("office")    
    private Office office;    
    …    
}    
     
    
对构造函数入参进行注释:    
    
清单 15. 对构造函数变量使用 @Qualifier 注释    
                    
public class Boss {    
    private Car car;    
    private Office office;    
   
    @Autowired   
    public Boss(Car car , @Qualifier("office")Office office){    
        this.car = car;    
        this.office = office ;    
    }    
}    
     
@Qualifier 只能和 @Autowired 结合使用,是对 @Autowired 有益的补充。一般来讲,@Qualifier 对方法签名中入参进行注释会降低代码的可读性,而对成员变量注释则相对好一些。    
    
   
使用 JSR-250 的注释    
   
Spring 不但支持自己定义的 @Autowired 的注释,还支持几个由 JSR-250 规范定义的注释,它们分别是 @Resource、@PostConstruct 以及 @PreDestroy。    
   
@Resource   
   
@Resource 的作用相当于 @Autowired,只不过 @Autowired 按 byType 自动注入,面 @Resource 默认按 byName 自动注入罢了。@Resource 有两个属性是比较重要的,分别是 name 和 type,Spring 将 @Resource 注释的 name 属性解析为 Bean 的名字,而 type 属性则解析为 Bean 的类型。所以如果使用 name 属性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略。如果既不指定 name 也不指定 type 属性,这时将通过反射机制使用 byName 自动注入策略。    
   
Resource 注释类位于 Spring 发布包的 lib/j2ee/common-annotations.jar 类包中,因此在使用之前必须将其加入到项目的类库中。来看一个使用 @Resource 的例子:    
   
清单 16. 使用 @Resource 注释的 Boss.java    
                    
package com.baobaotao;    
   
import javax.annotation.Resource;    
   
public class Boss {    
    // 自动注入类型为 Car 的 Bean    
    @Resource   
    private Car car;    
   
    // 自动注入 bean 名称为 office 的 Bean    
    @Resource(name = "office")    
    private Office office;    
}    
     
一般情况下,我们无需使用类似于 @Resource(type=Car.class) 的注释方式,因为 Bean 的类型信息可以通过 Java 反射从代码中获取。    
   
要让 JSR-250 的注释生效,除了在 Bean 类中标注这些注释外,还需要在 Spring 容器中注册一个负责处理这些注释的 BeanPostProcessor:    
   
<bean     
  class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>    
     
  
CommonAnnotationBeanPostProcessor 实现了 BeanPostProcessor 接口,它负责扫描使用了 JSR-250 注释的 Bean,并对它们进行相应的操作。    
   
@PostConstruct 和 @PreDestroy   
   
Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,您既可以通过实现 InitializingBean/DisposableBean 接口来定制初始化之后 / 销毁之前的操作方法,也可以通过 <bean> 元素的 init-method/destroy-method 属性指定初始化之后 / 销毁之前调用的操作方法。关于 Spring 的生命周期,笔者在《精通 Spring 2.x—企业应用开发精解》第 3 章进行了详细的描述,有兴趣的读者可以查阅。    
   
JSR-250 为初始化之后/销毁之前方法的指定定义了两个注释类,分别是 @PostConstruct 和 @PreDestroy,这两个注释只能应用于方法上。标注了 @PostConstruct 注释的方法将在类实例化后调用,而标注了 @PreDestroy 的方法将在类销毁之前调用。    
  
清单 17. 使用 @PostConstruct 和 @PreDestroy 注释的 Boss.java    
                    
package com.baobaotao;    
   
import javax.annotation.Resource;    
import javax.annotation.PostConstruct;    
import javax.annotation.PreDestroy;    
   
public class Boss {    
    @Resource   
    private Car car;    
   
    @Resource(name = "office")    
    private Office office;    
   
    @PostConstruct   
    public void postConstruct1(){    
        System.out.println("postConstruct1");    
    }    
   
    @PreDestroy   
    public void preDestroy1(){    
        System.out.println("preDestroy1");     
    }    
    …    
}    
     
您只需要在方法前标注 @PostConstruct 或 @PreDestroy,这些方法就会在 Bean 初始化后或销毁之前被 Spring 容器执行了。    
   
我 们知道,不管是通过实现 InitializingBean/DisposableBean 接口,还是通过 <bean> 元素的 init-method/destroy-method 属性进行配置,都只能为 Bean 指定一个初始化 / 销毁的方法。但是使用 @PostConstruct 和 @PreDestroy 注释却可以指定多个初始化 / 销毁方法,那些被标注 @PostConstruct 或 @PreDestroy 注释的方法都会在初始化 / 销毁时被执行。    
   
通过以下的测试代码,您将可以看到 Bean 的初始化 / 销毁方法是如何被执行的:    
  
清单 18. 测试类代码    
                    
package com.baobaotao;    
   
import org.springframework.context.support.ClassPathXmlApplicationContext;    
   
public class AnnoIoCTest {    
   
    public static void main(String[] args) {    
        String[] locations = {"beans.xml"};    
        ClassPathXmlApplicationContext ctx =     
            new ClassPathXmlApplicationContext(locations);    
        Boss boss = (Boss) ctx.getBean("boss");    
        System.out.println(boss);    
        ctx.destroy();// 关闭 Spring 容器,以触发 Bean 销毁方法的执行    
    }    
}    
     
   
这 时,您将看到标注了 @PostConstruct 的 postConstruct1() 方法将在 Spring 容器启动时,创建 Boss Bean 的时候被触发执行,而标注了 @PreDestroy 注释的 preDestroy1() 方法将在 Spring 容器关闭前销毁 Boss Bean 的时候被触发执行。    
       
使用 <context:annotation-config/> 简化配置    
   
Spring 2.1 添加了一个新的 context 的 Schema 命名空间,该命名空间对注释驱动、属性文件引入、加载期织入等功能提供了便捷的配置。我们知道注释本身是不会做任何事情的,它仅提供元数据信息。要使元数 据信息真正起作用,必须让负责处理这些元数据的处理器工作起来。     
   
而我们前面所介绍的 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 就是处理这些注释元数据的处理器。但是直接在 Spring 配置文件中定义这些 Bean 显得比较笨拙。Spring 为我们提供了一种方便的注册这些 BeanPostProcessor 的方式,这就是 <context:annotation-config/>。请看下面的配置:    
     
清单 19. 调整 beans.xml 配置文件    
                    
<?xml version="1.0" encoding="UTF-8" ?>    
<beans xmlns="http://www.springframework.org/schema/beans"   
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
     xmlns:context="http://www.springframework.org/schema/context"   
     xsi:schemaLocation="http://www.springframework.org/schema/beans     
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd    
 http://www.springframework.org/schema/context     
 http://www.springframework.org/schema/context/spring-context-2.5.xsd">    
     
    <context:annotation-config/>     
   
    <bean id="boss" class="com.baobaotao.Boss"/>    
    <bean id="office" class="com.baobaotao.Office">    
        <property name="officeNo" value="001"/>    
    </bean>    
    <bean id="car" class="com.baobaotao.Car" scope="singleton">    
        <property name="brand" value=" 红旗 CA72"/>    
        <property name="price" value="2000"/>    
    </bean>    
</beans>    
      
<context:annotationconfig/> 将隐式地向 Spring 容器注册 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、 PersistenceAnnotationBeanPostProcessor 以及 equiredAnnotationBeanPostProcessor 这 4 个 BeanPostProcessor。    
   
在配置文件中使用 context 命名空间之前,必须在 <beans> 元素中声明 context 命名空间。    
   
    
使用 @Component   
   
虽 然我们可以通过 @Autowired 或 @Resource 在 Bean 类中使用自动注入功能,但是 Bean 还是在 XML 文件中通过 <bean> 进行定义 —— 也就是说,在 XML 配置文件中定义 Bean,通过 @Autowired 或 @Resource 为 Bean 的成员变量、方法入参或构造函数入参提供自动注入的功能。能否也通过注释定义 Bean,从 XML 配置文件中完全移除 Bean 定义的配置呢?答案是肯定的,我们通过 Spring 2.5 提供的 @Component 注释就可以达到这个目标了。    
   
下面,我们完全使用注释定义 Bean 并完成 Bean 之间装配:    
   
   
清单 20. 使用 @Component 注释的 Car.java    
                    
package com.baobaotao;    
   
import org.springframework.stereotype.Component;    
   
@Component   
public class Car {    
    …    
}    
     
     
仅需要在类定义处,使用 @Component 注释就可以将一个类定义了 Spring 容器中的 Bean。下面的代码将 Office 定义为一个 Bean:    
    
清单 21. 使用 @Component 注释的 Office.java    
                    
package com.baobaotao;    
   
import org.springframework.stereotype.Component;    
   
@Component   
public class Office {    
    private String officeNo = "001";    
    …    
}    
     
这样,我们就可以在 Boss 类中通过 @Autowired 注入前面定义的 Car 和 Office Bean 了。    
   
清单 22. 使用 @Component 注释的 Boss.java    
                    
package com.baobaotao;    
   
import org.springframework.beans.factory.annotation.Autowired;    
import org.springframework.beans.factory.annotation.Required;    
import org.springframework.beans.factory.annotation.Qualifier;    
import org.springframework.stereotype.Component;    
   
@Component("boss")    
public class Boss {    
    @Autowired   
    private Car car;    
   
    @Autowired   
    private Office office;    
    …    
}    
    
@Component 有一个可选的入参,用于指定 Bean 的名称,在 Boss 中,我们就将 Bean 名称定义为“boss”。一般情况下,Bean 都是 singleton 的,需要注入 Bean 的地方仅需要通过 byType 策略就可以自动注入了,所以大可不必指定 Bean 的名称。    
   
在使用 @Component 注释后,Spring 容器必须启用类扫描机制以启用注释驱动 Bean 定义和注释驱动 Bean 自动注入的策略。Spring 2.5 对 context 命名空间进行了扩展,提供了这一功能,请看下面的配置:    
    
清单 23. 简化版的 beans.xml    
                    
<?xml version="1.0" encoding="UTF-8" ?>    
<beans xmlns="http://www.springframework.org/schema/beans"   
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    xmlns:context="http://www.springframework.org/schema/context"   
    xsi:schemaLocation="http://www.springframework.org/schema/beans     
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd    
 http://www.springframework.org/schema/context     
 http://www.springframework.org/schema/context/spring-context-2.5.xsd">    
    <context:component-scan base-package="com.baobaotao"/>    
</beans>    
      
这 里,所有通过 <bean> 元素定义 Bean 的配置内容已经被移除,仅需要添加一行 <context:component-scan/> 配置就解决所有问题了——Spring XML 配置文件得到了极致的简化(当然配置元数据还是需要的,只不过以注释形式存在罢了)。<context:component-scan/> 的 base-package 属性指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。    
   
<context:component-scan/> 还允许定义过滤器将基包下的某些类纳入或排除。Spring 支持以下 4 种类型的过滤方式,通过下表说明:    
   
表 1. 扫描过滤方式    
过滤器类型 说明     
注释 假如 com.baobaotao.SomeAnnotation 是一个注释类,我们可以将使用该注释的类过滤出来。     
类名指定 通过全限定类名进行过滤,如您可以指定将 com.baobaotao.Boss 纳入扫描,而将 com.baobaotao.Car 排除在外。     
正则表达式 通过正则表达式定义过滤的类,如下所示: com\.baobaotao\.Default.*     
AspectJ 表达式 通过 AspectJ 表达式定义过滤的类,如下所示: com. baobaotao..*Service+     
   
下面是一个简单的例子:    
   
<context:component-scan base-package="com.baobaotao">    
    <context:include-filter type="regex"     
        expression="com\.baobaotao\.service\..*"/>    
    <context:exclude-filter type="aspectj"     
        expression="com.baobaotao.util..*"/>    
</context:component-scan>    
      
值 得注意的是 <context:component-scan/> 配置项不但启用了对类包进行扫描以实施注释驱动 Bean 定义的功能,同时还启用了注释驱动自动注入的功能(即还隐式地在内部注册了 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor),因此当使用 <context:component-scan/> 后,就可以将 <context:annotation-config/> 移除了。    
   
默认情况下通过 @Component 定义的 Bean 都是 singleton 的,如果需要使用其它作用范围的 Bean,可以通过 @Scope 注释来达到目标,如以下代码所示:    
   
清单 24. 通过 @Scope 指定 Bean 的作用范围    
                    
package com.baobaotao;    
import org.springframework.context.annotation.Scope;    
…    
@Scope("prototype")    
@Component("boss")    
public class Boss {    
    …    
}    
     
这样,当从 Spring 容器中获取 boss Bean 时,每次返回的都是新的实例了。    
      
采用具有特殊语义的注释    
   
Spring 2.5 中除了提供 @Component 注释外,还定义了几个拥有特殊语义的注释,它们分别是:@Repository、@Service 和 @Controller。在目前的 Spring 版本中,这 3 个注释和 @Component 是等效的,但是从注释类的命名上,很容易看出这 3 个注释分别和持久层、业务层和控制层(Web 层)相对应。虽然目前这 3 个注释和 @Component 相比没有什么新意,但 Spring 将在以后的版本中为它们添加特殊的功能。所以,如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释,而用 @Component 对那些比较中立的类进行注释。    
        
注释配置和 XML 配置的适用场合    
   
是否有了这些 IOC 注释,我们就可以完全摒除原来 XML 配置的方式呢?答案是否定的。有以下几点原因:    
   
注 释配置不一定在先天上优于 XML 配置。如果 Bean 的依赖关系是固定的,(如 Service 使用了哪几个 DAO 类),这种配置信息不会在部署时发生调整,那么注释配置优于 XML 配置;反之如果这种依赖关系会在部署时发生调整,XML 配置显然又优于注释配置,因为注释是对 Java 源代码的调整,您需要重新改写源代码并重新编译才可以实施调整。     
如果 Bean 不是自己编写的类(如 JdbcTemplate、SessionFactoryBean 等),注释配置将无法实施,此时 XML 配置是唯一可用的方式。     
注释配置往往是类级别的,而 XML 配置则可以表现得更加灵活。比如相比于 @Transaction 事务注释,使用 aop/tx 命名空间的事务配置更加灵活和简单。     
所 以在实现应用中,我们往往需要同时使用注释配置和 XML 配置,对于类级别且不会发生变动的配置可以优先考虑注释配置;而对于那些第三方类以及容易发生调整的配置则应优先考虑使用 XML 配置。Spring 会在具体实施 Bean 创建和 Bean 注入之前将这两种配置方式的元信息融合在一起。    
       
小结    
   
Spring 在 2.1 以后对注释配置提供了强力的支持,注释配置功能成为 Spring 2.5 的最大的亮点之一。合理地使用 Spring 2.5 的注释配置,可以有效减少配置的工作量,提高程序的内聚性。但是这并不意味着传统 XML 配置将走向消亡,在第三方类 Bean 的配置,以及那些诸如数据源、缓存池、持久层操作模板类、事务管理等内容的配置上,XML 配置依然拥有不可替代的地位。

posted @ 2011-10-10 15:49 AK47 阅读(319) | 评论 (0)编辑 收藏

(转贴)数据库三范式经典实例解析

数据库的设计范式是数据库设计所需要满足的规范,满足这些规范的数据库是简洁的、结构明晰的,同时,不会发生插入(insert)、删除(delete)和更新(update)操作异常。反之则是乱七八糟,不仅给数据库的编程人员制造麻烦,而且面目可憎,可能存储了大量不需要的冗余信息。
     设计范式是不是很难懂呢?非也,大学教材上给我们一堆数学公式我们当然看不懂,也记不住。所以我们很多人就根本不按照范式来设计数据库。
     实质上,设计范式用很形象、很简洁的话语就能说清楚,道明白。本文将对范式进行通俗地说明,并以笔者曾经设计的一个简单论坛的数据库为例来讲解怎样将这些范式应用于实际工程。

范式说明
     第一范式(1NF):数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。
     例如,如下的数据库表是符合第一范式:

字段1 字段2 字段3 字段4
? ? ? ?
 而这样的数据库表是不符合第一范式的:
字段1 字段2 字段3 字段4
? ? 字段3.1 字段3.2 ?

 

     很显然,在当前的任何关系数据库管理系统(DBMS)中,傻瓜也不可能做出不符合第一范式的数据库,因为这些DBMS不允许你把数据库表的一列再分成二列或多列。因此,你想在现有的DBMS中设计出不符合第一范式的数据库都是不可能的。
     第二范式(2NF):数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。
     假定选课关系表为SelectCourse(学号, 姓名, 年龄, 课程名称, 成绩, 学分),关键字为组合关键字(学号, 课程名称),因为存在如下决定关系:
     (学号, 课程名称) → (姓名, 年龄, 成绩, 学分)
     这个数据库表不满足第二范式,因为存在如下决定关系:
     (课程名称) → (学分)
     (学号) → (姓名, 年龄)
即存在组合关键字中的字段决定非关键字的情况。
     由于不符合2NF,这个选课关系表会存在如下问题:
     (1) 数据冗余:
     同一门课程由n个学生选修,"学分"就重复n-1次;同一个学生选修了m门课程,姓名和年龄就重复了m-1次。
     (2) 更新异常:
     若调整了某门课程的学分,数据表中所有行的"学分"值都要更新,否则会出现同一门课程学分不同的情况。
     (3) 插入异常:
     假设要开设一门新的课程,暂时还没有人选修。这样,由于还没有"学号"关键字,课程名称和学分也无法记录入数据库。
     (4) 删除异常:
     假设一批学生已经完成课程的选修,这些选修记录就应该从数据库表中删除。但是,与此同时,课程名称和学分信息也被删除了。很显然,这也会导致插入异常。

     把选课关系表SelectCourse改为如下三个表:
     学生:Student(学号, 姓名, 年龄);
     课程:Course(课程名称, 学分);
     选课关系:SelectCourse(学号, 课程名称, 成绩)。
     这样的数据库表是符合第二范式的,消除了数据冗余、更新异常、插入异常和删除异常。
     另外,所有单关键字的数据库表都符合第二范式,因为不可能存在组合关键字。
     第三范式(3NF):在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖,指的是如果存在"A → B → C"的决定关系,则C传递函数依赖于A。因此,满足第三范式的数据库表应该不存在如下依赖关系:
     关键字段 → 非关键字段x → 非关键字段y
     假定学生关系表为Student(学号, 姓名, 年龄, 所在学院, 学院地点, 学院电话),关键字为单一关键字"学号",因为存在如下决定关系:
     (学号) → (姓名, 年龄, 所在学院, 学院地点, 学院电话)
这个数据库是符合2NF的,但是不符合3NF,因为存在如下决定关系:
     (学号) → (所在学院) → (学院地点, 学院电话)
即存在非关键字段"学院地点"、"学院电话"对关键字段"学号"的传递函数依赖。
     它也会存在数据冗余、更新异常、插入异常和删除异常的情况,读者可自行分析得知。
     把学生关系表分为如下两个表:
     学生:(学号, 姓名, 年龄, 所在学院);
     学院:(学院, 地点, 电话)。
这样的数据库表是符合第三范式的,消除了数据冗余、更新异常、插入异常和删除异常。
     鲍依斯-科得范式(BCNF):在第三范式的基础上,数据库表中如果不存在任何字段对任一候选关键字段的传递函数依赖则符合第三范式。
     假设仓库管理关系表为StorehouseManage(仓库ID, 存储物品ID, 管理员ID, 数量),且有一个管理员只在一个仓库工作;一个仓库可以存储多种物品。这个数据库表中存在如下决定关系:
     (仓库ID, 存储物品ID) →(管理员ID, 数量)
     (管理员ID, 存储物品ID) → (仓库ID, 数量)
     所以,(仓库ID, 存储物品ID)和(管理员ID, 存储物品ID)都是StorehouseManage的候选关键字,表中的唯一非关键字段为数量,它是符合第三范式的。但是,由于存在如下决定关系:
     (仓库ID) → (管理员ID)
     (管理员ID) → (仓库ID)
即存在关键字段决定关键字段的情况,所以其不符合 BCNF范式。它会出现如下异常情况:
     (1) 删除异常:
     当仓库被清空后,所有"存储物品ID"和"数量"信息被删除的同时,"仓库ID"和"管理员ID"信息也被删除了。
     (2) 插入异常:
     当仓库没有存储任何物品时,无法给仓库分配管理员。
     (3) 更新异常:
     如果仓库换了管理员,则表中所有行的管理员ID都要修改。
     把仓库管理关系表分解为二个关系表:
     仓库管理:StorehouseManage(仓库ID, 管理员ID);
     仓库:Storehouse(仓库ID, 存储物品ID, 数量)。
     这样的数据库表是符合BCNF范式的,消除了删除异常、插入异常和更新异常。


原帖地址: http://www.cublog.cn/u/23975/showart.php?id=391210

posted @ 2011-02-21 14:45 AK47 阅读(310) | 评论 (0)编辑 收藏

Hibernate 实体对象的生命周期汇总

本帖汇总了网上几篇关于hibernate的生命周期的帖子。

转载:

实体对象的生命周期在Hibernate应用中是一个很关键的概念,正确的理解实体对象的生命周期将对我们应用Hibernate做持久层设计起到很大的作用.而所谓的实体对象的生命周期就是指实体对象由产生到被GC回收的一段过程.在这过程中我们需要理解的就是实体对象生命周期中的三种状态.

1. 自由状态(Transient)
所谓的Transient状态,即实体对象在内存中自由存在,与数据库中的记录无关,通常是我们的J2EE中 VO,并没有被纳入Hibernate的实体管理容器.

1    Test test = new Test();
2        test.setName("energykk");
3        //此时的test对象处于Transient(自由状态)并没有被Hibernate框架所管理
4        

2.持久状态(Persistent)
何谓 Persistent? 即实体对象已经处于被Hibernate实体管理容器容器所管理的状态.这种状态下这个实体对象的引用将被纳入Hibernate实体管理容器容器所管理.
处于Persistent状态的实体对象,对它的变更也将被固化到数据库中.
在J2EE中通常指的是一个PO.
Transaction tr = session.beginTransaction();
        session.save(test);
        
//此时的test对象已经处于Persistent(持久状态)它被Hibernate 纳入实体管理容器
        tr.commit();
        Transaction tr2 
= session.beginTransaction();
        test.setName(
"xukai");
        
//在这个事务中我们并没有显示的调用save()方法但是由于Persistent状态的对象将会自动的固化到
        
//数据库中,因此此时正处在Persistent状态的test对象的变化也将自动被同步到数据库中
        tr2.commit();

处于Persistent状态的实体可以简单的理解为:如果一个实体对象与session发生了关联,并且处于session的有效期内,那么这个实体对象就处于Persistent状态.

3.游离状态(Detached)
处于Persistent状态的实体对象,其对应的session关闭以后,那么这个实体就处于 Detached状态.
我们可以认为session对象就是一个Persistent的宿主,一旦这个宿主失效,那么这个实体就处于 Detached状态.

session.close();
        
//与test对象关联的session被关闭,因此此时的test对象进入 Detached(游离状态)
        
        session2 
= HibernateSessionFactory.getSession();
        Transaction tr3 
= session2.beginTransaction();
        session2.update(test);
        
//此时正处于Detached状态的test对象由于再次借助与session2被纳入到Hibernate的实体管理容器所以此时的
        
//test对象恢复到Persistent状态
        test.setName("jjjj");
        tr3.commit();
        
        session2.close();

既然Transient状态的实体与Detached状态的实体都与Hibernate的实体管理容器没有关系,那他们到底存在哪些差异?
差异就在于处于Transient状态的只有一个Name的属性.此时的test对象所包含的数据信息仅限于此,他与数据库中的记录没有任何瓜葛.
但是处于Detached状态的实体已经不止包含Name这个属性,还被赋予了主键也就是通常POJO里的id属性,由于id是主键,他可以确定数据库表中的一条
唯一的记录,那么自然的处于Detached状态的实体就能与数据库表中拥有相同id的记录相关联.
这就是他们之间所存在的差异, 简而言之,Transient状态的实体缺乏与数据库表记录之间的联系,而Detached状态的试题恰恰相反.只不过是脱离了session这个数据库操作平台而已.
原帖地址 : http://www.blogjava.net/energykk/archive/2007/05/08/115927.html

 生命周期图:
原图地址:http://hi.baidu.com/quest2run/blog/item/39e1d08c7dbd45f4503d9222.html
 

persistence context



生命周期特征总结 :
原帖地址 : http://blog.csdn.net/hgd250/archive/2008/08/06/2775943.aspx
Transient:

    与数据库中的记录没有任何关系,即没有与其相关联的数据库记录.
    与session没有任何关系.即没有通过session对象的实例对其进行任何持久化的操作
Persistent:
    每个persistent状态的实体对象都与一个session对象的实例相关联
    处于 Persistent状态的实体对象是与数据库中的记录相关联的.
    Hibernate会依据persistent状态的实体对象的属性变化而改变数据库中相对应的记录
.
Detached:
    游离态是由持久态实体对象转变而来的.
    游离态实体不再与session对象相关联.
    游离态实体对象与数据库中的记录没有直接联系,对其所做的任何修改将不会影响到到数据库中的数据.
    游离态实体对象在数据库有相对应的数据记录,如果没有被其他事务删除.

posted @ 2011-02-14 14:26 AK47 阅读(323) | 评论 (0)编辑 收藏

(转贴)BigInteger 和 BigDecimal

高精度数字
Java 提供了两个类专门用于进行高精度运算BigInteger 和 BigDecimal ,尽管它们可大致划分到与封装器相同的类别里,但两者都没有对应的主类型;这两个类都有自己的一系列方法,类似于我们针对主类型执行的操作,也就是说能用 int 或float 做的事情,用BigInteger和BigDecimal 一样可以做,只是必须换用方法调用,而不是使用运算符。此外由于牵涉更多,所以运算速度会慢一点总之我们牺牲了速度,但换来了精度。

高精度浮点数BigDecimal

一些非整数值(如几美元和几美分这样的小数)需要很精确。浮点数不是精确值,所以使用它们会导致舍入误差。因此,使用浮点数来试图表示象货币量这样的精确数量不是一个好的想法。使用浮点数来进行美元和美分计算会得到灾难性的后果。浮点数最好用来表示象测量值这类数值,这类值从一开始就不怎么精确。
    从 JDK 1.3 起,Java 开发人员就有了另一种数值表示法来表示非整数:BigDecimal。BigDecimal 是标准的类,在编译器中不需要特殊支持,它可以表示任意精度的小数,并对它们进行计算。在内部,可以用任意精度任何范围的值和一个换算因子来表示 BigDecimal,换算因子表示左移小数点多少位,从而得到所期望范围内的值。因此,用 BigDecimal 表示的数的形式为 unscaledValue*10-scale。
用于加、减、乘和除的方法给  BigDecimal 值提供了算术运算。由于 BigDecimal 对象是不可变的,这些方法中的每一个都会产生新的 BigDecimal 对象。因此,因为创建对象的开销,BigDecimal 不适合于大量的数学计算,但设计它的目的是用来精确地表示小数。如果您正在寻找一种能精确表示如货币量这样的数值,则 BigDecimal 可以很好地胜任该任务。
如浮点类型一样,BigDecimal 也有一些令人奇怪的行为。尤其在使用 equals() 方法来检测数值之间是否相等时要小心。equals() 方法认为,两个表示同一个数但换算值不同(例如,100.00 和  100.000)的 BigDecimal 值是不相等的。然而,compareTo() 方法会认为这两个数是相等的,所以在从数值上比较两个  BigDecimal 值时,应该使用 compareTo() 而不是 equals()。
另外还有一些情形,任意精度的小数运算仍不能表示精确结果。例如,1 除以 9 会产生无限循环的小数 .111111...。出于这个原因,在进行除法运算时,BigDecimal 可以让您显式地控制舍入。movePointLeft() 方法支持 10 的幂次方的精确除法。
对于 BigDecimal,有几个可用的构造函数。其中一个构造函数以双精度浮点数作为输入,另一个以整数和换算因子作为输入,还有一个以小数的 String 表示作为输入。要小心使用  BigDecimal(double) 构造函数, 因为如果不了解它,会在计算过程中产生舍入误差。请使用基于整数或 String 的构造函数。
如果使用 BigDecimal(double) 构造函数不恰当,在传递给 JDBC setBigDecimal() 方法时,会造成似乎很奇怪的 JDBC 驱动程序中的异常。例如,考虑以下 JDBC 代码,该代码希望将数字 0.01 存储到小数字段:
  PreparedStatement ps =connection.prepareStatement("INSERT INTO Foo SET name=?, value=?");
  ps.setString(1, "penny");
  ps.setBigDecimal(2, new BigDecimal(0.01));
  ps.executeUpdate();
     在执行这段似乎无害的代码时会抛出一些令人迷惑不解的异常(这取决于具体的 JDBC 驱动程序),因为 0.01 的双精度近似值会导致大的换算值,这可能会使 JDBC 驱动程序或数据库感到迷惑。JDBC 驱动程序会产生异常,但可能不会说明代码实际上错在哪里,除非意识到二进制浮点数的局限性。相反,使用 BigDecimal("0.01") 或 BigDecimal(1, 2) 构造 BigDecimal 来避免这类问题, 因为这两种方法都可以精确地表示小数。
 

code :

import java.math.BigDecimal;
/** * *
* <p>Title: 开源,开放</p>
* * <p>Description: opeansource</p>
* * <p>Copyright: Copyright (c) 2004</p>
* * <p>Company: ?海棠</p>
* * @author HaiTang Ming
* * @version 1.0 */
public class BigDecimalUtil { 
//默认除法运算精度,及即保留小数点多少位 
private static final int DEF_DIV_SCALE = 2; 
//这个类不能实例化 
private BigDecimalUtil (){   } 
/**   
  * * 提供精确的加法运算。   
  * * @param v1 被加数   
  * * @param v2 加数   
  * * @return 两个参数的和   
  * */ 
public static double add(double v1,double v2){   
  BigDecimal b1 = new BigDecimal(Double.toString(v1));   
  BigDecimal b2 = new BigDecimal(Double.toString(v2));   
  return (b1.add(b2)).doubleValue(); 

/**

  *提供精确的减法运算。 
  * * @param v1 被减数 
  * * @param v2 减数 
  * * @return 两个参数的差
  **/ 
public static double sub(double v1,double v2){   
  BigDecimal b1 = new BigDecimal(Double.toString(v1));   
  BigDecimal b2 = new BigDecimal(Double.toString(v2));   
  return (b1.subtract(b2)).doubleValue(); 

/**   
  * * 提供精确的乘法运算。   
  * * @param v1 被乘数   
  * * @param v2 乘数   
  * * @return 两个参数的积   
  * */
public static double mul(double v1,double v2){   
  BigDecimal b1 = new BigDecimal(Double.toString(v1));   
  BigDecimal b2 = new BigDecimal(Double.toString(v2));   
  return (b1.multiply(b2)).doubleValue(); 

/**   
  * * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到   
  * * 小数点以后多少位,以后的数字四舍五入。   
  * * @param v1 被除数   
  * * @param v2 除数   
  * * @return 两个参数的商   
  * */ 
public static double div(double v1,double v2){   
  return div(v1,v2,DEF_DIV_SCALE); 

/**   
  * * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指   
  * * 定精度,以后的数字四舍五入。   
  * * @param v1 被除数 
  * @param v2 除数   
  * * @param scale 表示表示需要精确到小数点以后几位。   
  * * @return 两个参数的商   
  * */ 
public static double div(double v1,double v2,int scale){   
  if(scale<0){     
   throw new IllegalArgumentException("The scale must be a positive integer or zero");   
  }   
  BigDecimal b1 = new BigDecimal(Double.toString(v1));   
  BigDecimal b2 = new BigDecimal(Double.toString(v2));   
  return (b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP)).doubleValue(); 

/**   
  * * 提供精确的小数位四舍五入处理。   
  * * @param v 需要四舍五入的数字   
  * * @param scale 小数点后保留几位   
  * * @return 四舍五入后的结果   
  * */ 
public static double round(double v,int scale){ 
  if(scale<0){     
   throw new IllegalArgumentException("The scale must be a positive integer or zero");
  }   
  BigDecimal b = new BigDecimal(Double.toString(v));   
  BigDecimal one = new BigDecimal("1");   
  return (b.divide(one,scale,BigDecimal.ROUND_HALF_UP)).doubleValue(); 
}   
public static void main(String[] args){   
  System.out.println(add(234.44,534.90));
 
  double a = 123.345678;   
  double d = round(a,2);   
  System.out.println("round("+a+",2)--->"+d); 
}
}

高精度整数BigInteger

BigInteger支持任意精度的整数,也就是说我们可精确表示任意大小的整数值;同时在运算过程中不会丢失任何信息;
在BigInteger类中有所有的基本算术运算方法,如加、减、乘、除,以及可能会用到的位运算如或、异或、非、左移、右移等。下面是一些方法的例子:当然,如果要有更多的使用方法,可以查阅java api 。

code :public class BigIntegerTest { 
public BigIntegerTest() {   } 
/**   
  * * 测试BigInteger
  * */ 
public static void testBigInteger() {   
  BigInteger bi = new BigInteger("888");   
  //multiply :乘法   
  BigInteger result = bi.multiply(new BigInteger("2"));   
  System.out.println(result);   
  //divide : 除法   
  result = bi.divide(new BigInteger("2"));   
  System.out.println(result);   
  //add : 加法   
  result = bi.add(new BigInteger("232"));   
  System.out.println(result);   
  //subtract :减法   
  result = bi.subtract(new BigInteger("23122"));   
  System.out.println(result);   
  result = bi.shiftRight(2);   
  System.out.println(result); 
}   
public static void main(String[] args) {   
  testBigInteger(); 
}
}
原贴地址http://dev.firnow.com/course/3_program/java/javaxl/2008914 /142796_2.html

posted @ 2010-12-10 14:16 AK47 阅读(1014) | 评论 (0)编辑 收藏

(转贴) 超大整数相加,超过了long的范围,你要怎么做!

 

引用:

 这个只能够用字符串的形式来处理了,因为计算机能够处理的最大是long型,本文以字符串的形式来进行超大数据的相加,理论上只要你的内存允许,相加多大的数都可以。

/**

 * 超大整数相加:

 * 题目要求:如果系统要使用超大整数(超过long的范围),请你设计一个数据结构来存储这种

 * 超大型数字以及设计一种算法来实现超大整数的加法运算

 * @author Administrator

 *

 */

public class VeryBigNumAdd {

 

    /**

     * @param args

     */

    public static void main(String[] args) {

       // TODO Auto-generated method stub

       /*

       String a="1223232";

       for(int i=a.length()-1;i>=0;i--)

       {

           System.out.print(a.charAt(i));

       }

       */

       VeryBigNumAdd vbn=new VeryBigNumAdd();

       String a="123453243455535634535252345234677576252241234123523453664563634";

       String b="123453243455535634535252345234677576252241234123523453664563634";

       String result=vbn.doAdd(a,b);

       System.out.println("result:"+result);

    }

    /**

     *

     * @param a 加数字符串1

     * @param b 加数字符串2

     * @return 结果字符串

     * 分析:

     * 1、取得两个字符串的长度

     * 2、把两个的长度做比较,并得出较长的长度,及较短的长度

     * 3、把长度较短的加数字符串,在左面补0,使之与较长的字符串一样长

     * 4、从最高位,一个个数的取出来相加,当然首先得转换为整型

     * 5、设置进位,如果两个数相加及加上进位大于等于10,并且这不是最左边一个字符相加,相加结果等于

     *    (取出1+取出2+进位)-10,并把进位设为1;如果没有大于10,就把进位设为0,如些循环,把

     *    相加的结果以字符串的形式结合起来,就得到最后的结果

     */

    String doAdd(String a,String b)

    {

       String str="";

       int lenA=a.length();

       int lenB=b.length();

       int maxLen=(lenA>lenB) ? lenA : lenB;

       int minLen=(lenA<lenB) ? lenA : lenB;

       String strTmp="";

       for(int i=maxLen-minLen;i>0;i--)

       {

           strTmp+="0";

       }

       //把长度调整到相同

       if(maxLen==lenA)

       {

           b=strTmp+b;

       }else

           a=strTmp+a;

       int JW=0;//进位

       for(int i=maxLen-1;i>=0;i--)

       {        

           int tempA=Integer.parseInt(String.valueOf(a.charAt(i)));

           int tempB=Integer.parseInt(String.valueOf(b.charAt(i)));

           int temp;

           if(tempA+tempB+JW>=10 && i!=0)

           {

              temp=tempA+tempB+JW-10;

              JW=1;

           }

           else

           {

              temp=tempA+tempB+JW;

              JW=0;

           }        

           str=String.valueOf(temp)+str;        

       }

       return str;

    }

 

}

 

原帖地址: http://blog.csdn.net/fenglibing/archive/2007/08/23/1756773.aspx

    
    其实java 本身也提供了api ,java.math.BigInteger;import java.math.BigDecimal; 也可以实现。

code :

package com.kangdy.test;

import java.math.BigInteger;
import java.math.BigDecimal;

public class NumberTest {
 public static void main(String args[]){
  BigInteger b1= new BigInteger("2222222222222222222222222");
  BigInteger b2= new BigInteger("8888888888888888888888888");
  BigDecimal b3 = new BigDecimal("66666666666666666666666666");
  BigDecimal b4 = new BigDecimal("9999999999999999999999999999");
  System.out.println(b1.add(b2).toString());
  System.out.println(b3.add(b4).toString());
 }
}

这里只是给出简单的例子。



posted @ 2010-12-10 14:06 AK47 阅读(911) | 评论 (0)编辑 收藏

(转贴)java回调函数

原帖地址: http://ayzw001.blog.163.com/blog/static/1134114222009420112538726/

引用:

       所谓回调,就是客户程序C调用服务程序S中的某个方法a,然后S又在某个时候反过来调用C中的某个方法b,对于C来说,这个b便叫做回调函数。

一般说来,C不会自己调用b,C提供b的目的就是让S来调用它,而且是C不得不提供。由于S并不知道C提供的b叫甚名谁,所以S会约定b的接口规范(函数原型),然后由C提前通过S的一个函数r告诉S自己将要使用b函数,这个过程称为回调函数的注册,r称为注册函数。

下面举个通俗的例子:

某天,我打电话向你请教问题,当然是个难题,:),你一时想不出解决方法,我又不能拿着电话在那里傻等,于是我们约定:等你想出办法后打手机通知我,这样,我就挂掉电话办其它事情去了。过了XX分钟,我的手机响了,你兴高采烈的说问题已经搞定,应该如此这般处理。故事到此结束。

这个例子说明了“异步+回调”的编程模式。其中,你后来打手机告诉我结果便是一个“回调”过程;我的手机号码必须在以前告诉你,这便是注册回调函数;我的手机号码应该有效并且手机能够接收到你的呼叫,这是回调函数必须符合接口规范。

 

如果你还不太清楚看看这段描述合和代码:

声明一个接口,另外一个类有方法里面有个参数以是这个接口类型的,而后在另外类中实现这个接口(java中多用的是匿名内部类),而且以这个匿名的类生成的对象为参数传到上面提到类中,而后实现回调.......这种用法可以参考java里面常用到的数据库操作所用到的几个接口.....

//声明一个接口
public interface ICallBack {
    void postExec();
}

 

//另外一个类有方法里面有个参数以是这个接口类型的
public class FooBar {
    private ICallBack callBack;
    public void setCallBack(ICallBack callBack) {
        this.callBack = callBack;
    }
    public void doSth() {
        callBack.postExec();
    }
}
---------------------------------------
回调的实现
public class Test {
    public static void main(String[] args) {
        FooBar foo = new FooBar();
        foo.setCallBack(new ICallBack() {
            public void postExec() {
                System.out.println("method executed.");
            }
        });
        foo.doSth();//调用函数
    }
}

posted @ 2010-12-10 11:22 AK47 阅读(610) | 评论 (0)编辑 收藏

(转贴) 真正理解面向接口编程

面向对象设计里有一点大家已基本形成共识,就是面向接口编程,我想大多数人对这个是没有什么觉得需要怀疑的。

问题是在实际的项目开发中我们是怎么体现的呢? 难道就是每一个实现都提供一个接口就了事了?反过来说,你有时候有没有觉得接口是多余的事? 又或者,你仅仅是觉得现在类似spring这样的框架已习惯用接口这种方式而心存当然。

设计模式解析里提到了面向对象设计考虑的几个视角,一个是概念层,一个是规约层,一个是实现层。我如果没有猜错的话,实际上我们大多数人的眼睛一直是盯着实现层的,而这正是面向对象设计所极力避免的,即你不要在一开始就关注这些细节,你要关注的是规约(接口).

对于实际项目开发来说,如果我们把实现的过程分为多个阶段的话我们不妨这么划分,第一阶段,根据client端的需要去设计我们的规约(interface),在这个阶段任何实现都没有,所有的任务就是定义接口所需要的职责,以及所需要的一些po,vo;第二阶段,实现前面定义的规约。而以前我是怎么做的呢? 我是交叉作的,即假模假样的定义一个接口(其实我心里在想这个东西有屁用),然后定义了一个方法,然后就立即去实现这个方法,再然后我又定义一个方法,继续去实现,我现在终于想通了,这样好累,效率很低,最重要的是,这不属于真正的设计。
现在我是怎么做的呢?比如一个list.jsp里需要查询,列表,然后看明细信息,然后增加信息,我会第一步在接口里定义完(这个过程会有整体设计的意识),毫不关心底层实现(数据库、事务),我的目标就是"我想要这个功能,我想要那个功能",至于那个功能怎么实现在第一阶段我认为那不是我的事情(尽管这个事情最终还是由我来做) .大家看这个过程和前面的过程有什么本质的不同呢? 就是分层的概念更加明显,你的工作更有层次,每次都有先设计再实现的步骤,而前面那个过程很容易就让你不知不觉地陷入纯实现的陷阱中。

一点感想,欢迎大家拍砖。

原帖地址: http://www.blogjava.net/alex/archive/2007/03/12/103185.html

posted @ 2010-11-05 14:09 AK47 阅读(301) | 评论 (0)编辑 收藏

数字金额的中文大小写转化

曾经去过一家公司面试。笔试题的最后一题是一个数字金额大小写转化的问题。
当时没想那么多,仅仅想到应该把数拆开然后添加单位的一个大致的设计思路。
而那个面试官一个劲儿的问我用啥算法。那个题最后也没答上,回来后比较郁闷,
在网上搜了一下。这个答案还真不少。不过觉得有一种设计还比较靠谱。
大概是这样:
 * 先将整数与小数部分分开,计算小数部分,角分并保存
 * 整数部分长度不足12位,前面加0补足。
 * 将整数部分分割3部分。高4位代表亿,中间的是万,其余分别代表千,百,十,个
 * 定一个方法拼出每一部分串。
 * 最后整数与小数部分合成。

自己实现了一下,以下是代码。
code :

package com.kangdy.test;
/**
 * 数字金额转化成大写
 * 先将整数与小数部分分开,计算小数部分,角分并保存
 * 整数部分长度不足12位,前面加0补足。
 * 将整数部分分割3部分。高4位代表亿,中间的是万,其余分别代表千,百,十,个
 * 定一个方法拼出每一部分串。
 * 最后整数与小数部分合成。
 * @author dkang
 *
 */
public class NumberToString {

 String numberStr;

 public static final String unit[] = { "", "十", "百", "千", "万", "亿" };

 public static final String unit2[] = { "元", "角", "分" };

 public static final String numStr[] = { "零", "壹", "贰", "叁", "肆", "伍", "陆",
   "柒", "捌", "玖" };

 /**
  * 字符串长度不足12位用0补足
  *
  * @param str
  * @return
  */
 private String additionalZero(String str) {
  StringBuffer strb = new StringBuffer();
  if (str.length() < 12) {
   int size = 12 - str.length();
   for (int i = 0; i < size; i++) {
    strb.append("0");
   }
  }
  return strb.append(str).toString();
 }

 /**
  * 递归拆分数字成字符串
  *
  * @param value
  * @param strBuffer
  * @return
  */
 private String decomposeNumberToString(int value, StringBuffer strBuffer) {
  int quotient = 0;
  quotient = value / 10;
  if (quotient != 0) {
   decomposeNumberToString(quotient, strBuffer);
  }
  int remaider = value % 10;
  strBuffer.append(remaider + ",");
  return strBuffer.toString().substring(0,
    strBuffer.toString().length() - 1);
 }

 /**
  * 使用循环拆分数字成字符串
  *
  * @param value
  * @return
  */
 private String decomposeNumberToString2(int value) {
  StringBuilder strBuilder = new StringBuilder();
  int quotient = value;
  int remaider = 0;
  while (quotient != 0) {
   remaider = quotient % 10;
   strBuilder.append(remaider + ",");
   quotient = quotient / 10;
  }
  strBuilder.deleteCharAt(strBuilder.lastIndexOf(","));
  return strBuilder.reverse().toString();
 }

 /**
  * 添加单位
  *
  * @param temp
  * @return
  */
 private String addUnits(String temp) {
  StringBuffer sb = new StringBuffer();
  String str[] = temp.split(",");
  String tempStr = temp.replace(",", "");
  if (tempStr.contains("000")) {
   return sb.append(resplaceNumToStr(str[0]) + unit[3]).toString();
  } else if (tempStr.contains("00")) {
   if (tempStr.charAt(3) == '0') {
    return sb.append(resplaceNumToStr(str[0]) + unit[3]).append(
      resplaceNumToStr(str[1]) + unit[2]).toString();
   } else {
    return sb.append(resplaceNumToStr(str[0]) + unit[3]).append(
      numStr[0]).append(resplaceNumToStr(str[3])).toString();
   }
  } else {
   for (int i = 0; i < str.length; i++) {
    sb.append(resplaceNumToStr(str[i]));
    if (!str[i].equals("0")) {
     sb.append(unit[str.length - (i + 1)]);
    }
   }
  }
  return sb.toString();
 }

 /**
  * 数字替换
  *
  * @param str
  * @return
  */
 private String resplaceNumToStr(String str) {
  try {
   int num = Integer.parseInt(str);
   return numStr[num];
  } catch (Exception e) {
   e.printStackTrace();
  }
  return "";
 }

 /**
  * 把4位长度的数字转化成字符串
  *
  * @param number
  * @param i
  * @return
  */
 private String transformNumberToString(String number, int i) {
  StringBuffer strBuffer = new StringBuffer();
  StringBuilder strBuilder = new StringBuilder();
  try {
   int num = Integer.parseInt(number);
   if (num != 0) {
    String s1 = decomposeNumberToString(num, strBuffer);
    strBuilder.append(addUnits(s1));
    if (i == 1) {
     strBuilder.append(unit[5]);
    } else if (i == 2)
     strBuilder.append(unit[4]);
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
  return strBuilder.toString();
 }

 /**
  * 得到最终结果
  *
  * @param str
  * @return
  */
 public String IntegrationResultString(String str) {
  StringBuffer strBuffer = new StringBuffer();
  String numStr[] = null;
  if (str.indexOf(".") != -1) {
   numStr = str.split("\\.");
  } else {
   return strBuffer.append(createIntegerPartsResult(str)).toString();
  }
  String fractionalStr = createFractionalPartsResult(numStr[1]);
  String integerStr = createIntegerPartsResult(numStr[0]);
  return strBuffer.append(integerStr).append(fractionalStr).toString();
 }

 private String createIntegerPartsResult(String integer) {
  StringBuffer strBuffer = new StringBuffer();
  String temp = additionalZero(integer);
  String str1 = temp.substring(0, 4);
  strBuffer.append(transformNumberToString(str1, 1));
  String str2 = temp.substring(4, 8);
  strBuffer.append(transformNumberToString(str2, 2));
  String str3 = temp.substring(8, temp.length());
  strBuffer.append(transformNumberToString(str3, 3) + unit2[0]);
  return strBuffer.toString();
 }

 private String createFractionalPartsResult(String fractionalStr) {
  StringBuilder strB = new StringBuilder();
  String s1 = fractionalStr.substring(0, 1);
  String s2 = fractionalStr.substring(1, fractionalStr.length());
  if (!s1.equals("0")) {
   strB.append(resplaceNumToStr(s1) + unit2[1]);
  }
  if (!s2.equals("0")) {
   strB.append(resplaceNumToStr(s2) + unit2[2]);
  }
  return strB.toString();
 }

 public static void main(String args[]) {
  NumberToString test = new NumberToString();
  String str = "200123004054.11";
  System.out.println(test.IntegrationResultString(str));
 }
}



posted @ 2010-11-02 14:59 AK47 阅读(584) | 评论 (0)编辑 收藏

仅列出标题
共4页: 上一页 1 2 3 4 下一页 
<2024年5月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

导航

统计

常用链接

留言簿

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜