应用E序中?XML 文档的多数方法都把重Ҏ?XML 上:?XML 的观点用文档,按照 XML 元素、属性和字符数据内容~程。如果应用程序主要关心文档的 XML l构Q那么这U方法非常好。对于更兛_文档中所含数据而非文档本n的许多应用程序而言Q?数据l定提供了一U更单的使用 XML 的方法?
Z说明q一点,?1 l出了一个简?XML 文档的数据模型视图。文档成分——在q个例子中只有元素和文本节点——通过反映原始 XML 文档的结构连接在一赗Ş成的节点树很Ҏ和原始文档联p,但要解释树中表示的实际数据就不那么容易了?/p>
?1. 文档的文档模型视?/strong>
如果应用E序使用 XML 文档模型ҎQ您需要处理这U类型的树。这U情况下Q您用节点之间的父子关系在树的上下层之间DQ用属于同一父节点的子女之间的兄弟关pd树的同一层中D。您可以非常详尽地处理树l构Q当把树序列化ؓ文本Ӟ生成?XML 文档反映您所做的修改Q比如插入的注释Q?/p>
现在来看看与?1 截然不同的图 2Q它表示同一文档的数据绑定视图。在q里Q{换过E几乎隐藏了原始 XML 文档的所有结构,但是因ؓ只有通过两个对象Q更Ҏ看清楚真正的数据Q也更很Ҏ讉Kq些数据?/p>
?2. 文档的数据绑定视?/strong>
使用q种数据l构像是一般的 Java ~程——甚x本不需要知?XMLQ(哦,q是不要走得 ?/em>q了——我们这些专安问还得活……Q您的项目中臛_要有人明白,q种数据l构?XML 文档之间的映是如何建立的,但这仍然是向化迈出的一大步?
?仅仅是编E的化,数据l定q带来其他的好处。与文档模型Ҏ相比Q因为抽掉了许多文档l节Q数据绑定通常需要的内存更少。比如前面两个图中所C的数据l构Q文档模型方法用了 10 个单独的对象Q与此相比数据绑定只使用了两个。要创徏的东西少Q构造文档的数据l定表示可能更快一些。最后,数据l定与文档模型相比,应用E序可以更快地访问数据,因ؓ您可以控制如何表C和存储数据。我后面q要讲到q一炏V?
既然数据l定那么好,Zq要使用文档模型呢?以下两种情况需要用文档模型:
许多应用E序使用 XML 传输数据Q但q不兛_文档表示的细节。这cd用程序非帔R合使用数据l定。如果您的应用程序符合这U模式,Ll读下去?/p>
![]() ![]() |
![]()
|
目前有几U不同的框架支持 Java XML 数据l定Q但q没有标准的接口。这U情冉|l会得到改变QJava Community Process (JCP) ?JSR-031 正在努力定义q方面的标准Q请参阅 参考资?/a>Q。现在让我们选择一个框架ƈ学习使用它的接口?
本文选择?Castor 数据l定框架。Castor 目采用 BSD cd的证书,因此可在Mcd的应用程序(包括完整版权的项目)中用?Castor 实际上仅仅有 XML 数据l定Q它q支?SQL ?LDAP l定Q尽本文中不讨些其他的Ҏ。该目?2000 q初开始发P目前处于?beta 状态(一般可以用这个版本,但是如果需要问题修正,您可能需要升U到目前?CVS 版本Q。请参阅 参考资?/a>部分?Castor 站点链接Q以了解更多的细节ƈ下蝲该Y件?
![]() ![]() |
![]()
|
Castor XML 数据l定很容易上手,甚至不需要定?XML 文档格式。只要您的数据用c?JavaBean 的对象表C,Castor p自动生成表示q些数据的文档格式,然后从文档重构原始数据?/p>
![]() |
|
那么“c?JavaBean”是什么意思呢Q真正的 JavaBean 是可视化lgQ可以在开发环境中配置以用?GUI 布局。一些源于真?JavaBean 的惯例已l被 Java 团体普遍接受Q特别是对于数据cR如果一个类W合以下惯例Q我q之ؓ?#8220;c?JavaBean”的:
getX
?setX
Ҏ讉K属性(数据Q?关于技术定义已l扯得太q了Q当提到q些c?JavaBean cLQ我不再重复说明,只是UC?#8220;bean”cR?/p>
在整文章中Q我用航U班机时刻表作ؓCZ代码。我们从一个简单的 bean cd始说明它的工作原理,q个c表CZ个特定的航班Q包括四个信息项Q?/p>
下面的清?1 l出了处理航班信息的代码?/p>
清单 1. 航班信息 bean
public class FlightBean { private String m_carrier; private int m_number; private String m_departure; private String m_arrival; public FlightBean() {} public void setCarrier(String carrier) { m_carrier = carrier; } public String getCarrier() { return m_carrier; } public void setNumber(int number) { m_number = number; } public int getNumber() { return m_number; } public void setDepartureTime(String time) { m_departure = time; } public String getDepartureTime() { return m_departure; } public void setArrivalTime(String time) { m_arrival = time; } public String getArrivalTime() { return m_arrival; } } |
您可以看刎ͼq个 bean 本n没有什么意思,因此我想增加一个类q在默认?XML l定中用它Q如清单 2 所C?/p>
清单 2. 试默认的数据绑?/strong>
import java.io.*; import org.exolab.castor.xml.*; public class Test { public static void main(String[] argv) { // build a test bean FlightBean bean = new FlightBean(); bean.setCarrier("AR"); bean.setNumber(426); bean.setDepartureTime("6:23a"); bean.setArrivalTime("8:42a"); try { // write it out as XML File file = new File("test.xml"); Writer writer = new FileWriter(file); Marshaller.marshal(bean, writer); // now restore the value and list what we get Reader reader = new FileReader(file); FlightBean read = (FlightBean) Unmarshaller.unmarshal(FlightBean.class, reader); System.out.println("Flight " + read.getCarrier() + read.getNumber() + " departing at " + read.getDepartureTime() + " and arriving at " + read.getArrivalTime()); } catch (IOException ex) { ex.printStackTrace(System.err); } catch (MarshalException ex) { ex.printStackTrace(System.err); } catch (ValidationException ex) { ex.printStackTrace(System.err); } } } |
![]() |
|
q段代码首先构造了一?FlightBean
beanQƈ使用一些固定的数据初始化它。然后用?bean 默认?Castor XML 映射其写入一个输出文件。最后又d生成?XMLQ?同样使用默认映射重构 beanQ然后打印重构的 bean 中的信息。结果如下:
Flight AR426 departing at 6:23a and arriving at 8:42a
q个输出l果表明您已l成功地来回转换了航班信息(不算太糟Q只有两ơ方法调用)。现在我q不满于简单控制台输出Q准备再往深处挖一挖?/p>
Z更清楚地了解q个例子中发生了什么,看一?Marshaller.marshal()
调用生成?XML。文档如下:
<?xml version="1.0"?> <flight-bean number="426"> <arrival-time>8:42a</arrival-time> <departure-time>6:23a</departure-time> <carrier>AR</carrier> </flight-bean> |
Castor 使用 Java 内部查机制检?Marshaller.marshal()
调用传递的对象。在本例中,它发C定义的四个属性倹{Castor 在输出的 XML 中创Z个元素(文档的根元素Q表C整个对象。元素名从对象的cd中衍生出来,在这里是 flight-bean
。然后Castor 用以下两U方法中的一个,把该对象的属性值包括进来:
number
属性通过 getNumber()
Ҏ公开?int
|?
l果是上面所C的 XML 文档?/p>
![]() ![]() |
![]()
|
如果不喜?Castor 的默认映格式,您可以方便地改变映射。在我们的航班信息例子中Q比方说Q假定我们需要更紧凑的数据表C。用属性代替子元素有助于实现这个目标,我们也许q希望用比默认的名字更短一些的名字。如下所C的文档可以很好地满我们的需要:
<?xml version="1.0"?> <flight carrier="AR" depart="6:23a" arrive="8:42a" number="426"/> |
Z?Castor 使用q种格式而非默认的格式,首先需要定义描q这U格式的映射。映描q本w(非常意外的)是一?XML 文档。清?3 l出了把 bean ~组成上q格式的映射?/p>
清单 3. 紧凑格式的映?/strong>
<!DOCTYPE databases PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.exolab.org/mapping.dtd"> <mapping> <description>Basic mapping example</description> <class name="FlightBean" auto-complete="true"> <map-to xml="flight"/> <field name="carrier"> <bind-xml name="carrier" node="attribute"/> </field> <field name="departureTime"> <bind-xml name="depart" node="attribute"/> </field> <field name="arrivalTime"> <bind-xml name="arrive" node="attribute"/> </field> </class> </mapping> |
class
元素定义了一个命名类 FlightBean
的映。通过在该元素中加?auto-complete
属性ƈ把D?true
Q您可以告诉 Castor 对于该类的Q何属性,只要没有在这个元素中专门列出Q就使用默认映射。这样非常简便,因ؓ number
属性已l按照希望的方式处理了?
子元?map-to
告诉 CastorQ要?FlightBean
cȝ实例映射?XML 文档中的 flight
元素。如果您l箋使用默认的元素名 flight-bean
Q参?q后节中默认映输出的例子Q,可以不用该元素?
最后,对于每个希望以非默认方式处理的属性,可以引入一?field
子元素。这些子元素都按照相同的模式Q?name
属性给出映的属性名Q?bind-xml
子元素告?Castor 如何映射那个属性。这里要求把每个属性映成l定名称的属性?
现在已经定义了一个映,您需要告?Castor 框架在编l和解组数据时用那个映。清?4 说明了要实现q一点,需要对前面的代码做哪些修改?/p>
清单 4. 使用映射~组和解l?/strong>
... // write it out as XML (if not already present) Mapping map = new Mapping(); map.loadMapping("mapping.xml"); File file = new File("test.xml"); Writer writer = new FileWriter(file); Marshaller marshaller = new Marshaller(writer); marshaller.setMapping(map); marshaller.marshal(bean); // now restore the value and list what we get Reader reader = new FileReader(file); Unmarshaller unmarshaller = new Unmarshaller(map); FlightBean read = (FlightBean)unmarshaller.unmarshal(reader); ... } catch (MappingException ex) { ex.printStackTrace(System.err); ... |
与前?清单 2默认映射所用的代码相比Q这D代码稍微复杂一炏V在执行M其他操作之前Q首先要创徏一?Mapping
对象载入您的映射定义。真正的~组和解l也有区别。ؓ了用这个映,您需要创?Marshaller
?Unmarshaller
对象Q用定义的映配|它们,调用q些对象的方法,而不是像W一个例子那样用静态方法。最后,您必L供对映射错误产生的另一个异常类型的处理?
完成q些修改后,您可以尝试再ơ运行程序。控制台输出与第一个例子相同(?清单 2所C)Q但是现在的 XML 文档看v来符合我们的需要:
<?xml version="1.0"?> <flight carrier="AR" depart="6:23a" arrive="8:42a" number="426"/> |
![]() ![]() |
![]()
|
现在单个航班数据已经有了我们喜欢的Ş式,您可以定义一个更高的结构:航线数据。这个结构包括v降机场的标识W以及在该航U上飞行的一l航班。清?5 l出了一个包含这些信息的 bean cȝ例子?/p>
清单 5. 航线信息 bean
import java.util.ArrayList; public class RouteBean { private String m_from; private String m_to; private ArrayList m_flights; public RouteBean() { m_flights = new ArrayList(); } public void setFrom(String from) { m_from = from; } public String getFrom() { return m_from; } public void setTo(String to) { m_to = to; } public String getTo() { return m_to; } public ArrayList getFlights() { return m_flights; } public void addFlight(FlightBean flight) { m_flights.add(flight); } } |
在这D代码中Q我定义了一?addFlight()
ҎQ用于每ơ增加一个属于这条航U的航班。这是在试E序中徏立这U数据结构非常简便的办法Q但是可能和您预料的相反Q?Castor 在解l时q不使用U方法向航线中增加航班。相反,它?getFlights()
Ҏ讉K一l航班,然后直接d到集合中?
在映中处理航班集合只需要稍微改变上一个例子(?清单 3所C)中的 field
元素。清?6 昄了修改后的映文件?
<!DOCTYPE databases PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.exolab.org/mapping.dtd"> <mapping> <description>Collection mapping example</description> <class name="RouteBean"> <map-to xml="route"/> <field name="from"> <bind-xml name="from" node="attribute"/> </field> <field name="to"> <bind-xml name="to" node="attribute"/> </field> <field name="flights" collection="collection" type="FlightBean"> <bind-xml name="flight"/> </field> </class> <class name="FlightBean" auto-complete="true"> <field name="carrier"> <bind-xml name="carrier" node="attribute"/> </field> <field name="departureTime"> <bind-xml name="depart" node="attribute"/> </field> <field name="arrivalTime"> <bind-xml name="arrive" node="attribute"/> </field> </class> </mapping> |
一切都和上一个映(?清单 3所C)完全相同Q只不过?field
元素定义了一?RouteBean
?flights
属性。这个映用C两个原来不需要的属性?collection
属性的?collection
把该属性定义成一?java.util.Collection Q其他值分别定义数l,java.util.Vectors {等Q?type
属性定义包含在集合中的对象cdQ值是完整的限定类名。这里的值是 FlightBean
Q因为对q些cL没有使用包?
另一个区别在 FlightBean
cd素中Q不再需要?map-to
子元素定义绑定的元素名。定?RouteBean
?flights
属性的 field
元素Q通过它的 bind-xml
子元素定义了q一炏V因为编l或解组 FlightBean
对象只能通过该属性,它们永q用这?bind-xml
元素讑֮的名U?
我不再详l列个例子的试E序Q因为数据绑定部分和上一个例子相同。以下是用一些示例数据生成的 XML 文档Q?/p>
<?xml version="1.0"?> <route from="SEA" to="LAX"> <flight carrier="AR" depart="6:23a" arrive="8:42a" number="426"/> <flight carrier="CA" depart="8:10a" arrive="10:52a" number="833"/> <flight carrier="AR" depart="9:00a" arrive="11:36a" number="433"/> </route> |
![]() ![]() |
![]()
|
现在可以为处理完整的航班时刻表做最后的准备了。您q需要增加三?beanQ?
AirportBean
用于用于机场信息
CarrierBean
用于航线信息
TimeTableBean
把一切组合v?Z保持味性,除了上一个例子(参阅 处理集合Q中用到?RouteBean
?FlightBean
之间的从属关p,您还要在 bean 之间增加一些联pR?
要增加的W一个联pL修改 FlightBean
Q让它直接引用班Z息,而不再仅仅用代码标识班机。以下是?FlightBean
的修改:
public class FlightBean { private CarrierBean m_carrier; ... public void setCarrier(CarrierBean carrier) { m_carrier = carrier; } public CarrierBean getCarrier() { return m_carrier; } ... } |
然后?RouteBean
做同L修改Q让它引用机Z息:
public class RouteBean { private AirportBean m_from; private AirportBean m_to; ... public void setFrom(AirportBean from) { m_from = from; } public AirportBean getFrom() { return m_from; } public void setTo(AirportBean to) { m_to = to; } public AirportBean getTo() { return m_to; } ... } |
我没有给出新?bean 自n的代码,因ؓ和前面的代码相比没有什么新鲜的东西。您可以从下载文?code.jar 中找到完整的CZ代码Q请参阅 参考资?/a>Q?
您可能需要映文档的其他一些特性,以支持编l和解组的对象之间的引用。清?7 l出了一个完整的映射Q?/p>
清单 7. 完整的时刻表映射
<!DOCTYPE databases PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.exolab.org/mapping.dtd"> <mapping> <description>Reference mapping example</description> <class name="TimeTableBean"> <map-to xml="timetable"/> <field name="carriers" type="CarrierBean" collection="collection"> <bind-xml name="carrier"/> </field> <field name="airports" type="AirportBean" collection="collection"> <bind-xml name="airport"/> </field> <field name="routes" type="RouteBean" collection="collection"> <bind-xml name="route"/> </field> </class> <class name="CarrierBean" identity="ident" auto-complete="true"> <field name="ident"> <bind-xml name="ident" node="attribute"/> </field> </class> <class name="AirportBean" identity="ident" auto-complete="true"> <field name="ident"> <bind-xml name="ident" node="attribute"/> </field> </class> <class name="RouteBean"> <field name="from" type="AirportBean"> <bind-xml name="from" node="attribute" reference="true"/> </field> <field name="to" type="AirportBean"> <bind-xml name="to" node="attribute" reference="true"/> </field> <field name="flights" type="FlightBean" collection="collection"> <bind-xml name="flight"/> </field> </class> <class name="FlightBean" auto-complete="true"> <field name="carrier"> <bind-xml name="carrier" node="attribute" reference="true"/> </field> <field name="departureTime"> <bind-xml name="depart" node="attribute"/> </field> <field name="arrivalTime"> <bind-xml name="arrive" node="attribute"/> </field> </class> </mapping> |
除了新增?bean 之外Q这里有一个重要的变化Q就是增加了 identity
?reference
属性?class
元素?identity
属性,通知 Castor q个命名属性是该类实例的唯一标识W。在q里Q我?CarrierBean
?AirportBean
?ident
属性定义成它们的标识符?
bind-xml
元素?reference
属性,提供了对于该映射 Castor 所需要的另一部分链接信息?reference
设ؓ true
的映告?Castor ~组和解l引用对象的标识W,而不是对象本w的副本。从 RouteBean
链接 AirportBean
Q表CU的h点)的引用,?FlightBean
链接 CarrierBean
的引用,都用了q种Ҏ?
?Castor 使用q种cd的映解l数据时Q它自动把对象标识符转化为对实际对象的引用。您需要保证标识符的值确实是唯一的,甚至不同cd的对象之间也要保证这U唯一性。对于本例中的数据,q一点不成问题:飞机的标识符是两个字W,而机场的标识W是三个字符Q永q不会冲H。如?实有潜在冲H的可能性,只要在所代表的对象类型的每个标识W加上唯一的前~Q就可以很容易地避免q种问题?
q个例子的测试代码没有新东西Q只是增加了一些示例数据。清?8 l出了编lŞ成的 XML 文档Q?/p>
清单 8. ~组的时刻表
<?xml version="1.0"?> <timetable> <carrier ident="AR" rating="9"> <URL>http://www.arcticairlines.com</URL> <name>Arctic Airlines</name> </carrier> <carrier ident="CA" rating="7"> <URL>http://www.combinedlines.com</URL> <name>Combined Airlines</name> </carrier> <airport ident="SEA"> <location>Seattle, WA</location> <name>Seattle-Tacoma International Airport</name> </airport> <airport ident="LAX"> <location>Los Angeles, CA</location> <name>Los Angeles International Airport</name> </airport> <route from="SEA" to="LAX"> <flight carrier="AR" depart="6:23a" arrive="8:42a" number="426"/> <flight carrier="CA" depart="8:10a" arrive="10:52a" number="833"/> <flight carrier="AR" depart="9:00a" arrive="11:36a" number="433"/> </route> <route from="LAX" to="SEA"> <flight carrier="CA" depart="7:45a" arrive="10:20a" number="311"/> <flight carrier="AR" depart="9:27a" arrive="12:04p" number="593"/> <flight carrier="AR" depart="12:30p" arrive="3:07p" number="102"/> </route> </timetable> |
![]() ![]() |
![]()
|
现在Q时刻表中的所有数据都最l完成了Q简单地看一看如何在E序中处理它们。用数据绑定,您已l徏立了时刻表的数据l构Q它由几U类型的 bean l成。处理数据的应用E序代码可以直接使用q些 bean?/p>
比方_假设您要查看在西雅图和洛杉矶之间有哪些航班可供选择Qƈ且要求班具备指定的最低品质评L别。清?9 l出了用数据绑?bean l构获取q些信息的基本代码(完整的细节请参阅?参考资?/a>下蝲的源文gQ?
private static void listFlights(TimeTableBean top, String from, String to, int rating) { // find the routes for outbound and inbound flights Iterator r_iter = top.getRoutes().iterator(); RouteBean in = null; RouteBean out = null; while (r_iter.hasNext()) { RouteBean route = (RouteBean)r_iter.next(); if (route.getFrom().getIdent().equals(from) && route.getTo().getIdent().equals(to)) { out = route; } else if (route.getFrom().getIdent().equals(to) && route.getTo().getIdent().equals(from)) { in = route; } } // make sure we found the routes if (in != null && out != null) { // find outbound flights meeting carrier rating requirement Iterator o_iter = out.getFlights().iterator(); while (o_iter.hasNext()) { FlightBean o_flight = (FlightBean)o_iter.next(); if (o_flight.getCarrier().getRating() >= rating) { // find inbound flights meeting carrier rating // requirement, and leaving after outbound arrives int time = timeToMinute(o_flight.getArrivalTime()); Iterator i_iter = in.getFlights().iterator(); while (i_iter.hasNext()) { FlightBean i_flight = (FlightBean)i_iter.next(); if (i_flight.getCarrier().getRating() >= rating && timeToMinute(i_flight.getDepartureTime()) > time) { // list the flight combination printFlights(o_flight, i_flight, from, to); } } } } } } |
您可以尝试用前?清单 8中的数据。如果您询问从西雅图QSEAQ到z杉ӞLAXQ、别大于或{于 8 的班机,׃得到如下的结果:
Leave SEA on Arctic Airlines 426 at 6:23a return from LAX on Arctic Airlines 593 at 9:27a Leave SEA on Arctic Airlines 426 at 6:23a return from LAX on Arctic Airlines 102 at 12:30p Leave SEA on Arctic Airlines 433 at 9:00a return from LAX on Arctic Airlines 102 at 12:30p |
q里我不准备全面讨论使用 XML 文档模型的等价代码,那太复杂了,以单独成章。解册个问题最单的方式Q可能是首先解析 carrier
元素Q创建每个标识符代码到相应对象之间的映射链接。然后用和 清单 9中示例代码类似的逻辑。和使用 bean 的例子相比,每一步都更加复杂Q因Z码用的?XML 成分而不是真正的数据倹{性能可能更糟——只Ҏ据进行少量的操作q不是问题Q但是如果数据处理是应用E序的核心,q就会成Z个主要的焦点?
如果?bean ?XML 的映中使用更多的数据类型{换,差别会更大(无论从代码的复杂性还是从性能的角度看Q。比方说Q假设您使用很多的航班时_可能希望把文本时间{化成一U更好的国际化表C(如一天内的分钟数Q参?清单 9Q。您可以选择为文本和国际化格式定义可以替换的 get
?set
ҎQ让映射仅仅使用文本形式Q,也可以定义一个定制的 org.exolab.castor.mapping.FieldHandler
实现?Castor 使用q些倹{保留时间值的内部形式Q可以避免匹配清?9 中的航班时进行{换,也许q能加快处理速度?
除了本文中所q的之外—?FieldHandler
只是一个例子,Castor q有许多qh的特性。但愿这些例子和讨论使您能够初步领略q个框架的强大功能和灉|性?我相信,您将和我一样发?Castor 非常有用也非常有?
![]() ![]() |
![]()
|
对于使用 XML 交换数据的应用程序,数据l定是文档模型很好的替代品。它化了~程Q因为您不必再按?XML 的方式思考。相反,您可以直接用代表应用程序所用数据含义的对象。与文档模型相比Q它q潜在地提供了更好的内存和处理器使用效率?/p>
本文中,我?Castor 框架展示了一些越来越复杂的数据绑定的例子。所有这些例子都使用所谓的 直接数据l定Q开发h员根据数据定义类Q然后把数据映射?XML 文档l构。下一文章中Q我探讨另一U方法: 模式数据l定Q利用模式(?DTD、XML 模式或者其他的cdQ生成和那个模式对应的代码?
Castor 同时支持模式Ҏ和本文中介绍的直接绑定,您将在以后看到更多的 Castor 应用。我q关注着 JSR-031 Java 数据l定标准的进展,q对q些Ҏ的性能q行比较。更多了?Java 中的 XML 数据l定q个领域Q请速来讉KL最q的 IBM developerWorks?
大多数情况下Q{换框枉过ClassDescriptor和FieldDescriptor来描q{换时所需要的信息?/p>
二?转换框架
转换框架中最主要的两个类是:org.exolab.castor.xml.Marshaller和org.exolab.castor.xml.Unmarshaller
marshal: Marshaller.marshal(obj,writer);
unmarshal: Unmarshaller.unmarshal(Person.class,reader);
上面的这U{换方式,只适合于按照默认方式进行{化,如果要用映文Ӟ需要采用以下方式?br />
marshalQ?br />
Mapping mapping = new Mapping();
mapping.loadMapping(“mapping.xml”);
Marshaller marshaller = new Marshaller(writer);
marshaller.setMapping(mapping);
marshaller.marshal(obj);
Marshaller?个marshalҎ中,只有marshal(Object obj)q个Ҏ不是静态的Q其他的四个都是静态的marshal(obj,writer), marshal(obj,handler), marshal(obj,node)
unmarshalQ?br />
Mapping mapping = new Mapping();
mapping .loadMapping(“mapping.xml”);
Unmarshaller unm = new Unmarshaller(“Person.class”);//使用Person.class作ؓ构造Unmarshaller的参?br />
unm.setMapping(mapping);
Person person = (Person)unm.unmarshal(reader);
Unmarshaller中,object可以从reader中{换而来Q也可以从source、node转换而来Q静态方法均是两个参敎ͼ非静态方法都是一个来源作为参数?/p>
三?使用存在的Class和对?br /> Castor几乎可以Q何对象和XMLq行转换。当指定class的描q文件不存在Ӟ转换框架使用默认的reflection机制来获得对象的信息?/p>
转化对象存在的主要约束是Q?br /> q些class必须有一个public而且default的构造函敎ͼ必须有adequate get/setҎ?/p>
四?cLq符(ClassDescriptor)
org.exolab.castor.xml.XMLClassDescriptor
cLq符提供转换时所需的必要信息。ClassDescriptor不仅可以用于Castor XMLQ还可以用于Castor JDO
cLq符包括一l字D|q符(FieldDescriptor)
cLq符通常情况下有四种创徏方式Q两U在~译?效率较高)Q两U在q行?较ؓ方便)
~译时描q符Q?br />
1. 让需要被describe的类实现org.exolab.castor.xml.XMLClassDescriptor接口
2. 使用Source Code Generator创徏合适的descriptor
q行时描q符Q?br />
3. 默认Q用Castor的introspect机制
4. 提供mapping文gQ或者默认和配置文gq用
使用”default introspection”机制必须为每一个要转换的field配备对应的get/setҎQ?br /> 如果没有get/setҎQ但是是public的fieldQ也可以以direct field access的方式被转换Q?br /> 但是如果一个类中ؓ有的成员定义了get/setҎQ即使其他成员是public的,也不会被转换Q?br /> 自动内省机制是自动触发的。可以通过castor.properties文g控制自动转换的特性,比如改变名称转换、基本型别是转换为attributeq是element{?/p>
Mapping文g也可以用来描q要被{换的cRmapping的装载发生在marshal和unmarshal之前Qorg.exolab.castor.mapping.MappingQ?/p>
数据库表主键的知识点Q?br>Generator 为每?POJO 的实例提供唯一标识。一般情况,我们使用“native”。class 表示采用q成器接口net.sf.hibernate.id.IdentifierGenerator 实现的某个实例,其中包括Q?/p>
“assigned”
主键由外部程序负责生成,?save() 之前指定一个?/p>
“hilo”
通过hi/lo 法实现的主键生成机Ӟ需要额外的数据库表或字D|供高位值来源?/p>
“seqhilo”
与hilo cMQ通过hi/lo 法实现的主键生成机Ӟ需要数据库中的 SequenceQ适用于支?Sequence 的数据库Q如Oracle?/p>
“increment”
主键按数值顺序递增。此方式的实现机制ؓ在当前应用实例中l持一个变量,以保存着当前的最大|之后每次需要生成主键的时候将此值加1作ؓ主键。这U方式可能生的问题是:不能在集下使用?/p>
“identity”
采用数据库提供的主键生成机制。如DB2、SQL Server、MySQL 中的主键生成机制?/p>
“sequence”
采用数据库提供的 sequence 机制生成主键。如 Oralce 中的Sequence?/p>
“native”
?Hibernate Ҏ使用的数据库自行判断采用 identity、hilo、sequence 其中一U作Z键生成方式?
“uuid.hex”
?Hibernate Z128 ?UUID 法 生成16 q制数|~码后以长度32 的字W串表示Q作Z键?/p>
“uuid.string”
与uuid.hex cMQ只是生成的主键未进行编码(长度16Q,不能应用?PostgreSQL 数据库中?/p>
“foreign”
使用另外一个相兌的对象的标识W作Z键?/p>
以下举例Q?br>1、指定参数的情况Q?br> <id name="id" unsaved-value="0">
<generator class="sequence">
<param name="sequence">SEQ_CHILD</param>
</generator>
</id>
使用的是sequenceQ适合oracle数据库;
2、对于sql server2000中的数据库子增字D?在配|文件用下列方法实玎ͼ
<id name="id" type="long" unsaved-value="0">
<column name="id" sql-type="numeric" not-null="true" />
<generator class="identity" />
</id>
q里主要?identity:代表由sql server2000数据库自己提供子增字D?如果要hibernate自己提供,则用increment关键字来实现
3、如果表中的主键用字W串cd:可以用hibernate自己提供的方法实C键唯一:
<id name="id" type="string" unsaved-value="null">
<column name="cid" sql-type="char(32)" not-null="true" />
<generator class="uuid.hex" />
</id>
使用的是uuid.hex: 采用128位的法来生成一?2位字W串。最通用的一U方式。适用于所有数据库?/p>
重要的知识点:
1. 如果有部门表,有员工表,员工表中有dep_id,则表部门cd员工cLone-to-many的关p?
可以使用: ( 在部门类department中用下?
Departmentc?
/** 部门的所有员?nbsp; */
private Set staffs = new TreeSet();
xml的文?
<set name="staffs" >
<key column="dep_id"/>
<one-to-many class="hbp.sys.data.Staff"/>
</set>
如果是list,需要用索引<index> </index>,具体其中的含?不是很明?待以后研I?br>
2. 如果部门要有一个负责h,即部门表(tb_department)中有一个字D?staff_id.
那么表示部门和负责h之间的关pLmany-to-one的关p?br> Departmentc?
/** 部门负责人id */
private Staff staff;
xml 文g
<many-to-one name="staff" class="hbp.sys.data.Staff" column="staff_id"/>
3. 多对多关p?一般我们是做一个中间关联表.我用角色和权限做了个例子,
Right(id,name) Role(id,name) 中间?tb_role_right(role_id,right_id)
RightcM有一个Role的集?private Set roles=new TreeSet();
RolecM也有一个Right的集?private Set rights=new TreeSet();
则两者是明显的多对多关系.使用many-to-many来实?
xml文g?br> right.hbm.xml:如下:
<set name="roles" table="tb_role_right" cascade="all">
<key column="right_id"/>
<many-to-many column="role_id" class="hbp.sys.data.Role"/>
</set>
role.hbm.xml文g中类?
<set name="rights" table="tb_role_right" cascade="all">
<key column="role_id"/>
<many-to-many column="right_id" class="hbp.sys.data.Right"/>
</set>
4. 几个值得注意的问?
a)在xml?映射文g?写类的名字时一定用cȝ全名:??cd?(hbp.sys.data.Staff),q个错误使我费了半天?:(
b)我在写实现基本DAO操作?写了
session.delete("from Right as right where right.id="+id);
E序L报错,我折腾了半天,跟踪到底,才恍然大?hibernate在解析sql语句的时候把
其中的right,当成了数据库中的双?"保留?),?q种关键?不能随便用啊,:)
5. hibernate中HQL语言的查询根据你的sql的不同而返回不同的对象cd.
如果你用session.find(String hql)
一般会q回一个List,?from Staff staff;q回的是包含所有的员工对象的集?br> 如你的hql?select count(*) from Staff staff;则返回的是一个Integer对象
如果你用的hql?select count(distinct staff.name),count(*) from Staff staff;则返回的是一个Object
即Object[],需要先把他转换成Object[],然后在取[0],[1].
q种设计我不知道hibernate是如何处理的,感觉既好也不?好的是可以用一个find获得L查询
不好在于Ҏhql来处理返回结?Ҏ出错.