2005年12月28日

    这个网站可以测试你电脑的大概功率,当然,这个功率是所有部件都处于高负荷运行时的功率,可以作为参考。另外,笔记本的部件不在测试之列,如果台式机过于先进(例如显卡是Nvidia Geforce 7800 GTX)也不能找到相关的部件选项。
    测试链接:
http://www.jscustompcs.com/power_supply/

    进入网站后,需要选择电脑的CPU类型是AMD还是Intel,或者你要测试的是服务器,进入页面后再选择电脑的各个部件即可。有空可以看看,纯属无聊消遣!

TestYourComputer.gif

2005年12月19日

ClassCastExceptionJVM在检测到两个类型间转换不兼容时引发的运行时异常。此类错误通常会终止用户请求。在执行任何子系统的应用程序代码时都有可能发生ClassCastException异常。通过转换,可以指示Java编译器将给定类型的变量作为另一种变量来处理。对基础类型和用户定义类型都可以转换。Java语言规范定义了允许的转换,其中大多数可在编译时进行验证。不过,某些转换还需要运行时验证。如果在此运行时验证过程中检测到不兼容,JVM就会引发ClassCastException异常。例如:

Fruit f;

Apple a = (Apple)f;

当出现下列情况时,就会引发ClassCastException异常:

1.        FruitApple类不兼容。当应用程序代码尝试将某一对象转换为某一子类时,如果该对象并非该子类的实例,JVM就会抛出ClassCastException异常。

2.        FruitApple类兼容,但加载时使用了不同的ClassLoader。这是这种异常发生最常见的原因。在这里,需要了解一下什么是ClassLoader


ClassLoader

         ClassLoader是允许JVM查找和加载类的一种Java类。JVM有内置的ClassLoader。不过,应用程序可以定义自定义的ClassLoader。应用程序定义新的ClassLoader通常出于以下两种原因:

1.        自定义和扩展JVM加载类的方式。例如,增加对新的类库(网络、加密文件等)的支持。

2.        划分JVM名称空间,避免名称冲突。例如,可以利用划分技术同时运行同一应用程序的多个版本(基于空间的划分)。此项技术在应用服务器(WebLogic Server)内的另一个重要用途是启用应用程序热重新部署,即在不重新启动JVM的情况下启动应用程序的新版本(基于时间的划分)

ClassLoader按层级方式进行组织。除系统BootClassLoader外,其它ClassLoader都必须有父ClassLoader

在理解类加载的时候,需要注意以下几点:

1.        永远无法在同一ClassLoader中重新加载类。“热重新部署”需要使用新的ClassLoader。每个类对其ClassLoader的引用都是不可变的:this.getClass().getClassLoader()

2.        在加载类之前,ClassLoader始终会先询问其父ClassLoader(委托模型)。这意味着将永远无法重写“核心”类。

3.        同级ClassLoader间互不了解。

4.        由不同ClassLoader加载的同一类文件也会被视为不同的类,即便每个字节都完全相同。这是ClassCastException的一个典型原因。

5.        可以使用Thread.setContextClassLoader(a)ClassLoader连接到线程的上下文。

基于以上的基本原理,可以加深大家对ClassCastException的理解,和在碰到问题时提供一种解决问题的思路。

 

 

参考文献:

dev2dev专刊 2005 第二期

j2sdk-1_5_0-doc

2005年12月17日

    数组是Java语言内置的类型,除此之外,Java有多种保存对象引用的方式。Java类库提供了一套相当完整的容器类,使用这些类的方法可以保存和操纵对象。在此,先分析数组的基本功能和特性。

1.数组的基本特性

         数组与其它种类的容器(List/Set/Map)之间的区别在于效率、确定的类型和保存基本类型数据的能力。数组是一种高效的存储和随机访问对象引用序列的方式,使用数组可以快速的访问数组中的元素。但是当创建一个数组对象(注意和对象数组的区别)后,数组的大小也就固定了,当数组空间不足的时候就再创建一个新的数组,把旧的数组中所有的引用复制到新的数组中。

         Java中的数组和容器都需要进行边界检查,如果越界就会得到一个RuntimeException异常。这点和C++中有所不同,C++vector的操作符[]不会做边界检查,这在速度上会有一定的提高,Java的数组和容器会因为时刻存在的边界检查带来一些性能上的开销。

         Java中通用的容器类不会以具体的类型来处理对象,容器中的对象都是以Object类型处理的,这是Java中所有类的基类。另外,数组可以保存基本类型,而容器不能,它只能保存任意的Java对象。

         一般情况下,考虑到效率与类型检查,应该尽可能考虑使用数组。如果要解决一般化的问题,数组可能会受到一些限制,这时可以使用Java提供的容器类。

