Client Application Developer's Guide
This chapter describes the Liquid Data client-side data programming model and framework based on Service Data Objects (SDO). It introduces SDO and describes common programming tasks undertaken with SDO. It covers the following topics:
The Service Data Object (SDO) specification defines a Java-based programming architecture and API for data access. A central goal of SDO is to provide client applications with a unified programming model for working with data in a disconnected way, regardless of its physical source or format. SDO thereby simplifies the way applications use data.
SDO specifies a data programming API as well as an architecture. The architecture part of the specification describes the components for enabling data access, such as mediators which serve as the intermediary between the client and back-end sources. In SDO terms, Liquid Data is a member.
In terms of client data programming, SDO has characteristics in common with other data access technologies, such as JDBC and Java Data Objects (JDO). Like JDO, SDO provides a static API for accessing data through typed accessors (for example, getCustomerName()
). Like JDBC's RowSet interface, SDO has a dynamic API for accessing data through untyped accessors (for example, getString("CUSTOMER_NAME")
). What distinguishes SDO from other technologies, however, is that SDO gives applications both a static and a dynamic API for accessing data, along with a disconnected model for accessing externally persisted data.
Liquid Data implements the SDO specification as its Java client programming model. In concrete terms, this means that when a client application invokes a read function on a data service through the Data Service Mediator API (also called the Mediator API) or Liquid Data control, it gets the information back as a data object. A data object is the fundamental component of the SDO programming model and represents a unit of structured information.
The role of data objects, along with other key components in the SDO framework, are summarized as follows:
String
or int
. A complex type corresponds to a branch node in the tree, and contains another data object.Note: For more on the Data Service Mediator API, see Accessing Data Services from Java Clients.
See also For More Information on page 2-29.
Figure 2-1 SDO Components with Data Graph
Data objects are passed between the mediator and client applications in a data graph. A data graph can have only a single root object (for example, CUSTOMERDocument in Figure 2-1). For result involving repeating objects, therefore, a single root element prefixed by "ArrayOf" is introduced to serve as the data graph root node. For more information, see ArrayOf Types.
Liquid Data leverages XMLBeans technology to generate static interfaces from XML. As a result, many features of the underlying XMLBeans technology are available in SDO as well. For more information on XMLBeans, see http://xmlbeans.apache.org
.
Furthermore, all SDO types inherit from XmlObject, so factory classes for creating instances and parsing data objects are present from the inherited XmlObject interface.
This section presents a simple example (Listing 2-1) of an SDO client application. The example gets information from Liquid Data through the Mediator API by instantiating a remote interface to a data service and invoking data service functions. It extracts information for a customer, modifies it, and submits the change to the mediator for update to the source or sources.
Listing 2-1 Sample SDO Client Application
import java.util.Hashtable;
import javax.naming.InitialContext;
import dataServices.customerDB.customer.ArrayOfCUSTOMERDocument;
import dataservices.customerdb.CUSTOMER;
public class ClientApp {
public static void main(String[] args) throws Exception {
Hashtable h = new Hashtable();
h.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
h.put(Context.PROVIDER_URL,"t3://localhost:7001");
h.put(Context.SECURITY_PRINCIPAL,"weblogic");
h.put(Context.SECURITY_CREDENTIALS,"weblogic");
Context context = new InitialContext(h);
// get the Customer data service and run dynamic invocation of data service
CUSTOMER custDS = CUSTOMER.getInstance(context, "RTLApp");
ArrayOfCUSTOMERDocument myCustomer =
(ArrayOfCUSTOMERDocument) custDS.invoke("CUSTOMER", null);
// get and show customer name
String existingFName =
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).getFIRSTNAME();
String existingLName =
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).getLASTNAME();
System.out.println(" \n---------------- \n Before Change: \n");
System.out.println(existingFName + existingLName);
// change the customer name
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).setFIRSTNAME("J.B.");
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).setLASTNAME("Kwik");
custDS.submit(myCustomer,"ld:DataServices/CustomerDB/CUSTOMER");
// re-query and print new name
myCustomer = (ArrayOfCUSTOMERDocument) custDS.invoke("CUSTOMER",null);
String newFName =
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).getFIRSTNAME();
String newLName =
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).getLASTNAME();
String newName = newFName.concat(newLName);
System.out.println(" \n---------------- \n After Change: \n");
System.out.println(newFName + newLName); }
}
The example above includes the following processing steps:
invoke()
function of the data service, pouring the results into an ArrayOfCUSTOMERDocument object. In the sample, an SDO is acquired through the Data Service Mediator API and modified through the SDO static API. Keep in mind that you can acquire data objects through the Liquid Data control as well. Therefore, it is useful to note the difference in the sample between mediator API calls and SDO calls.
The data service interface is instantiated and invoked through mediator API calls as follows:
ArrayOfCUSTOMERDocument myCustomer =
( ArrayOfCUSTOMERDocument) ds.invoke("CUSTOMER", null);
Once the data object is created, its properties are accessed using the SDO static interface (which returns the actual type of that node):
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).getFIRSTNAME();
As mentioned elsewhere, the SDO client data programming model includes both static and dynamic interfaces. The equivalent call using the dynamic interface would be as follows:
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).get("FIRSTNAME");
(This will returns an Object instance that you will need to cast to the type ordinarily returned by the static interface.)
Finally, the change is submitted to the data service mediator for propagation to the back-end source through the submit()
function in the mediator interface.
Although code for handling exceptions is not shown in the example, a runtime error in SDO throws an SDOException
. If an exception is generated by a data source, it is wrapped in an SDOException
.
Note: For more information on the Mediator API, see Accessing Data Services from Java Clients.
For complete documentation on the mediator and SDO APIs, refer to the SDO Update Javadoc.
Data objects and data graphs can be serialized and printed to standard output. In fact, viewing a printed data graph when developing client applications can help you understand how data objects are composed.
Listing 2-2 shows a data graph associated with a modified data object. The printout is produced by the following code:
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).setFIRSTNAME("J.B.");
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).setLASTNAME("Nimble");System.out.println(myCustomer.getDataGraph());
Notice the data graph features in the following listing examples:
For more information on data graphs, see Working with Data Graphs.
Listing 2-2 Serialized Data Graph
<com:datagraph xmlns:com="commonj.sdo">
<xsd>
<xs:schema
targetNamespace="ld:DataServices/CustomerDB/CUSTOMER"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:single="ld:DataServices/CustomerDB/CUSTOMER">
<xs:include schemaLocation="CUSTOMER.xsd"/>
<xs:element name="ArrayOfCUSTOMER">
<xs:complexType>
<xs:sequence>
<xs:element ref="single:CUSTOMER" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
</xsd>
<changeSummary>
<CUSTOMER com:ref="/ArrayOfCUSTOMER/CUSTOMER[1]">
<FIRST_NAME>J. B.</FIRST_NAME>
<LAST_NAME>Nimble</LAST_NAME>
</CUSTOMER></changeSummary>
<ns0:ArrayOfCUSTOMER xmlns:ns0="ld:DataServices/CustomerDB/CUSTOMER">
<ns0:CUSTOMER>
<CUSTOMER_ID>CUSTOMER1</CUSTOMER_ID>
<FIRST_NAME>Jack B. </FIRST_NAME><LAST_NAME>Quick</LAST_NAME>
<CUSTOMER_SINCE>2001-10-01</CUSTOMER_SINCE>
<EMAIL_ADDRESS>Jack@hotmail.com</EMAIL_ADDRESS>
<TELEPHONE_NUMBER>2145134119</TELEPHONE_NUMBER>
<SSN>295-13-4119</SSN>
<BIRTH_DAY>1970-01-01</BIRTH_DAY>
<DEFAULT_SHIP_METHOD>AIR</DEFAULT_SHIP_METHOD>
<EMAIL_NOTIFICATION>1</EMAIL_NOTIFICATION>
<NEWS_LETTTER>0</NEWS_LETTTER>
<ONLINE_STATEMENT>1</ONLINE_STATEMENT>
<LOGIN_ID>Jack</LOGIN_ID>
</ns0:CUSTOMER><ns0:CUSTOMER>
<CUSTOMER_ID>CUSTOMER2</CUSTOMER_ID>
<FIRST_NAME>Kevin</FIRST_NAME>
<LAST_NAME>Smith</LAST_NAME>
<CUSTOMER_SINCE>2001-10-01</CUSTOMER_SINCE>
<EMAIL_ADDRESS>JOHN_2@yahoo.com</EMAIL_ADDRESS>
<TELEPHONE_NUMBER>3607467964</TELEPHONE_NUMBER>
<SSN>087-46-7964</SSN>
<BIRTH_DAY>1978-09-21</BIRTH_DAY>
<DEFAULT_SHIP_METHOD>AIR</DEFAULT_SHIP_METHOD>
<EMAIL_NOTIFICATION>1</EMAIL_NOTIFICATION>
<NEWS_LETTTER>0</NEWS_LETTTER>
<ONLINE_STATEMENT>1</ONLINE_STATEMENT>
<LOGIN_ID>Jerry</LOGIN_ID></ns0:CUSTOMER>
<ns0:CUSTOMER>
. . .
</ns0:ArrayOfCUSTOMER>
</com:datagraph>
Liquid Data client developers can use the Liquid Data Console to view the XML schema types associated with data services. (See Figure 2-2) The return type tab indicates, for example, whether an element is a string, int, or complex type.
Figure 2-2 Return Types Display in Liquid Data Console
The Table 2-3 shows XML schema types correspondence to SDO Java types.
As mentioned elsewhere, data graphs are used to pass data objects between clients and the data service mediator. While a data service function can return multiple elements, a data graph can only have a single root element.
To accommodate functions that return multiple array types, Liquid Data manufactures a root element to serve as the single container for array types. The elements are named with the prefix "ArrayOf". For example, for a function defined to return multiple CUSTOMER elements, the root element is ArrayOfCUSTOMER, as shown in Listing 2-3.
Listing 2-3 Array Root Element
<ns0:ArrayOfCUSTOMER xmlns:ns0="ld:DataServices/CustomerDB/CUSTOMER">
<ns0:CUSTOMER>
<CUSTOMER_ID>CUSTOMER1</CUSTOMER_ID>
<FIRST_NAME>Jack B.</FIRST_NAME>
<LAST_NAME>Quick</LAST_NAME>
<CUSTOMER_SINCE>2001-10-01</CUSTOMER_SINCE>
<EMAIL_ADDRESS>Jack@hotmail.com</EMAIL_ADDRESS>
<TELEPHONE_NUMBER>2145554119</TELEPHONE_NUMBER>
<SSN>295-00-4119</SSN>
<BIRTH_DAY>1970-01-01</BIRTH_DAY>
<DEFAULT_SHIP_METHOD>AIR</DEFAULT_SHIP_METHOD>
<EMAIL_NOTIFICATION>1</EMAIL_NOTIFICATION>
<NEWS_LETTTER>0</NEWS_LETTTER>
<ONLINE_STATEMENT>1</ONLINE_STATEMENT>
<LOGIN_ID>Jack</LOGIN_ID>
</ns0:CUSTOMER>
</ns0:ArrayOfCUSTOMER>
The array type does not appear in the return type displayed in the Liquid Data Administration console. However, interface functions for it are generated and included in client packages. If ArrayOf types are included in the client package for the data type you want to use, they will include the generated root element. An array is indicated in the console with an asterisk appended to the return type of the function. For example, CUSTOMER* indicates a CUSTOMER array.
As otherwise mentioned (see What is Service Data Objects (SDO) Programming? on page 2-1), SDO specifies both static (strongly typed) and dynamic interfaces for data objects:
getCUSTOMERNAME()
. get("CUSTOMER")
. Equivalent typed and dynamic interfaces are provided by the Data Service Mediator API, allowing you to work with data objects in either a dynamic or static model from end-to-end, that is, from data acquisition to client-side manipulation.
Table 2-4 outlines the advantages of each approach.
Note: For more information on the Mediator API, see Accessing Data Services from Java Clients.
The following sections provide more information on the Liquid Data implementation of both kinds of interfaces.
The static interface is a Java interface generated from a data service's XML schema definition, similar to JAXB or XMLBean static interfaces. The interface files, packaged in a JAR, are typically generated by the Liquid Data implementor using WebLogic Workshop.
Note: For information on generating the client JAR file using WebLogic Workshop, see the Data Services Developer's Guide.
There is another way to generate a JAR file. See Setting Up Data Source Aliases for Relational Sources Accessed by Liquid Data, in Advanced Topics.
The generated typed interface contains static accessors for all properties of the XML datatype. If the property is complex (such as CREDIT and ORDER in Figure 2-5), an interface class is generated for the property in the containing package. The interface includes accessors for the properties that make up the complex property.
When developing Data Service Mediator client applications, it is helpful to browse the contents of the generated client package in a development tool (such as Eclipse) to get acquainted with how Liquid Data generates interfaces from data service types. The types of functions that are generated depend on the XML Schema definition for the type. For example, for properties that can have multiple occurrences, as defined in their schema, getPROPERTYArray() functions are generated.
Consider the return type shown in the metadata browser illustrated in Figure 2-5.
Figure 2-5 CUSTOMER Return Type
For each complex property—such as the global CUSTOMER element and properties CREDIT, ORDER, and POITEM—separate interfaces are generated with accessors for their contained properties. For each simple attribute, Liquid Data generates set and get methods. For example, the following are generated in the CUSTOMER interface for the CUSTOMERNAME string attribute:
getCUSTOMERNAME()
setCUSTOMERNAME(String)
For multiple occurrence properties in the return type (indicated by an asterisk in the Return Type tab of the Liquid Data console), functions for getting the array and manipulating array items are generated. In the XML Schema, a property may occur multiple times if it has a maxOccurs attribute set to "unbounded" or greater than 1. Attributes cannot occur multiple times.
For example, the following functions are generated for the CREDIT element:
getCREDITArray()
getCREDITArray(int)
addNewCREDIT()
insertNewCREDIT(int)
removeCREDIT(int)
setCREDITArray(int, CREDIT)
setCREDITArray(CREDIT[])
sizeOfCREDITArray()
Because CREDIT is a complex attribute, a separate CREDIT interface with these functions is generated for it in the customer package:
getCREDITRATING()
getCREDITSCORE()
setCREDITRATING(String)
setCREDITSCORE(int)
Similar methods are generated for the ORDER and its POITEM interfaces.
Note that these additional methods are also generated:
getCUSTOMER()
setCUSTOMER(CUSTOMER)
addNewCUSTOMER()
get()
methods are derived from the underlying XMLBeans technology. They are methods for getting and setting values as XML types. While get
methods are not a part of the SDO specification, they can be used if desired. They are generated for elements or attributes whose type is a simple type. toString()
method, for example, for printing to output. The following table lists the rules for the typed (or static) method generation.
The dynamic interface has generic property accessors (such as set()
and get()
) as well as accessors that get or set data as a specified type, such as String
, Date
, List
, BigInteger
, and BigDecimal
. These accessor methods take the following form:
set(String
propertyName
, Object
propertyValue
)
n
, Object propertyValue
)n
)get(commonj.sdo.Property
propertyName
)
get(int
n
, Object
propertyValue
)get(int
n
)
set
[Type]
(String
propertyName
, Object
propertyValue
)
get
[Type]
(String
propertyName
)
propertyName
)propertyName,
String propertyName
)propertyName,
String propertyName,
String propertyName
)(commonj.sdo.Property
propertyName
)
propertyName
)The generic get methods return an Object type. Also, with the generic set and get methods you can specify an nth property to get or set. Type in the above list indicates the specific data type to be set or retrieved; for example, setBigDecimal
and getBigDecimal
. This includes the accessors provided for getting and setting properties as primitive types, which include, for example, setInt()
, setDate()
, getString()
, and so on. For a full list, see the SDO Update Javadoc available at:
http://download.oracle.com/docs/cd/E13190_01/liquiddata/docs85/sdoUpdateJavadoc/index.html
The propertyName
argument indicates the property whose value you want to get or set, and propertyValue
is the new value. For example, given a data object myCustomer
, the following code sets a value by its property name, LAST_NAME:
myCustomer.set("LAST_NAME", "Nimble");
XPath provides considerable flexibility in how you identify nodes in XML-based information. You can identify properties in SDO accessor arguments by SDO extension of XPath expressions. XPath can find nodes by position, relative position, type, and other criteria.
This section describes common programming tasks involving SDOs. It covers the following:
The first step in using Liquid Data in a client application is to acquire a data object through a typed or untyped interface.
When you instantiate a data object through either interface, in addition to the data object, you get a data graph to which the object is attached and a handle to the root data object in the data graph. By default, change tracking (logging) on the data graph is enabled, which means that any changes performed on the object values are recorded in the change summary, enabling data source updates and rollbacks.
Note: You can populate a new data object by calling a function in the Mediator API or using a Liquid Data Control function. The code samples in this chapter generally use the Mediator API. For more information on using the mediator and Liquid Data Control interfaces, see Accessing Data Services from Java Clients, and Accessing Data Services from Workshop Applications.
To instantiate a data object using a typed data service, import the packages that contain the generated data service and data type interfaces from the <app>
-ld-client.jar
and instantiate the data service object using the getInstance()
method. The data type interfaces are contained in a package that has the following prefix to its package path:
org.openuri.temp.schemas
The following shows a sample of using the typed interface to instantiate a data object:
import dataservices.rtlservices.CustomerView;
import retailer.ArrayOfCUSTOMERDocument;
CustomerView custViewDS = CustomerView.getInstance(context, "RTLApp");
ArrayOfCUSTOMERDocument arrCustDoc =
custViewDS.getCustomerView("CUSTOMER3");
Once you have the data service, you can call its public read method, getCustomerView()
, to get an instance of the root schema element of the data service, ArrayOfCUSTOMERDocument.
A document type, such as ArrayOfCUSTOMERDocument, is a construct for representing a global, top-level element in a data service schema. It lets you access the contents of the entire result returned by a data service function.
To instantiate a data object through a dynamic interface, create a DataService object using the DataServiceFactory class.
The libraries to import from the Liquid Data application client JAR provide the interfaces to the dynamic data services, which can be used as follows:
import com.bea.ld.dsmediator.client.DataServiceFactory;
DataService custDS =
DataServiceFactory.newXmlService(
context,"RTLApp","ld:DataServices/CustomerDB/CUSTOMER");
ArrayOfCUSTOMERDocument myCustomer =
(ArrayOfCUSTOMERDocument) custDS.invoke("CUSTOMER", null);
After obtaining a data object, you can access its properties. To access a data object property (similar to instantiating data objects), you can use a typed or untyped interface functions.
Liquid Data generates typed (or static) accessors based on the XML type returned by a data service function. As an alternative to untyped data access, you can use typed accessor functions. (Typed functions provide greater ease-of-use than untyped functions.)
For example, the following code example shows how to get the LAST_NAME property of a CUSTOMER instance using typed accessors:
ArrayOfCUSTOMER arrCust = myCustomer.getArrayOfCUSTOMER();
CUSTOMER[] customer = arrCust.getCUSTOMERArray();
String lastName = customer[0].getLASTNAME();
You can use the untyped (dynamic) API with types that are unknown or not yet deployed at development time. In the untyped interface, the type names are passed as parameters in the untyped accessor call, and the returned object is cast to the type needed. However, in cases where returned type is unbound, you will need to cast the returned object to a List and use an iterator, if necessary. The following is the untyped implementation of the code sample shown in Typed Property Access above, and gets a single CUSTOMER object:
ArrayOfCUSTOMER arrCust =
(ArrayOfCUSTOMER)myCustomer.get("ArrayOfCUSTOMER");
List customerList = (List) arrCust.get("CUSTOMER[1]");
CUSTOMER customer = (CUSTOMER) customerList.get(0);
String lastName = (String) customer.get("LAST_NAME");
In cases where you are working with an unbounded type (such as ArrayOfCustomer in the above example), and you want to traverse all the objects in the type, you implement an iterator, such as:
List customerList = (List) arrCust.get("CUSTOMER");
Iterator iterator = customerList.iterator();
while ( iterator.hasNext() ){
if (iterator.next instanceof CUSTOMER){
String lastName = (String) customer.get("LAST_NAME");
}
}
Note that the string specified in the get method matches the name of the element as specified in the data service. For example, the typed get method for returning the customer's last name is getLASTNAME()
while the untyped method is get("LAST_NAME")
rather than get("LASTNAME")
.
You can identify properties in SDO accessor arguments by element name, such as LAST_NAME. Accessor functions take property identifiers specified as XPath expressions, as follows:
customer.get("CUSTOMER_PROFILE[1]/ADDRESS[AddressID="ADDR_10_1"]")
The example gets the ADDRESS at the specified path with the specified addressID. If elements have identical identifier values, all elements are returned. For example. the ADDRESS element also has a CustomerID (a customer can have more than one address), so all addresses would be returned. (Note that the get method returns a DataObject, so you will need to cast the returned object to the appropriate type. For unbound objects, you will need use a List.)
SDO augments standard XPath notation in how you specify index positions in a path in order to identify an instance in an array. The following SDO call can be used to retrieve the last name of a customer in an array of customers:
String lastName = (String) arrCust.get("CUSTOMER.0/LAST_NAME")
SDO also supports bracketed-style index notations. The following gets the name of the same department as in the previous example:
String lastName = (String) arrCust.get("CUSTOMER[1]/LAST_NAME")
Notice that the index for the dot-number notation is zero-based, whereas standard XPath notation is one-based. Therefore, both notation examples retrieve the last name of the first customer in an array of properties. Zero-based indexing is more familiar to Java language programmers and allows zero-based counter values in loop constructs to be used in path expressions without having to add 1.
Note: A "query too complex" exception is raised if required JAR files are not in the JVM's CLASSPATH when an XPath path expression is executed. If you encounter this error, make sure that the JAR files xqrl.jar
and wlxbean.jar
are in the CLASSPATH.
You can get a data object's containing parent data object by doing the following:
myCustomer.get("..")
You can get the root containing data object by doing the following:
myCustomer.get("/")
(This is similar to executing myCustomer.getDataGraph().getRootObject()
.)
Note: For more information on XPath in Liquid Data SDO, see XPath Support in the Untyped SDO API.
You can modify data object property values using set()
methods. Like get()
methods, there are both static and dynamic interfaces for setting properties. However, set methods differ from get methods in that they have an additional argument: the new value of the property. For example, to set the last name of a customer using the dynamic API, you would do the following:
CUSTOMER customer = (CUSTOMER) myCustomer.get("CUSTOMER");
customer.set("LAST_NAME", "Smith");
The example sets the LAST_NAME field to a new value "Smith". By comparison, an operation that sets a value for a typed property using the static API would be:
myCustomer.getCUSTOMER().setLASTNAME("Smith");
A very important behavioral property of the SDO model is that the back-end data source associated with a modified object (if there is one) is not changed until a submit()
method is called on the data service bound to the object. Meanwhile, the old value is recorded in a change summary, the change log kept in the data graph that holds the object. For more information on data graphs, see Working with Data Graphs.
You can create new a data object (and have a corresponding changed applied to the data sources associated with its data service) by using an add method and then calling submit()
on the data service bound to the data object. The lineage of the data (the back-end data sources associated with it) is derived from the data service.
A new data object can be added to a root data object or, more commonly, as a new element in a data object array. In addition, whole new arrays can be added to data objects as well.
The following example demonstrates how to add a data object to an array of objects.
CUSTOMERDocument.CUSTOMER newCustomer =
myCustomer.getArrayOfCUSTOMER().addNewCUSTOMER();
int idNo =
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray().length;
newCustomer.setCUSTOMERID("CUSTOMER" + String.valueOf(idNo));
newCustomer.setFIRSTNAME("Clark");
newCustomer.setLASTNAME("Kent");
newCustomer.setCUSTOMERSINCE(java.util.Calendar.getInstance());
newCustomer.setEMAILADDRESS("kent@dailyplanet.com");
newCustomer.setTELEPHONENUMBER("555-555-5555");
newCustomer.setSSN("509-00-3683");
newCustomer.setDEFAULTSHIPMETHOD("Air");
There are few points to note about adding data objects:
submit()
. submit()
call. For more information on primary key computation, see Submitting Data Object Changes on page 2-24.Just as records can be added to a data source by creating new data objects and submitting the changed data graph, you can similarly remove records by deleting data objects from an existing data graph.
A data object is deleted by removing it from the context of its containing object. When you remove an object from a container, the reference to the item is deleted but not the values. (The values are cleaned up later by Java garbage collection.)
To delete a data object, use the delete()
method. For example, the following searches a CUSTOMER array for a customer's name and deletes that customer.
CUSTOMERDocument.CUSTOMER[] customers =
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray();
for (int i=0; i < customers.length; i++){
if (customers[i].getFIRSTNAME().equals("Clark") &&
customers[i].getLASTNAME().equals("Kent") )
{
customers[i].delete();
custDS.submit(myCustomer,"ld:DataServices/CustomerDB/CUSTOMER");
}
}
If the deleted object contains any child elements, they are deleted as well. However, note that only the data object on which a delete call has explicitly been performed is tracked in the change summary as having been deleted.
To submit data changes, call the submit()
method on the data service bound to an object, passing the root changed object and the fully qualified name of the data service bound to the object. Note that the submit()
method is part of the mediator API.
The untyped interface submit()
method has the following signature:
abstract public void submit (DataObject do, String dataservice)
throws java.lang.Exception
The dataservice
argument is the fully qualified name of the data service to which you want the data object to be bound. The function for decomposition for that data service is used to establish the lineage of the data object (the correlation between data object properties and back-end data sources), for example:
custDS.submit(myCustomer,"ld:DataServices/CustomerDB/CUSTOMER");
In this example, the dataservice
argument specifies that the CUSTOMER data service to which the myCustomer
data object is to be bound.
The typed version of the submit()
function only takes the data object as an argument:
custDS.submit(myCustomer);
After submitting the change, if you want to continue using the object in the client application, it is recommended that you rerun the method used to acquire the data object. This ensures that any side effects of the update operation (at the physical data service level) are incorporated in the data object.
Note that if new objects were added that correspond to relational records in back-end data sources, and if the records have auto-generated primary key fields, the fields are generated in the database source and returned to the client in a property array. The properties include name-value items corresponding to the column name and new auto-generated key value.
The following example shows how to modify a data object and submit the change using the typed interface:
CUSTOMER custDS = CUSTOMER.getInstance(ctx, "RTLApp");
ArrayOfCUSTOMERDocument myCustomer =
(ArrayOfCUSTOMERDocument) custDS.invoke("CUSTOMER", null);
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).setLASTNAME("Nimble");
custDS.submit(myCustomer);
Note: For more information, see Enabling SDO Data Source Updates.
The following example shows how to modify a data object and submit the change using the untyped interface:
CUSTOMER custDS = CUSTOMER.getInstance(ctx, "RTLApp");
ArrayOfCUSTOMERDocument myCustomer =
(ArrayOfCUSTOMERDocument) custDS.invoke("CUSTOMER", null);
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).setLASTNAME("Nimble");
custDS.submit(myCustomer,"ld:DataServices/CustomerDB/CUSTOMER");
When using the untyped interface, it is often necessary to check the properties of a data object once it is acquired. The Type interface gives client applications the ability to discover information on a data object at runtime. The information includes the type of the data object and its list of properties with their types.
The getType()
method returns the Type interface for a data object. The Type interface gives the client application access to the data object's properties, both elements and attributes.
The following example shows how to get the type of a data object and print a property's value:
DataObject o = ...;
Type type = o.getType();
System.out.println(o.getString("CUSTOMERNAME")); }
if (type.getName().equals("CUSTOMER") {
Once you have the type of the object, you can get the list of properties defined for the type and access their values using the getProperties()
method. The following example iterates through the property list of a data object and prints out information about each property:
public void printDataObject(DataObject dataObject, int indent) {
Type type = dataObject.getType();
List properties = type.getProperties();
for (int p=0, size=properties.size(); p < size; p++) {
if (dataObject.isSet(p)) {
Property property = (Property) properties.get(p);
// For many-valued properties, process a list of values
if (property.isMany()) {
List values = dataObject.getList(p);
for (int v=0; count=values.size(); v < count; v++) {
printValue(values.get(v), property, indent);
}
else {
// For single-valued properties, print out the value
printValue(dataObject.get(p), property, indent);
}
}
}
}
}
The following table lists other useful methods in the Type interface.
Returns from all the properties of this type, the one with the specified name. (See Table 2-7 for a list of the methods in the Property class.) |
|
Returns whether the specified object is an instance of this type. |
Table 2-7 lists the methods of the Property interface.
Returns the default value this property will have in a data object where the property hasn't been set |
|
Returns the type of the property. (See Table 2-6 for a list of the methods in the Type class.) |
|
Returns whether the property is containment; that is, whether it represents by-value composition. |
|
A data graph is the container for objects passed between the client application and the Liquid Data mediator. The root object of the data graph is typically a data object corresponding to the root type of the data service return type, such as a Customer object.
In addition to data objects, a data graph contains metadata on the data object and a change summary. A change summary is a record of client-side data changes. The mediator uses the change summary to propagate those changes to the back-end data sources.
You get a data graph automatically with the data object when you invoke a data service function. You can also create a data graph and attach a data object to it on the client side or replace the root object returned from a data service function invocation.
Note: For a print-out of a data graph, see Looking at a Data Graph.
There are several useful methods on the data graph interface. You can access the root data object of a data graph using the getRootObject()
method. To add a root object to an empty data graph, use the createRootObject()
method. Note that if the data graph already has a root object, it is overwritten. To get the data graph of an existing data object, use the following form:
CUSTOMERDocument.getDataGraph()
The getChangeSummary()
method allows you to access the data change log. This is particularly useful when creating data update overrides. These are classes for customizing how data updates are propagated to back-end data sources. For more information on update overrides, see Enabling SDO Data Source Updates.
XPath expressions give you a great deal of flexibility in how you locate data objects and attributes in accessors in the untyped interface. For example, you can filter the results of a get()
function invocation based on data elements and values:
company.get("CUSTOMER[1]/POITEMS/ORDER[ORDERID=3546353]")
Note: For more examples of using XPath expressions with SDO, see Accessing Data Object Properties.
The Liquid Data SDO implementation extends support of XPath 1.0 as specified by the SDO language specification. However, there are a few points to keep in mind regarding the Liquid Data implementation:
("CUSTOMER//ArrayOfPOITEM")
In this example, the wildcard matches all purchase order arrays below the CUSTOMER root, which includes either of the following:
CUSTOMER/ORDERS/ArrayOfPOITEM
CUSTOMER/RETURNS/ArrayOfPOITEM
Because this notation introduces type ambiguity (types can be either ORDERS or RETURNS), it is not supported by the Liquid Data SDO implementation.
<ORDER ID="3434">
For example, the following paths refer to the same element, the first ORDER child node under CUSTOMER:
o.get("CUSTOMER/ORDER[1]");
The same expression in SDO's augmented notation:
o.get("CUSTOMER/ORDER.0");
A 0-based index is more convenient for Java programmers who are accustomed to 0-based counters, and who want to use counter values as index values without adding 1. Liquid Data fully supports both the traditional index notation and the augmented notation. However, note that the augmented form of expression are replaced with the traditional form by the SDO preprocessor. This avoids conflicts with elements named with a dot-number, such as <myAcct.12>.
This chapter introduces SDO and covers common operations. For detailed information on SDO, use the following references.
http://dev2dev.bea.com/technologies/commonj/sdo/index.jsp
The SDO specification page contains introductory information on SDO:
http://dev2dev.bea.com/technologies/commonj/sdo/index.jsp
http://xmlbeans.apache.org/
http://www.w3.org/TR/2004/WD-xquery-20040723/