jaxb_JAXB –新手的观点,第2部分
jaxb
在本系列的第1部分中,我討論了使用JAXB和JPA將數(shù)據(jù)從XML文件加載到數(shù)據(jù)庫中的基礎(chǔ)知識。 (如果需要使用JSON而不是XML,則相同的想法應(yīng)轉(zhuǎn)化為類似Jackson的工具。)該方法是使用共享域?qū)ο?#xff0c;即,一組帶有描述XML映射和關(guān)系映射的注釋的POJO。 。
通過使用一個.java文件來描述所有數(shù)據(jù)表示形式,可以輕松地編寫數(shù)據(jù)加載器,卸載器和轉(zhuǎn)換器。 從理論上講這很簡單,但隨后我提到了理論與實踐之間的區(qū)別。 從理論上講,沒有區(qū)別。
現(xiàn)在,在第2部分中,我們將介紹當(dāng)要求這兩個工具在一個實際的數(shù)據(jù)模型上協(xié)同工作時可能遇到的一些陷阱,以及可以用來克服這些障礙的技術(shù)。
名字叫什么?
這第一點可能很明顯,但是無論如何我都會提到:與依賴于bean屬性約定的任何工具一樣,JAXB對您的方法名敏感。 您可以通過配置直接字段訪問來避免該問題,但是正如我們很快就會看到的那樣,可能有一些您想堅持使用屬性訪問的原因。
屬性名稱確定相應(yīng)元素的默認標(biāo)記名稱(盡管可以用注釋覆蓋它-在最簡單的情況下,例如@XmlElement)。 更重要的是,您的getter和setter名稱必須匹配。 當(dāng)然,最好的建議是讓您的IDE生成getter和setter,這樣就不會出現(xiàn)拼寫錯誤。
處理@EmbeddedId
假設(shè)您要加載一些表示訂單的數(shù)據(jù)。 每個訂單可能有多個訂單項,每個訂單的訂單項從1開始依次編號,因此所有訂單項的唯一ID將是訂單ID和訂單項編號的組合。 假設(shè)您使用@EmbeddedId方法表示鍵,則您的訂單項可能會這樣表示:
@Embeddable public class LineItemKey {private Integer orderId;private Integer itemNumber;/* … getters and setters … */ }@XmlRootElement @Entity @Table(name=”O(jiān)RDER_ITEM”) public class OrderLineItem {@EmbeddedId@AttributeOverrides(/*…*/)private LineItemKey lineItemKey;@Column(name=”PART_NUM”)private String partNumber;private Integer quantity;// … getters and setters … };編組和解組代碼看起來很像第1部分中 Employee示例中的代碼。 注意,由于LineItemKey類是由OrderLineItem引用的,因此我們不必顯式地告訴JAXBContext有關(guān)LineItemKey類。
LineItemKey liKey = new LineItemKey();liKey.setOrderId(37042);liKey.setItemNumber(1);OrderLineItem lineItem = new OrderLineItem();lineItem.setLineItemKey(liKey);lineItem.setPartNumber(“100-02”);lineItem.setQuantity(10);JAXBContext jaxb = JAXBContext.newInstance(OrderLineItem.class);Marshaller marshaller = jaxb.createMarshaller();marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);marshaller.marshal(lineItem, System.out);但是,我們可能不會對由此產(chǎn)生的XML結(jié)構(gòu)感到興奮:
<?xml version=”1.0” encoding=”UTF-8” standalone=”yes”?> <orderLineItem><lineItemKey><itemNumber>1</itemNumber><orderId>37042</orderId></lineItemKey><partNumber>100-02</partNumber><quantity>10</quantity> </orderLineItem>如果我們不希望<lineItemKey>元素怎么辦? 如果我們讓JAXB使用屬性訪問,那么一個選擇就是更改屬性定義(即我們的getter和setter),使OrderLineItem看起來像JAXB的平面對象(并可能對我們應(yīng)用程序的其余部分而言);這可能是一件好事)。
@XmlRootElement @Entity @Table(name=”O(jiān)RDER_ITEM”) public class OrderLineItem {@EmbeddedId@AttributeOverrides(/*…*/)private LineItemKey lineItemKey;// … additional fields …@XmlTransientpublic LineItemKey getLineItemKey() {return lineItemKey;}public void setLineItemKey(LineItemKey lineItemKey) {this.lineItemKey = lineItemKey;}// “pass-thru” properties to lineItemKeypublic Integer getOrderId() {return lineItemKey.getOrderId();}public void setOrderId(Integer orderId) {if (lineItemKey == null) {lineItemKey = new LineItemKey();}lineItemKey.setOrderId(orderId);}public Integer getItemNumber() {return lineItemKey.getItemNumber();}public void setItemNumber(Integer itemNumber) {if (lineItemKey == null) {lineItemKey = new LineItemKey();}lineItemKey.setItemNumber(itemNumber);}// … additional getters and setters … };請注意,在lineItemKey getter上添加了@XmlTransient; 這告訴JAXB不要映射此特定屬性。 (如果JPA使用字段訪問,則可以完全刪除lineItemKey getter和setter。另一方面,如果JPA使用屬性訪問,則需要將“直通”獲取器標(biāo)記為@Transient以防止JPA提供程序推斷到ORDER_ITEM表的錯誤映射。)
但是,如果lineItemKey標(biāo)記為@ XmlTransient,JAXB將不知道在拆組期間需要創(chuàng)建嵌入式LineItemKey實例。 在這里,我們通過使“傳遞”設(shè)置器確保實例存在來解決該問題。 JPA至少應(yīng)在使用字段訪問的情況下對此進行容忍。 如果您希望該方法具有線程安全性,則必須同步設(shè)置器。 或者,您可以在默認構(gòu)造函數(shù)中創(chuàng)建LineItemKey(如果您確信JPA提供程序不會介意)。
確保僅影響JAXB(沒有專用的getter和setter)的另一個選項可能是使用ObjectFactory,該ObjectFactory在返回LineItemKey之前將其注入OrderLineItem中。 但是,據(jù)我所知,ObjectFactory必須覆蓋一個包中的所有類,因此,如果您在同一包中有許多簡單的域?qū)ο蠛鸵恍?fù)雜的對象(并且沒有其他理由來創(chuàng)建ObjectFactory),那么您可能要避免這種方法。
您可能還想通過在嘗試獲取返回值之前檢查LineITemKey是否存在來保護直通getter免受空指針異常的影響。
無論如何,我們的XML現(xiàn)在應(yīng)該如下所示:
<?xml version=”1.0” encoding=”UTF-8” standalone=”yes”?> <orderLineItem><itemNumber>1</itemNumber><orderId>37042</orderId><partNumber>100-02</partNumber><quantity>10</quantity> </orderLineItem>相關(guān)對象:一對多
當(dāng)然,您的訂單項屬于訂單,因此您可能有一個ORDER表(和相應(yīng)的Order類)。
@XmlRootElement @Entity @Table(name=”O(jiān)RDER”) public class Order {@Id@Column(name=”O(jiān)RDER_ID”)private Integer orderId;@OneToMany(mappedBy=”order”)private List<OrderLineItem> lineItems;// … getters and setters … }我們已經(jīng)與OrderLineItem建立了一對多的關(guān)系。 請注意,出于JPA的目的,我們期望OrderLineItem擁有此關(guān)系。
現(xiàn)在,我們將從OrderLineItem中刪除@XmlRootElement批注。 (我們不必這樣做;注釋使該類有資格成為根元素,但不排除也將其用作嵌套元素。但是,如果我們要繼續(xù)編寫僅表示OrderLineItem的XML,則還有一些其他的決定要做,因此我們暫時不做。)
為了使編組滿意,我們將OrderLineItem @XmlTransient的Order屬性。 這避免了循環(huán)引用,否則該循環(huán)引用可以解釋為無限深的XML樹。 (您可能始終不打算在<orderLineItem>元素下嵌入完整的訂單詳細信息。)
將<orderLineItem>嵌入在<order>元素下,不再需要將<orderId>元素放在<orderLineItem>下。 我們知道從應(yīng)用程序中其他地方的代碼仍然可以使用lineItem.getOrder()。getOrderId()來從OrderLineItem中刪除orderId屬性。
新版本的OrderLineItem如下所示:
@Entity @Table(name=”O(jiān)RDER_ITEM”) public class OrderLineItem {@EmbeddedId@AttributeOverrides(/*…*/)private LineItemKey lineItemKey;@MapsId(“orderId”)@ManyToOneprivate Order order;@Column(name=”PART_NUM”)private String partNumber;private Integer quantity; @XmlTransientpublic Order getOrder() {return order;}public void setOrder(Order order) {this.order = order;}public Integer getItemNumber() {return lineItemKey.getItemNumber();}public void setItemNumber(Integer itemNumber) {if (lineItemKey == null) {lineItemKey = new LineItemKey();}lineItemKey.setItemNumber(itemNumber);}// … more getters and setters … };我們的JAXBContext需要被告知有關(guān)Order類的信息。 在這種情況下,無需明確告知OrderLineItem。 因此我們可以像這樣測試編組:
JAXBContext jaxb = JAXBContext.newInstance(Order.class);List<OrderLineItem> lineItems = new ArrayList<OrderLineItem>();Order order = new Order();order.setOrderId(37042);order.setLineItems(lineItems);OrderLineItem lineItem = new OrderLineItem();lineItem.setOrder(order);lineItem.setLineNumber(1);lineItem.setPartNumber(“100-02”);lineItem.setQuantity(10);lineItems.add(lineItem);lineItem = new OrderLineItem();lineItem.setOrder(order);lineItem.setLineNumber(2);lineItem.setPartNumber(“100-17”);lineItem.setQuantity(5);lineItems.add(lineItem);Marshaller marshaller = jaxb.createMarshaller();marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);marshaller.marshal(order, System.out);請注意,我們?yōu)槊總€訂單項設(shè)置了order屬性。 編組時,JAXB不會關(guān)心此問題(因為該屬性為@XmlTransient,并且其他屬性均不取決于它影響的內(nèi)部狀態(tài)),但我們希望保持對象關(guān)系的一致性。 如果我們要將訂單傳遞給JPA,那么無法設(shè)置order屬性將成為一個問題-我們很快就會回到這一點。
我們應(yīng)該得到這樣的輸出:
<?xml version=”1.0” encoding=”UTF-8” standalone=”yes”?><order><orderId>37042</orderId><lineItems><lineNumber>1</lineNumber><partNumber>100-02</partNumber><quantity>10</quantity></lineItems><lineItems><lineNumber>2</lineNumber><partNumber>100-17</partNumber><quantity>5</quantity></lineItems></order>默認元素名稱映射在每個訂單項周圍放置一個<lineItems>標(biāo)記(因為這是屬性名稱),這有點差。 我們可以通過將@XmlElement(name =“ lineItem”)放在Order的getLineItems()方法上來解決此問題。 (然后,如果我們希望將整個訂單項元素列表包裝在單個<lineItems>元素中,則可以在同一方法上使用@XmlElementWrapper(name =“ lineItems”)批注來實現(xiàn)。)
此時,封送測試應(yīng)該看起來不錯,但是如果取消封送訂單并要求JPA保留生成的訂單行項目對象,就會遇到麻煩。 問題在于解組器未設(shè)置OrderLineItem的order屬性(出于JPA的目的,該屬性擁有Order-to-OrderLineItem關(guān)系)。
我們可以通過讓Order.setLineItems()遍歷訂單項列表并在每個訂單項上調(diào)用setOrder()來解決此問題。 這依賴于JAXB首先構(gòu)建訂單項列表,然后將其傳遞給setLineItems();。 它在我的測試中起作用,但是我不知道它是否將始終與所有JAXB實現(xiàn)一起起作用。
另一種選擇是在解組之后但將對象傳遞給JPA之前,在每個OrderLineItem上調(diào)用setOrder()。 這也許更簡單,但是感覺就像是在跳動。 (封裝的部分要點是,您的設(shè)置員應(yīng)該可以確保對象始終保持內(nèi)部一致的狀態(tài);那么為什么要將這種責(zé)任轉(zhuǎn)移給對象類之外的代碼呢?)
為了簡化操作,在嘗試解決此問題時,我將跳過一些我曾想過的更詳細的想法。 稍后我們將討論@XmlID和@XmlIDREF時,我們將討論另一種解決方案。
財產(chǎn)獲取案例
我依靠修改后的二傳手來解決前兩個問題。 如果您習(xí)慣了設(shè)置器應(yīng)該只有一行(this.myField = myArgument)的想法,這似乎值得懷疑。 (然后再次,如果您不讓設(shè)置員為您做任何工作,那么通過封裝字段來購買什么?)
@XmlTransientpublic List<OrderLineItem> getLineItems() {return lineItems;}public void setLineItems(List<OrderLineItem> lineItems) {this.lineItems = lineItems;}// @Transient if JPA uses property access@XmlElement(name=”lineItem”)public List<OrderLineItem> getLineItemsForJAXB() {return getLineItems();}public void setLineItemsForJAXB(List<OrderLineItems> lineItems) {setLineItems(lineItems);// added logic, such as calls to setOrder()…}如果需要,您可以避免在應(yīng)用程序的其他任何地方使用“ ForJAXB”屬性,因此,如果您覺得必須“僅針對JAXB”添加設(shè)置器邏輯,則該方法將阻止添加的邏輯妨礙您。
但是,以我的觀點,我上面描述的setter邏輯類型只是從外部代碼中隱藏了bean屬性的實現(xiàn)細節(jié)。 我認為在這些情況下,JAXB鼓勵更好的抽象。
如果您認為JAXB是序列化對象內(nèi)部狀態(tài)的一種方法,那么字段訪問似乎更可取。 (我聽說過要在任何情況下都將JPA與字段訪問一起使用的論點。)不過,到最后,您希望該工具為您完成工作。 將JAXB視為構(gòu)建(或記錄)對象的外部機制可能更加實用。
相關(guān)對象:一對一,多對多
在一對多關(guān)系正常工作的情況下,似乎一對一關(guān)系應(yīng)該很容易。 但是,盡管一對多關(guān)系通常會使其具有XML的層次結(jié)構(gòu)性質(zhì)(“許多”是“一個”的子代),但一對一關(guān)系中的對象通常只是對等體; 因此,充其量,將一個元素嵌入另一個XML表示形式的選擇是任意的。
多對多關(guān)系對層次模型提出了更大的挑戰(zhàn)。 而且,如果您有一個更復(fù)雜的關(guān)系網(wǎng)絡(luò)(無論其基數(shù)如何),可能沒有一種直接的方法將對象排列成樹。
在探索通用解決方案之前,最好暫時停頓一下,問問自己是否需要通用解決方案。 我們的項目需要加載兩種符合父子關(guān)系的對象,因此我之前描述的技術(shù)就足夠了。 可能是您根本不需要將整個對象模型保存為XML。
但是,如果您確實發(fā)現(xiàn)需要建模不適合父子模型的關(guān)系的方法,則可以使用@XmlID和@XmlIDREF來實現(xiàn)。
當(dāng)您學(xué)習(xí)使用@XmlID的規(guī)則時,您可能會問自己,將原始外鍵元素存儲在reference元素下是否容易(類似于RDBMS通常表示外鍵的方式)。 您可以,并且編組將不會產(chǎn)生漂亮的XML問題。 但是在解組期間或之后,您將負責(zé)自行重組關(guān)系圖。 @XmlID的規(guī)則很煩人,但是我發(fā)現(xiàn)它們很難適應(yīng),避免它們會證明這種努力是合理的。
ID值必須是字符串,并且它們在XML文檔中的所有元素(不僅是給定類型的所有元素)中必須是唯一的。 這是因為從概念上講,ID引用是無類型的。 實際上,如果讓JAXB從架構(gòu)構(gòu)建域?qū)ο?#xff0c;它將把您的@XmlIDREF元素(或?qū)傩?#xff09;映射到Object類型的屬性。 (但是,當(dāng)注釋自己的域類時,只要引用的類型具有以@XmlID注釋的字段或?qū)傩?#xff0c;就可以將@XmlIDREF與帶類型的字段和屬性一起使用。我寧愿這樣做,因為這樣可以避免不必要的強制轉(zhuǎn)換在我的代碼中。)建立關(guān)系的鍵可能不遵循這些規(guī)則; 但這沒關(guān)系,因為您可以創(chuàng)建一個屬性(例如,名為xmlId)。
假設(shè)我們的每個訂單都有一個客戶和一個“收貨人”地址。 此外,每個客戶都有一個帳單郵寄地址列表。 數(shù)據(jù)庫中的兩個表(CUSTOMER和ADDRESS)都使用Integer代理鍵,其順序從1開始。
在我們的XML中,“客戶”和“收貨人”地址可以表示為“訂單”下的子元素; 但也許我們需要跟蹤當(dāng)前沒有任何訂單的客戶。 同樣,帳單地址列表可以表示為“客戶”下的子元素列表,但這將不可避免地導(dǎo)致數(shù)據(jù)重復(fù),因為客戶將訂單運送到了帳單地址。 因此,我們將使用@XmlID。
我們可以如下定義地址:
@Entity@Table(name=”ADDRESS”)public class Address {@Id@Column(name=”ADDRESS_ID”)private Integer addressId;// other fields…@XmlTransientpublic Integer getAddressId() {return addressId;}public void setAddressId(Integer addressId) {this.addressId = addressId;}// @Transient if JPA uses property access@XmlID@XmlElement(name=”addressId”)public String getXmlId() {return getClass().getName() + getAddressId();}public void setXmlId(String xmlId) {//TODO: validate xmlId is of the form <className><Integer>setAddressId(Integer.parseInt(xmlId.substring( getClass().getName().length() )));}// … more getters and setters … }這里的xmlId屬性提供了JAXB的addressId視圖。 在類名前面加一個可在其鍵可能會沖突的類型之間提供唯一性。 如果表具有更復(fù)雜的自然鍵,則必須將鍵的每個元素轉(zhuǎn)換為字符串,并可能使用某種分隔符,并將其全部串聯(lián)在一起。
這種想法的一種變體是使用@XmlAttribute而不是@XmlElement。 我通常更喜歡使用元素作為數(shù)據(jù)值(因為它們在邏輯上是文檔的內(nèi)容),但是XmlId可以說是描述<Address> XML元素,而不是地址本身,因此記錄起來可能很有意義作為屬性。
為了解組工作,我們還必須從setter的xmlId中解析出addressId值。 如果我們同時保留xmlId屬性和addressId屬性,則可以避免這種情況。 在這種情況下,xmlId setter可能會丟掉它的值; 但是我不喜歡該選項,因為它可以節(jié)省相對較少的工作量,并且有可能遇到XMLId和addressId值不一致的XML文檔。 (有時您可能不得不承認文檔不一致的可能性,例如,如果您堅持戀愛關(guān)系的雙方,我將在后面討論。)
接下來,我們將創(chuàng)建客戶映射:
@Entity@Table(name=“CUSTOMER”)public class Customer {@Id@Column(name=”CUSTOMER_ID”)private Integer customerId;@ManyToMany@JoinTable(name = “CUST_ADDR”)private List<Address> billingAddresses;// other fields…@XmlTransientpublic Integer getCustomerId() {return customerId;}public void setCustomerId(Integer customerId) {this.customerId = customerId;}@XmlIDREF@XmlElement(name = “billingAddress”)public List<Address> getBillingAddresses() {return billingAddresses;}public void setBillingAddresses(List<Address> billingAddresses) {this.billingAddresses = billingAddresses;}// @Transient if JPA uses property access@XmlID@XmlElement(name=”customerId”)public String getXmlId() {return getClass().getName() + getCustomerId();}public void setXmlId(String xmlId) {//TODO: validate xmlId is of the form <className><Integer>setCustomerId(Integer.parseInt(xmlId.substring( getClass().getName().length() )));}// … more getters and setters …}客戶的xmlId的處理與地址的處理相同。 我們用@XmlIDREF批注標(biāo)記了billingAddresses屬性,告訴JAXB每個<billingAddress>元素都應(yīng)包含一個引用地址的ID值,而不是實際的Address元素結(jié)構(gòu)。 以同樣的方式,我們將customer和shipToAddress屬性添加到Order中,并用@XmlIDREF注釋。
此時,所有對客戶或地址的引用都被標(biāo)記為@XmlIDREF。 這意味著盡管我們可以將數(shù)據(jù)封送為XML,但結(jié)果實際上將不包含任何Customer或Address數(shù)據(jù)。 如果在您解組時@XmlIDREF與文檔中的@XmlID不對應(yīng),則未編組對象上的相應(yīng)屬性將為null。 因此,如果我們真的希望這樣做,我們必須創(chuàng)建一個新的@XmlRootElement來包含所有數(shù)據(jù)。
@XmlRootElementpublic class OrderData {private List<Order> orders;private List<Address> addresses;private List<Customer> customers;// getters and setters}此類與我們數(shù)據(jù)庫中的任何表都不對應(yīng),因此它沒有JPA批注。 與先前的List-type屬性一樣,我們的getter可以具有@XmlElement和@XmlElementWrapper批注。 如果我們組裝并封送一個OrderData對象,則可能會得到以下內(nèi)容:
<?xml version=”1.0” encoding=”UTF-8” standalone=”yes”?><orderData><addresses><address><addressId>Address1010</addressId><!-- … other elements … --></address><address><addressId>Address1011</addressId><!-- … --></address></addresses><customers><customer><billingAddress>Address1010</billingAddress><billingAddress>Address1011</billingAddress><customerId>Customer100</customerId></customer></customers><orders><order><customer>Customer100</customer><lineItem><itemNumber>1</itemNumber><partNumber>100-02</partNumber><quantity>10</quantity></lineItem><lineItem><lineNumber>2</lineNumber><partNumber>100-17</partNumber><quantity>5</quantity></lineItem><orderId>37042</orderId><shipToAddress>Address1011</shipToAddress></order></orders></orderData>到目前為止,我們僅映射了每個關(guān)系的一側(cè)。 如果我們的域?qū)ο笮枰С蛛p向?qū)Ш?#xff0c;則可以選擇:我們可以將關(guān)系一側(cè)的屬性標(biāo)記為@XmlTransient; 這使我們處在與以分層表示的一對多關(guān)系相同的情況下,解組將不會自動設(shè)置@XmlTransient屬性。 或者,我們可以將兩個屬性都設(shè)置為@XmlIDREF,因為意識到有人可能會編寫不一致的XML文檔。
回顧相關(guān)對象:一對多
早些時候,當(dāng)我們查看一對多關(guān)系時,我們僅依賴于包含-嵌入在父元素中的子元素。 包容性的局限性之一是它只允許我們映射關(guān)系的一側(cè)。 由于我們的域?qū)ο笮枰聪蜿P(guān)系才能與JPA配合使用,因此這在解組期間使我們跳過了一些麻煩。
我們已經(jīng)看到@XmlID和@XmlIDREF提供了更一般的關(guān)系表示。 混合使用這兩種技術(shù),我們可以表示父子關(guān)系的兩面(需要注意的是,就像我們在XML中顯示關(guān)系的兩面一樣,您可以手工編寫具有不一致關(guān)系的XML文檔)。
我們可以修改前面的一對多示例,使其看起來像這樣:
@XmlRootElement @Entity @Table(name=”O(jiān)RDER”) public class Order {@Id@Column(name=”O(jiān)RDER_ID”)private Integer orderId;@OneToMany(mappedBy=”order”)private List<OrderLineItem> lineItems;@XmlTransientpublic Integer getOrderId() {return orderId;}public void setOrderId(Integer orderId) {this.orderId = orderId;}@XmlID@XmlElement(name=”orderId”)public String getXmlId() {return getClass().getName() + getOrderId;}public void setXmlId(String xmlId) {//TODO: validate xmlId is of the form <className><Integer>setOrderId(Integer.parseInt(xmlId.substring( getClass().getName().length() )));}@XmlElement(“l(fā)ineItem”)public List<OrderLineItem> getLineItems() {return lineItems;}public void setLineItems(List<OrderLineItem> lineItems) {this.lineItems = lineItems;} }@Entity @Table(name=”O(jiān)RDER_ITEM”) public class OrderLineItem {@EmbeddedId@AttributeOverrides(/*…*/)private LineItemKey lineItemKey;@MapsId(“orderId”)@ManyToOneprivate Order order;@Column(name=”PART_NUM”)private String partNumber;private Integer quantity; @XmlIDREFpublic Order getOrder() {return order;}public void setOrder(Order order) {this.order = order;}public Integer getItemNumber() {return lineItemKey.getItemNumber();}public void setItemNumber(Integer itemNumber) {if (lineItemKey == null) {lineItemKey = new LineItemKey();}lineItemKey.setItemNumber(itemNumber);}// … more getters and setters … }當(dāng)編組Order時,我們現(xiàn)在將orderId寫為XML ID。 我們沒有使用OrderLineItem @XmlTransient的order屬性,而是通過寫@XmlIDREF而不是完整的Order結(jié)構(gòu)來避免無限遞歸; 因此,雙方的關(guān)系都以我們在解組時可以理解的方式得以保留。
生成的XML如下所示:
<?xml version=”1.0” encoding=”UTF-8” standalone=”yes”?><order><orderId>Order37042</orderId><lineItem><lineNumber>1</lineNumber><order>Order37042</order><partNumber>100-02</partNumber><quantity>10</quantity></lineItem><lineItem><lineNumber>2</lineNumber><order>Order37042</order><partNumber>100-17</partNumber><quantity>5</quantity></lineItem></order>而編組和解組工作都是我們想要的。 重復(fù)包含訂單ID值是我們可能對輸出唯一的抱怨。 我們可以通過使用@XmlAttribute而不是@XmlElement來減少視覺影響。 這是另一種情況,我們可能會認為該值不是“真實內(nèi)容”,因為我們只是將其放入以幫助JAXB進行編組。
總結(jié)思想
如標(biāo)題所示,我作為JAXB的新手經(jīng)歷了本練習(xí)。 這絕不是關(guān)于JAXB可以做什么的全面討論,從我閱讀的文檔中,我什至說我已經(jīng)忽略了它的一些最復(fù)雜的功能。
但是,我希望這可以作為有用的入門,并可以說明來自bean約定以及與POJO進行友好交互的工具和框架的強大功能。
我還要重申一點,就是您可以根據(jù)自己的意愿使這種技術(shù)變得復(fù)雜。 因此,了解您的需求真正需要多少復(fù)雜度是關(guān)鍵。
翻譯自: https://www.javacodegeeks.com/2014/09/jaxb-a-newcomers-perspective-part-2.html
jaxb
總結(jié)
以上是生活随笔為你收集整理的jaxb_JAXB –新手的观点,第2部分的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 就不想再用笔记本电脑自带的网卡了就不想再
- 下一篇: 如果你的电脑中毒你会怎么办电脑中毒了该怎