2.操作数组的实用功能

         java.util.Arrays类中,有许多static静态方法,提供了操作数组的一些基本功能:

         equals()方法—-用于比较两个数组是否相等,相等的条件是两个数组的元素个数必须相等,并且对应位置的元素也相等。

         fill()方法—-用以某个值填充整个数组,这个方法有点笨。

         asList()方法—-接受任意的数组为参数,将其转变为List容器。

         binarySearch()方法—-用于在已经排序的数组中查找元素,需要注意的是必须是已经排序过的数组。当Arrays.binarySearch()找到了查找目标时,该方法将返回一个等于或大于0的值,否则将返回一个负值,表示在该数组目前的排序状态下此目标元素所应该插入的位置。负值的计算公式是“-x-1”x指的是第一个大于查找对象的元素在数组中的位置,如果数组中所有的元素都小于要查找的对象,则x = a.size()。如果数组中包含重复的元素,则无法保证找到的是哪一个元素,如果需要对没有重复元素的数组排序,可以使用TreeSet或者LinkedHashSet。另外,如果使用Comparator排序了某个对象数组,在使用该方法时必须提供同样的Comparator类型的参数。需要注意的是,基本类型数组无法使用Comparator进行排序。

         sort()方法—-对数组进行升序排序。

         Java标准类库中,另有static方法System.arraycopy()用来复制数组,它针对所有类型做了重载。

3.数组的排序

         Java1.01.1两个版本中,类库缺少基本的算法操作,包括排序的操作,Java2对此进行了改善。在进行排序的操作时,需要根据对象的实际类型执行比较操作,如果为每种不同的类型各自编写一个不同的排序方法,将会使得代码很难被复用。一般的程序设计目标应是“将保持不变的事物与会发改变的事物相分离”。在这里,不变的是通用的排序算法,变化的是各种对象相互比较的方式。

Java有两种方式来实现比较的功能,一种是实现java.lang.Comparable接口,该接口只有一个compareTo()方法,并以一个Object类为参数,如果当前对象小于参数则返回负值,如果相等返回零,如果当前对象大于参数则返回正值。另一种比较方法是采用策略(strategy)设计模式,将会发生变化的代码封装在它自己的类(策略对象)中,再将策略对象交给保持不变的代码中,后者使用此策略实现它的算法。因此,可以为不同的比较方式生成不同的对象,将它们用在同样的排序程序中。在此情况下,通过定义一个实现了Comparator接口的类而创建了一个策略,这个策略类有compare()equals()两个方法,一般情况下实现compare()方法即可。

使用上述两种方法即可对任意基本类型的数组进行排序,也可以对任意的对象数组进行排序。再提示一遍,基本类型数组无法使用Comparator进行排序。

    Java标准类库中的排序算法针对排序的类型进行了优化——针对基本类型设计了“快速排序”,针对对象设计的“稳定归并排序”。一般不用担心其性能。

2005年12月13日

运行时类型识别(Run-time Type Identification, RTTI)主要有两种方式,一种是我们在编译时和运行时已经知道了所有的类型,另外一种是功能强大的“反射”机制。

       要理解RTTIJava中的工作原理,首先必须知道类型信息在运行时是如何表示的,这项工作是由“Class对象”完成的,它包含了与类有关的信息。类是程序的重要组成部分,每个类都有一个Class对象,每当编写并编译了一个新类就会产生一个Class对象,它被保存在一个同名的.class文件中。在运行时,当我们想生成这个类的对象时,运行这个程序的Java虚拟机(JVM)会确认这个类的Class对象是否已经加载,如果尚未加载,JVM就会根据类名查找.class文件,并将其载入,一旦这个类的Class对象被载入内存,它就被用来创建这个类的所有对象。一般的RTTI形式包括三种:

1.       传统的类型转换。如“(Apple)Fruit”,由RTTI确保类型转换的正确性,如果执行了一个错误的类型转换,就会抛出一个ClassCastException异常。

2.       通过Class对象来获取对象的类型。如

Class c = Class.forName(“Apple”);

Object o = c.newInstance();

