← Prev in month
← Prev in thread
AW: AW: AW: [sdo] ISSUE 119: Projection and Keys
Hi Bryan,
In th current proposal, the implementation would create
one entity for each distinct key value. The entity would have all default
values, except for the key properties, which would of course match the
key.
In earlier versions of the proposal, I defined a second
projection method that took a second argument. I called this argument an
"EntityResolver" and the idea was exactly to allow the client (probably in this
case a DAS) to provide some special logic to manage the reference objects.
In the SAP prototype, for instance, we have an implementation that basically
wraps a JPA EntityManager.
This is certainly something I want to address before we
go final, but I don't think we necessarily have to define it as part of this
issue. It seems to me that the current proposal is clear enough that it
can be accepted as is...we've been unable to make progress on this
proposal for weeks now, and I thought it might go easier if we added things
piece by piece.
One of the things holding me back from making a
proposal with an EntityManager is that even though I think the functionality is
clear and clearly needed, I'm not sure about how the API should look.
Specifically, depending on how ISSUE 134 is resolved, we may get some kind of
"Projector" object, which would be a much better place it parameterize
projection...with an entity manager, or anything else. I'm not very
enthusiastic about having a second "project" method.
It also sort of disturbed me in our implementation that
the application would get back a graph in which the entities at the edges were
attached, but the real SDO content was still detached. It might be better
to allow other hooks into the projection process, eg, one for normal objects, so
that the implementation can call "merge" or something.
It seems you agree we need an EntityResolver, do you
need to see it as part of the resolution of this issue, or do you think we can
solve it seperately?
Ron
Von: Bryan Aupperle [mailto:]
Gesendet: Freitag, 15. August 2008 16:38
An:
Betreff: Re: AW: AW: [sdo] ISSUE 119:
Projection and Keys
Ron,
I must not have been sufficiently clear about
the scenario I am concerned about.
I understand the case where you start with the
context with entities and project to the context with keys. The mapping
between entities and keys can be created and maintained. I agree there is
no problem projecting back tot he context with entities.
Now consider the opposite case. Start with
an object containing keys and the mapping between entities and keys has not yet
been created (perhaps the object was received via a service call).
Now project to a context with entities. Those entities have to
be retrieved from the data store. Or am I missing something here?
Bryan Aupperle, Ph.D.
STSM, WebSphere
Enterprise Platform Software Solution Architect
Research Triangle Park,
NC
+1 919-254-7508 (T/L 444-7508)
Internet Address:
"Barack, Ron"
<>
08/15/2008 09:58 AM
To
Bryan Aupperle/Raleigh/IBM@IBMUS,
<>
cc
Subject
AW: AW: [sdo] ISSUE 119:
Projection and Keys
Hi Bryan,
good to know that someone is reading the
proposals...
I didn't mean located within the backend store, I meant within
the graph reachable from Oe, called Gk in the text.
Projection maintains
identity (one reason why I think it can be unified with binding). That is,
if I have an object O in HelperContext hc1, and I project it to
HelperContext hc2, then
O == hc1.project( hc2.project( O ))
What I
really what to say in the proposal is that the same must hold true for keys.
That is, if the O has a property p, that is a DataObject in hc1, and a key
in hc2, then
o.get(p) == (hc1.project( hc2.project ( O ))) .get(p);
Moreover,
whereever I set the value of a property in the transitive closure reachable from
hc2.project(O) (Gk in the text), to the value of hc2.project(O).get(p), then,
when I project back into hc1, I will always get the same instance, namely
O.get(p).
It's actually easy, it's just hard to say it.
Basically, it means
that when you project from a context with entities to a context with keys, the
user has implicitly defined a map from entities to keys based on the objects in
the projection. This map is not universal... it's based solely on the
graph being transfered.
Did that help or make things worse?
Ron
Von: Bryan Aupperle [mailto:]
Gesendet: Freitag, 15. August 2008 15:25
An:
Betreff: Re: AW: [sdo] ISSUE 119: Projection
and Keys
This
is a tangential question, and perhaps one that I should already know the answer
to, but here goes. In your example with context Ce and Ck, if I have
object Ok and project it into Ce, then for each key in Ok the corresponding
entity has to be located. Since we have agree that entity management is a
DAS responsibility and not an SDO library responsibility, are we laying any new
requirements on DASs, or has the DAS group already agreed that there has to be a
standard API for returning an entity given a key?
Bryan Aupperle, Ph.D.
STSM,
WebSphere Enterprise Platform Software Solution Architect
Research
Triangle Park, NC
+1 919-254-7508 (T/L 444-7508)
Internet Address:
"Barack, Ron"
<>
08/14/2008 07:20 AM
To
<>
cc
Subject
AW: [sdo] ISSUE 119:
Projection and Keys
Hi
Everyone,
I've modified the proposal a bit, based on Frank's suggestion that
projection from keys to entities be made more intelligent. In fact, I
think that the behavior is now intelligent enough so that a second project
method giving a "restoreContext" is no longer required. New text is in
blue.
Comments of course are very welcome.
Ron
Use Cases for Keys and Projection
We begin with
a motivating use case.
Domain models tend to be larger and complex than the views
used by individual clients. Moreover, when going from a large server-side model,
it is necessary to define the scope of the data which should be transmitted to a
remote client. The boundaries of the data which should be transmitted in a
single packet must somehow be defined. On the other hand, the door must be left
open for clients to further explore the model in succeeding calls to the
server.
Imagine a service that returns an Employee object. In
the domain model, Employee has a refence to Department, and Department in turn
has a list of all Employees. If we define the transmission packet to be the
transient closure reachable from Employee, then we can never send a single
employee, we will always send the complete list of employees in the
department.
Our approach is to use keys to represent the boundary
points in a transmission packet. It is assumed that clients that wish to explore
the model beyond these boundaries will be able to use the keys to somehow
perform lookups, but the API through which this is done is out-of-scope for SDO
(being more of a DAS issue).
As an example, we can imagine that the server has a
SDO type system in which Employee has a property with type Department. In the
client's type system, however, the corresponding property has type
{commonj.sdo}string. This value would represent the identity of the Department.
The basic functionality we wish to achive is to allow projection between these
two HelperContexts.
Keys and Projection
A type and its
keyType are compatible with each other. That mean, an object with a
reference to an entity in one context can be projected onto an object with a
reference to the key type in another context.
We illustrate this with an example.
HelperContext #1
Type Employee
*
property - id (Integer) - KEY
* property - name (String)
* property -
direct-report (List of Employee)
is compatible with the metamodel
HelperContext #2
Type Employee
*
property - id (Integer) - KEY
* property - name (String)
* property -
direct-report (List of Integer)
Here we see the type of the direct-report property
has been replaced in the second HelperContext with the corresponding key
type.
The projection from an entity to its key has some
important semantic differences when compared with projection between entities.
Projection between entities creates a new view of the same underlying
data. By contrast, a key does not represent a view, but rather a reference
to an entity in the underlying data model. When working with different
views of the same underlying data, it is natural that changes in one view are
reflected in all other views. By contrast, changing the value of a key
changes the target of the reference rather than the value of the key property in
the referenced data object. When projecting from an entity to a key, a new
instance of the key is always created. By contrast, when we project from
entity to entity, we get the same number of objects in the target HelperContext
that we had in the source HelperContext.
Conversely, when projecting from a key to
an entity then for each distinct key value within the graph being projected, all
references to that key must resolve to the same entity.
In many scenarios data will round-trip
between contexts, including between contexts in which entity map to keys.
Let us consider two context Ce and Ck, representing the entity and the key
context, respectively, and a DataObject Oe, in context Ce. Projecting Oe
into Ck returns a DataObject, Ok. The transitive closure reachable from Ok
is Gk. Every key value in Gk maps to an single entity in Ce, and it is
this entity is that is found when Ok (and effective, all of Gk) is projected
back into Ce. In cases where the user has set a key property to a value
that is not found in Gk, then, as a result of projecting Ok into Ce, a new
entity will be created. The created entity has default values for all
properties other than the key fields.
We illustrate with an example. Note that the
example uses containment relationships in both contexts. This is done for
clarity, since it allows an XML representation of the data. The use-case,
however, is stronger when the contexts have different (or perhaps no)
containment structures. Imagine the following data in HelperContext
#1
<employee id="11">
<name>Foo Bar</name>
<direct-report id="21">
<name>Jane Doe</name>
</direct-report>
<direct-report
id="31">
<name>Jim
Jones</name>
</direct-report>
<direct-report id="22">
<name>John Smith</name>
</direct-report>
</employee>
After projecting to HelperContext #2, we have the
following data
<employee id="11">
<name>Foo
Bar</name
<direct-report>21</direct-report>
<direct-report>31</direct-report>
<direct-report>22</direct-report>
</employee>
If we imagine the client changes the
list of direct reports, so that the second item in the list has value “41”
instead of “31”, then the meaning of the change is not that the employee with
name “Jim Jones” now has a new ID, but that “Jim Jones” has been replaced by
another employee.
On projecting from context 2 back into context 1, the
instance of Employee with id="22" would be detached from the Employee with
id="11". Also a new Employee with id="41" would be created and added as a direct
report to the Employee with id="11". If the graph in context 1
were in scope of a ChangeSummary, then because the "direct-report" property in
context1 is a containment property, the change would be tracked as a delete or a
create. If the relationship were non-containment, the change would be
tracked as a modification to the employee with id=11, not as a delete to the
employee with id=22 or as the creation of a new employee with id=41;
semantically, it is only the reference to entity 22 that has changed, the entity
itself still exists, and is unaltered.
Projecting this data from context 2:
<employee id="11">
<name>Foo Bar</name
<direct-report>21</direct-report>
<direct-report>41</direct-report>
<direct-report>22</direct-report>
</employee>
Into context 1 yields:
<employee
id="11">
<name>Foo Bar</name>
<direct-report id="21">
<name>Jane Doe</name>
</direct-report>
<direct-report
id="41"/>
<direct-report id="22">
<name>John
Smith</name>
</direct-report>
</employee>
Projection between entities and keys
becomes more powerful when the relationship through which the entities tie into
the graph are non-containment. The following example shows how a complex
model that lacks containment relationships can be projected onto a context that
requires a specific XML serialization, which implicitly prunes the orignal
domain to the requirements of a specific client.
HelperContext #1
Type School
*
property - name (String) - KEY
* property - students (Student) -
many=true
* property - courses (Course) - many=true
Type Student
* property - name (String) - KEY
* property - courses
(Course) - many=true, containment=false, opposite=students
* property -
school (School) - containment=false, opposite=students
Type Course
* property - name (String) -
KEY
* property - students (Students) - many=true, containment=false,
opposite=courses
* property - school (School) - containment=false,
opposite=courses
Notice the m:n relationship between
Student and Course. If we imagine a service that should expose this domain
model to clients, it is possible that some clients will wish to obtain the list
of students participating in a particular course, while other clients may wish
to obtain the list of courses in which a particular student is enrolled.
The application cannot determine based on the structure of the data which
of the two possible containment structures is "correct". Notice also that
returning the transitive closure would return all the data associated with the
entire school.
In this example the client wants the data
structured according to the following XSD
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://projection"
xmlns:tns="http://projection"
elementFormDefault="qualified">
<complexType name="School">
<sequence>
<element
name="students" type="tns:Student" maxOccurs="unbounded"/>
</sequence>
<attribute name="name"
type="string"/>
</complexType>
<complexType name="Student">
<sequence>
<element
name="courses" type="string" maxOccurs="unbounded"/>
</sequence>
<attribute name="name"
type="string"/>
</complexType>
<element name="school"
type="tns:School"/>
</schema>
Notice that we are imposing a containment
structure on the original context, as well as pruning it by replacing the course
entity by the corresponding key. The following code illustrates the
behavior of the project method.
DataObject cal =
_helperContext.getDataFactory().create(School.class);
// Create the SDO graph
cal.set("name","Berkeley");
DataObject billy =
cal.createDataObject("students");
billy.set("name", "Billy
Bear");
DataObject bob = cal.createDataObject("students");
bob.set("name", "Bobbie
Bear");
DataObject basketWeaving = cal.createDataObject("courses");
basketWeaving.set("name",
"Basket Weaving");
DataObject algol =
cal.createDataObject("courses");
algol.set("name", "Algol");
DataObject revolution
= cal.createDataObject("courses");
revolution.set("name",
"Revolution");
// hook things up
billy.getList("courses").add(basketWeaving);
billy.getList("courses").add(algol);
bob.getList("courses").add(basketWeaving);
bob.getList("courses").add(revolution);
// Create a second context defined by an
XSD
HelperContext hc2 = HelperProvider.getNewContext();
hc2.getXSDHelper().define(getClass().getClassLoader().getResourceAsStream("com/sap/sdo/testcase/internal/pojo/ex/projection.xsd"),
null);
// Project from the java context to the XSD
context
DataObject projection = hc2.getDataFactory().project(cal);
// Produce
XML based on the XSD
String xml = hc2.getXMLHelper().save(projection, "http://projection",
"school");
// I'm imagining here that sending the XML out over the wire
(eg, using it as a response to a
// WebService request. On the client side,
we go from the XML back to SDO. We use the context
// based on the
XSD.
DataObject projection2 =
hc2.getXMLHelper().load(xml).getRootObject();
// We can make some changes. We can add a
new course...
projection2.getList("students.0/courses").add("Fortran and
You");
//
So, now the trip back to the server…
//I'm skipping the XML step, and simply
projecting the modified (XML oriented) data back into
// my java context
DataObject cal2 =
_helperContext.getDataFactory().project(projection2);
// Test that there is one entity per key
value
DataObject billy2 = (DataObject)cal2.getList("students").get(0);
DataObject basketWeavingBill =
(DataObject)billy2.getList("courses")
.get(0);
DataObject bob2 =
(DataObject)cal2.getList("students").get(1);
DataObject
basketWeavingBob = (DataObject)bob2.getList("courses").get(0);
assertSame(basketWeaving2,
basketWeavingBob);
← Prev in month
← Prev in thread