3.       通过关键字instanceofClass.isInstance()方法来确定对象是否属于某个特定类型的实例,准确的说,应该是instanceof / Class.isInstance()可以用来确定对象是否属于某个特定类及其所有基类的实例,这和equals() / ==不一样,它们用来比较两个对象是否属于同一个类的实例,没有考虑继承关系。

 

 

反射

       如果不知道某个对象的类型,可以通过RTTI来获取,但前提是这个类型在编译时必须已知,这样才能使用RTTI来识别。即在编译时,编译器必须知道所有通过RTTI来处理的类。

使用反射机制可以不受这个限制,它主要应用于两种情况,第一个是“基于构件的编程”,在这种编程方式中,将使用某种基于快速应用开发(RAD)的应用构建工具来构建项目。这是现在最常见的可视化编程方法,通过代表不同组件的图标拖动到图板上来创建程序,然后设置构件的属性值来配置它们。这种配置要求构件都是可实例化的,并且要暴露其部分信息,使得程序员可以读取和设置构件的值。当处理GUI时间的构件时还必须暴露相关方法的细细,以便RAD环境帮助程序员覆盖这些处理事件的方法。在这里,就要用到反射的机制来检查可用的方法并返回方法名。Java通过JavaBeans提供了基于构件的编程架构。

       第二种情况,在运行时获取类的信息的另外一个动机,就是希望能够提供在跨网络的远程平台上创建和运行对象的能力。这被成为远程调用(RMI),它允许一个Java程序将对象分步在多台机器上,这种分步能力将帮助开发人员执行一些需要进行大量计算的任务,充分利用计算机资源,提高运行速度。

       Class支持反射,java.lang.reflect中包含了Field/Method/Constructor类,每个类都实现了Member接口。这些类型的对象都是由JVM在运行时创建的,用来表示未知类里对应的成员。如可以用Constructor类创建新的对象,用get()set()方法读取和修改与Field对象关联的字段,用invoke()方法调用与Method对象关联的方法。同时,还可以调用getFields()getMethods()getConstructors()等方法来返回表示字段、方法以及构造器的对象数组。这样,未知的对象的类信息在运行时就能被完全确定下来,而在编译时不需要知道任何信息。

       另外,RTTI有时能解决效率问题。当程序中使用多态给程序的运行带来负担的时候,可以使用RTTI编写一段代码来提高效率。

2005年12月09日

    Google现在的火热程度都快超过当年Windows 95,在推出Google Talk / Google Map / Google Earth / Gmail / Picasa2 等等重量级服务及软件以后,近日又推出了一项名为Transit Trip Planner(出行路线规划)的新服务,目的是为出行用户找出最便捷的公交路线。

    公交线路服务是地理信息的重要应用之一或者是将来全面应用的热点之一,在当今地理信息发展到十字路口,在技术理论理念的突破步履维艰的时候,Google公司的Google Map / Google Earth 和即将在全球普及的Transit Trip Planner服务给地理信息世界注入了一剂强心针,在理想地理信息应用和现阶段的实际应用之间找到了一个平衡点,和Google公司一贯作风一样,以简洁实用高效的特点吸引了无数人的眼光,提供了最简便的应用。

2005120901.jpg

2005120902.jpg



    Transit Trip Planner服务需要用户输入他们的首选路线,例如出发地、目的地、出行日期和时间等,然后它会收集所有的公交调度信息,为用户制订出行路线计划。这项服务目前还处于测试阶段,并且还没有和Google本地搜索整合,相信不久就可以和Google Map / Google Earth 一样在全球得到应用,进一步改善用户的地图服务体验,以现有的基础提供最实用的地图影像服务。Google和Sun公司已经建立了良好的合作关系,Google的发展势头将对Java应用的进一步推广产生积极的作用,让我们一起期待Google和Java的发展……

2005年11月21日

Action类是用户请求和业务逻辑之间的桥梁。每个Action充当用户的一项业务代理。在RequestProcessor类预处理请求时,在创建了Action的实例后,就调用自身的processActionPerform()方法,该方法再调用Action类的execute()方法。Action的execute()方法调用模型的业务方法,完成用户请求的业务逻辑,然后根据执行结果把请求转发给其他合适的Web组件。在实际的应用中,主要有以下几种比较常见的使用方法:

1.普通的Action应用

<action path="/normalAction"
type="package.OneActionClass">
name="oneForm"
input="page.jsp"
<forward name="success" path="success.jsp"/>
<forward name="failure" path="failure.jsp"/>
</action>

    Struts的ActionServlet接收到一个请求,然后根据struts-config.xml的配置定位到相应的mapping(映射);接下来如果form的范围是request或者在定义的范围中找不到这个form,创建一个新的form实例,如果找到则重用;取得form实例以后,调用其reset()方法,然后将表单中的参数放入form,如果validate属性不为false,调用validate()方法;如果validate()返回非空的ActionErrors,将会被转到input属性指定的URI,如果返回空的ActionErrors,那么执行Action的execute()方法,根据返回的ActionForward确定目标URI。即execute()仅当validate()成功以后才执行;input属性指定的是一个URI。


2.有Form的Action应用

<action path="/formAction"
type="org.apache.struts.actions.ForwardAction"
name="oneForm"
input="page.jsp"
parameter="another.jsp"
/>

    Struts会在定义的scope搜寻oneForm,如果找到则重用,如果找不到则新建一个实例;取得form实例以后,调用其reset()方法,然后将表单中的参数放入form,如果validate属性不为false,调用validate()方法;如果validate()返回非空的ActionErrors,将会被转到input属性指定的URI,如果返回空的ActionErrors,那么转到parameter属性指定的目标URI。

    这种方法使得没有action类可以存放我们的业务逻辑,所以所有需要写入的逻辑都只能写到form的reset()或者validate()方法中。validate()的作用是验证和访问业务层。因为这里的action映射不包括forward,所以不能重定向,只能用默认的那个forward。这种仅有form的action可以用来处理数据获取并forward到另一个JSP来显示。


3.仅有Action的Action应用

<action path="/actionAction"
type="package.OneActionClass">
input="page.jsp"
<forward name="success" path="success.jsp"/>
<forward name="failure" path="failure.jsp"/>
</action>

    ActionServlet接收到请求后,取得action类实例,调用execute()方法;然后根据返回的ActionForward在配置中找forward,forward到指定的URI或action。这样就没有form实例被传入execute()方法,于是execute()必须自己从请求中获取参数。Action可以被forward或者重定向。这种action不能处理通过HTML FORM提交的请求,只能处理链接式的请求。


4.仅有JSP的Action应用

<action path="/jspAction"
type="org.apache.struts.actions.ForwardAction"
parameter="another.jsp"
/>

    ActionServlet接到请求后调用ForwardAction的execute()方法,execute()根据配置的parameter属性值来forward到那个URI。这种情况下,没有任何form被实例化,比较现实的情形可能是form在request更高级别的范围中定义;或者这个action被用作在应用程序编译好后充当系统参数,只需要更改这个配置文件而不需要重新编译系统。


5.两个Action对应一个Form(和第四种方式部分的Action作用相近)

<action path="/oneAction"
type="package.OneActionClass">
name="oneForm"
input="one.jsp"
<forward name="success" path="/anotherAction.do"/>
</action>
<action path="/anotherAction"
type="package.AnotherActionClass">
name="oneForm"
input="another.jsp"
<forward name="success" path="success.jsp"/>
</action>

    这个组合模式可以被用来传递form对象,就每个单独的action来讲,处理上并没有和完整的action有什么实质的区别。需要注意的是在后一个action中同样会调用form的reset()和validate()方法,因此我们必须确保form中的信息不被重写。这种情况分两种方式处理:a) 在request中放入一个指示器表明前一个action有意向后一个action传递form,从而在后一个action可以保留那个form中的值,这一方式只能在使用forward时使用。b) 当使用redirect而不是forward时,可以把指示器放在session或更高的级别,在命令链的最后一环将这个指示器清除。


6.两个Action对应两个form

<action path="/oneAction"
type="package.oneActionClass">
name="oneForm"
input="one.jsp"
<forward name="successful" path="/anotherAction.do" redirect="true"/>
</action>
<action path="/anotherAction"
type="package.AnotherActionClass">"
name="anotherForm"
input="another.jsp"
<forward name="success" path="success.jsp"/>
</action>

    这个组合方式跟前一种在流程上没有太大区别,只是我们现在对于两个action分别提供了form,于是代码看上去更加清晰。于是我们可以分别处理WEB应用程序的输入和输出。值得注意的是,后一个action同样会尝试往form中写入那些参数,不过我们可以这样处理:a) 在后一个form中使用另一套属性名;b) 只提供getter而不提供setter。

基本处理过程:
    前一个action接收输入、验证、然后将数据写入业务层或持久层,重定向到后一个action,后一个action手动的从业务层/持久层取出数据,写入form(通过其他方式),交给前台JSP显示。这样做的好处是不必保留输入form中的值,因此可以使用redirect而不是forward。这样就降低了两个action之间的耦合度,同时也避免了不必要的重复提交。

注明:文中所提及的“仅有Form”指的是没有继承Struts提供的Action类,而是直接使用了Struts自身提供的Action类;“仅有Action”指的是仅继承了Struts提供的Action类而没有使用Form。

2005年11月15日

    在使用Hibernate进行查询的时候大家都会用到Hibernate缓存,其中Session缓存即一块内存空间,存放了相互关联的Java对象,这些位于Session缓存中的对象就是持久化对象,Session根据持久化对象的状态变化来同步更新数据库。这个Session缓存是Hibernate的一级缓存。此外,SessionFactory有一个内置缓存和一个外置缓存,即Hibernate的第二级缓存。而Hibernate正是由于这些缓存的存在,才使得其数据库操作效率提高,就是说,在提供了方便易操作的操作数据库数据的方式的同时保证了工作效率,但是不能因此而免去后顾之忧,需要在设计业务逻辑层的时候考虑使用最优的架构,节省有效的系统资源。在查询方面,Hibernate主要从以下几个方面来优化查询性能:

1.降低访问数据库的频率,减少select语句的数目。实现手段包括:

    使用迫切左外连接或迫切内连接检索策略。
    对延迟检索或立即检索策略设置批量检索数目。
    使用查询缓存。

2.避免多余加载程序不需要访问的数据。实现手段包括:

    使用延迟检索策略。
    使用集合过滤。

3.避免报表查询数据占用缓存。实现手段为利用投影查询功能,查询出实体的部分属性。

4.减少select语句中的字段,从而降低访问数据库的数据量。实现手段为利用Query的iterate()方法。

    在插入和更新数据时,要控制insert和update语句,合理设置映射属性来保证插入更新的性能,例如,当表中包含许多字段时,建议把dynamic-update属性和dynamic-update属性都设为true,这样在insert和update语句中就只包含需要插入或更新的字段,这可以节省数据库执行SQL语句的时间,从而提高应用的运行性能。

    还有什么其它的提升性能的方式希望和大家一起讨论。

2005年11月14日

    在关系数据库中的主键可分为自然主键(具有业务含义)和代理主键(不具有业务含义),其中代理主键可以适应不断变化的业务需求,因此更加流行。代理主键通常为整数类型,与此对应,在持久化类中野应该把OID定义为整数类型,Hibernate允许把OID定义为short、int和long类型,以及它们的包装类型。

    Hibernate提供了几种内置标识符生成器,每一种标识符生成器都有它的使用范围,应该根据所使用的数据库和Hibernate应用的软件架构来选择合适的标识符生成器。下面是几种常用数据库系统可使用的标识符生成器:

MYSQL: identity increment hilo native
MS SQL Server: identity increment hilo native
Oracle: sequence seqhilo hilo increment native
夸平台开发: native

    OID是为持久化层服务的,它不具备业务含义,而域对象位于业务逻辑层,用来描述业务模型。因此,在域对象中强行加入不具备业务含义的OID,可以看作是持久化层对业务逻辑层的一种渗透,但这种渗透是不可避免的,否则Hibernate就无法建立缓存中的对象与数据库中记录的对应关系。

    当然,映射中还包括自然主键的映射方案。对于从头设计的关系数据库模型,应该优先考虑使用代理主键。

2005年11月13日

    在网上很多文章和论坛都在讨论Hibernate,初次接触Hibernate,当然需要知道它是什么,可以用来做什么。用简单的语言来描述,可以认为Hibernate是:

    它是连接Java应用程序和关系数据库的中间件。
    它对JDBC API进行了封装,负责Java对象的持久化。
    在分层的软件架构中它位于持久花层,封装了所有数据访问细节,使业务逻辑层可以专注于实现业务逻辑。
    它是一种ORM(Object-Relation Mapping)映射工具,能够建立面向对象的域模型和关系数据库模型之间的映射。

    在Java应用中使用Hibernate包含以下步骤:

1.创建Hibernate的配置文件。
2.创建持久化类。
3.创建对象-关系映射文件。
4.通过Hibernate API编写访问数据库的代码。

2005年11月12日

在科学和工程技术领域,模型是一个很有用途的概念,它可以用来模拟一个真实的系统。在软件开发领域,模型用来表示真实世界的实体。在软件开发的不同阶段,需要为目标系统创建不同类型的模型。在分析阶段,需要创建概念模型。在设计阶段,需要创建域模型和数据模型。其中,域模型是面向对象的,数据模型是面向关系的,域模型和数据模型之间存在一种对象-关系映射。

概念模型

    概念模型清楚地显示了问题域中的实体。不管是技术人员还是非技术人员都能看得懂改面模型,他们可以很容易地提出模型中存在的问题,帮助分析人员及早对模型进行修改。在软件设计域开发周期中,模型的变更需求提出得越晚,所耗费得开发成本就越大。

    概念模型描述了每个实体得概念和属性,以及实体之间的关系:一对一、一对多和多对多。在现实生活中都可以找到相应的例子,例如一只母鸡有很多小鸡是一对多关系,一位客户选购了很多商品,而这些商品也可以被许多客户选购,这是多对多关系。

关系数据模型

    到目前为止,关系数据库仍然是使用最广泛的数据库,它存储的是关系数据。关系数据模型是在概念模型的基础上建立起来的,用于描述这些关系数据的静态结构,它由以下内容组成:

    一个或多个表
    表的所有索引
    视图
    触发器
    表与表之间的参照完整性

    数据库Schema是对数据模型的实现。对于支持SQL的关系数据库,可以采用SQL DDL语言来创建数据库Schema。SQL DDL 用于生成数据库中的物理实体,例如下面的创建CUSTOMERS表的 SQL DDL:

    create table CUSTOMERS {
      ID int not null,
      NAME varchar(20),
      AGE int,
      primary key (ID)
    };

    值得注意的是,数据库Schema有两种含义,一种是概念上的Schema,指的是一组DDL语句集,该语句集完整地描述了数据库的结构。还有一种是物理上的Schema,指的是数据库中的一个名字空间,它包含一组表、视图和存储过程等命名对象。物理Schema可以通过标准SQL语句来创建、更新和修改。例如以下SQL语句创建了两个物理Schema:

    create schema SCHEMA_A;
    create table SCHEMA_A.CUSTOMERS(ID int not null,……);

    create schema SCHEMA_B;
    create table SCHEMA_B.CUSTOMERS(ID int not null,……);

域模型

    域模型由以下内容组成:具有状态和行为的域对象;域对象之间的关系。

域对象

1.实体域对象:代表人、地点、事物或概念。通常,可以把业务领域中的名词,例如客户、订单、商品,当然也包括前面提到过的母鸡,作为实体域对象;
2.过程域对象:代表应用中的业务逻辑或流程。它通常依赖于实体域对象。
3.事件域对象:代表应用中的一些事件,例如异常、警告或超时等。

域对象之间的关系

    在域模型中,类之间存在四种关系。

1.关联(Association)
   
    关联指的是类之间的引用关系,这是实体域对象之间最普遍的一种关系。关联可以分为一对一、一对多和多对多关联。

2.依赖(Dependency)

    依赖指的是类之间的访问关系。如果类A访问类B的属性或方法,或者说是A负责实例化B,那么可以说类A依赖类B。

3.聚集(Aggregation)

    聚集指的是整体与部分之间的关系,在实体域对象之间也很常见。例如,人与手就是聚集关系,在Person类中由一个hands集合,它存放被聚集的Hand对象:

    public class Person {
      private Set hands = new HashSet();
      …………
    }

4.一般化(Generalization)

    一般化指的是类之间的继承关系。

域对象的持久化概念

    当实体域对象在内存中创建后,它们不可能永远存在。最后,他们要么从内存中清除,要么被持久化到数据存储库中。内存无法永久地保存数据,因此必须对实体域对象进行持久化。否则,如果对象没有被持久化,用户在应用运行时创建地订单信息将在应用结束运行后随之消失。

    当然,并不是所有地域对象都需要持久化,通常只有实体域对象才需要持久化,另外,有些实体域对象也不需要持久化。

    狭义的理解,“持久化”仅仅指把域对象永久保存到数据库中;广义的理解,“持久化”包括和数据库相关的各种操作。

   参考书籍:孙卫琴 精通Hibernate Java对象持久化技术详解