You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2018/01/05 14:21:33 UTC
[05/11] cayenne git commit: CAY-2371 Switch documentation from
Docbook to Asciidoctor format
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7783cd34/docs/docbook/cayenne-guide/src/docbkx/lifecycle-events.xml
----------------------------------------------------------------------
diff --git a/docs/docbook/cayenne-guide/src/docbkx/lifecycle-events.xml b/docs/docbook/cayenne-guide/src/docbkx/lifecycle-events.xml
deleted file mode 100644
index 364b5da..0000000
--- a/docs/docbook/cayenne-guide/src/docbkx/lifecycle-events.xml
+++ /dev/null
@@ -1,333 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to you under the Apache License, Version
- 2.0 (the "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0 Unless required by
- applicable law or agreed to in writing, software distributed under the
- License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- CONDITIONS OF ANY KIND, either express or implied. See the License for
- the specific language governing permissions and limitations under the
- License.
--->
-<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink"
- version="5.0" xml:id="lifecycle-events">
- <title>Lifecycle Events</title>
- <para>An application might be interested in getting notified when a Persistent object moves
- through its lifecycle (i.e. fetched from DB, created, modified, committed). E.g. when a new
- object is created, the application may want to initialize its default properties (this can't
- be done in constructor, as constructor is also called when an object is fetched from DB).
- Before save, the application may perform validation and/or set some properties (e.g.
- "updatedTimestamp"). After save it may want to create an audit record for each saved object,
- etc., etc. </para>
- <para>All this can be achieved by declaring callback methods either in Persistent objects or in
- non-persistent listener classes defined by the application (further simply called
- "listeners"). There are eight types of lifecycle events supported by Cayenne, listed later
- in this chapter. When any such event occurs (e.g. an object is committed), Cayenne would
- invoke all appropriate callbacks. Persistent objects would receive their own events, while
- listeners would receive events from any objects. </para>
- <para>Cayenne allows to build rather powerful and complex "workflows" or "processors" tied to
- objects lifecycle, especially with listeners, as they have full access to the application
- evnironment outside Cayenne. This power comes from such features as filtering which entity
- events are sent to a given listener and the ability to create a common operation context for
- multiple callback invocations. All of these are discussed later in this chapter.</para>
- <section xml:id="types-of-lifecycle-events">
- <title>Types of Lifecycle Events</title>
- <para>Cayenne defines the following 8 types of lifecycle events for which callbacks can be
- regsitered:<table frame="void">
- <caption>Lifecycle Event Types</caption>
- <col width="16%"/>
- <col width="84%"/>
- <thead>
- <tr>
- <th>Event</th>
- <th>Occurs...</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>PostAdd</td>
- <td>right after a new object is created inside
- <code>ObjectContext.newObject()</code>. When this event is fired the
- object is already registered with its ObjectContext and has its ObjectId
- and ObjectContext properties set.</td>
- </tr>
- <tr>
- <td>PrePersist</td>
- <td>right before a new object is committed, inside
- <code>ObjectContext.commitChanges()</code> and
- <code>ObjectContext.commitChangesToParent()</code> (and prior to
- "<code>validateForInsert()</code>").</td>
- </tr>
- <tr>
- <td>PreUpdate</td>
- <td>right before a modified object is committed, inside
- <code>ObjectContext.commitChanges()</code> and
- <code>ObjectContext.commitChangesToParent()</code> (and prior to
- "<code>validateForUpdate()</code>").</td>
- </tr>
- <tr>
- <td>PreRemove</td>
- <td>right before an object is deleted, inside
- <code>ObjectContext.deleteObjects()</code>. The event is also
- generated for each object indirectly deleted as a result of CASCADE
- delete rule.</td>
- </tr>
- <tr>
- <td>PostPersist</td>
- <td>right after a commit of a new object is done, inside
- <code>ObjectContext.commitChanges()</code>.</td>
- </tr>
- <tr>
- <td>PostUpdate</td>
- <td>right after a commit of a modified object is done, inside
- <code>ObjectContext.commitChanges()</code>.</td>
- </tr>
- <tr>
- <td>PostRemove</td>
- <td>right after a commit of a deleted object is done, inside
- <code>ObjectContext.commitChanges()</code>.</td>
- </tr>
- <tr>
- <td>PostLoad</td>
- <td>
- <itemizedlist>
- <listitem>
- <para>After an object is fetched inside
- <code>ObjectContext.performQuery()</code>.</para>
- </listitem>
- <listitem>
- <para>After an object is reverted inside
- <code>ObjectContext.rollbackChanges()</code>.</para>
- </listitem>
- <listitem>
- <para>Anytime a faulted object is resolved (i.e. if a
- relationship is fetched).</para>
- </listitem>
- </itemizedlist>
- </td>
- </tr>
- </tbody>
- </table></para>
- </section>
- <section xml:id="callback-persistent">
- <title>Callbacks on Persistent Objects</title>
- <para>Callback methods on Persistent classes are mapped in CayenneModeler for each
- ObjEntity. Empty callback methods are automatically created as a part of class
- generation (either with Maven, Ant or the Modeler) and are later filled with appropriate
- logic by the programmer. E.g. assuming we mapped a 'post-add' callback called
- 'onNewOrder' in ObjEntity 'Order', the following code will be
- generated:<programlisting language="java">public abstract class _Order extends CayenneDataObject {
- protected abstract void onNewOrder();
-}
-
-public class Order extends _Order {
-
- @Override
- protected void onNewOrder() {
- //TODO: implement onNewOrder
- }
-}</programlisting></para>
- <para>As <code>onNewOrder()</code> is already declared in the mapping, it does not need to
- be registered explicitly. Implementing the method in subclass to do something meaningful
- is all that is required at this point. </para>
- <para>As a rule callback methods do not have any knowledge of the outside application, and
- can only access the state of the object itself and possibly the state of other
- persistent objects via object's own ObjectContext.</para>
- <para>
- <note>
- <para><emphasis role="italic">Validation and callbacks:</emphasis> There is a clear
- overlap in functionality between object callbacks and
- <code>DataObject.validateForX()</code> methods. In the future validation may
- be completely superceeded by callbacks. It is a good idea to use "validateForX"
- strictly for validation (or not use it at all). Updating the state before commit
- should be done via callbacks.</para>
- </note>
- </para>
- </section>
- <section xml:id="callback-non-persistent">
- <title>Callbacks on Non-Persistent Listeners</title>
-
- <para>
- <note>
- <para>While listener callback methods can be declared in the Modeler (at least
- as of this wrting), which ensures their automatic registration in runtime,
- there's a big downside to it. The power of the listeners lies in their
- complete separation from the XML mapping. The mapping once created, can be
- reused in different contexts each having a different set of listeners.
- Placing a Java class of the listener in the XML mapping, and relying on
- Cayenne to instantiate the listeners severly limits mapping reusability.
- Further down in this chapter we'll assume that the listener classes are
- never present in the DataMap and are registered via API.</para>
- </note>
- </para>
- <para>A listener is simply some application class that has one or more annotated
- callback methods. A callback method signature should be <code>void
- someMethod(SomePersistentType object)</code>. It can be public, private, protected
- or use default access:</para>
- <para>
- <programlisting language="java"> public class OrderListener {
-
- @PostAdd(Order.class)
- public void setDefaultsForNewOrder(Order o) {
- o.setCreatedOn(new Date());
- }
-}</programlisting>
- </para>
- <para>Notice that the example above contains an annotation on the callback method that
- defines the type of the event this method should be called for. Before we go into
- annotation details, we'll show how to create and register a listener with Cayenne. It is
- always a user responsibility to register desired application listeners, usually right
- after ServerRuntime is started. Here is an example:</para>
- <para>First let's define 2 simple
- listeners.<programlisting language="java">public class Listener1 {
-
- @PostAdd(MyEntity.class)
- void postAdd(Persistent object) {
- // do something
- }
-}
-
-public class Listener2 {
-
- @PostRemove({ MyEntity1.class, MyEntity2.class })
- void postRemove(Persistent object) {
- // do something
- }
-
- @PostUpdate({ MyEntity1.class, MyEntity2.class })
- void postUpdate(Persistent object) {
- // do something
- }
-}</programlisting></para>
- <para>Ignore the annotations for a minute. The important point here is that the listeners
- are arbitrary classes unmapped and unknown to Cayenne, that contain some callback
- methods. Now let's register them with
- runtime:<programlisting language="java">ServerRuntime runtime = ...
-
-LifecycleCallbackRegistry registry =
- runtime.getDataDomain().getEntityResolver().getCallbackRegistry();
-
-registry.addListener(new Listener1());
-registry.addListener(new Listener2());</programlisting></para>
- <para>Listeners in this example are very simple. However they don't have to be. Unlike
- Persistent objects, normally listeners initialization is managed by the application
- code, not Cayenne, so listeners may have knowledge of various application services,
- operation transactional context, etc. Besides a single listener can apply to multiple
- entities. As a consequence their callbacks can do more than just access a single
- ObjectContext. </para>
- <para>Now let's discuss the annotations. There are eight annotations exactly matching the
- names of eight lifecycle events. A callback method in a listener should be annotated
- with at least one, but possibly with more than one of them. Annotation itself defines
- what event the callback should react to. Annotation parameters are essentially an entity
- filter, defining a subset of ObjEntities whose events we are interested
- in:<programlisting language="java">// this callback will be invoked on PostRemove event of any object
-// belonging to MyEntity1, MyEntity2 or their subclasses
-@PostRemove({ MyEntity1.class, MyEntity2.class })
-void postRemove(Persistent object) {
- ...
-}</programlisting><programlisting language="java">// similar example with multipe annotations on a single method
-// each matching just one entity
-@PostPersist(MyEntity1.class)
-@PostRemove(MyEntity1.class)
-@PostUpdate(MyEntity1.class)
-void postCommit(MyEntity1 object) {
- ...
-}</programlisting></para>
- <para>As shown above, "value" (the implicit annotation parameter) can contain one or more
- entity classes. Only these entities' events will result in callback invocation. There's
- also another way to match entities - via custom annotations. This allows to match any
- number of entities without even knowing what they are. Here is an example. We'll first
- define a custom
- annotation:<programlisting language="java">@Target(ElementType.TYPE)
-@Retention(RetentionPolicy.RUNTIME)
-public @interface Tag {
-
-}</programlisting></para>
- <para>Now we can define a listener that will react to events from ObjEntities annotated with
- this
- annotation:<programlisting language="java">public class Listener3 {
-
- @PostAdd(entityAnnotations = Tag.class)
- void postAdd(Persistent object) {
- // do something
- }
-}</programlisting></para>
- <para>As you see we don't have any entities yet, still we can define a listener that does
- something useful. Now let's annotate some
- entities:<programlisting language="java">@Tag
-public class MyEntity1 extends _MyEntity1 {
-
-}
-
-@Tag
-public class MyEntity2 extends _MyEntity2 {
-
-}</programlisting></para>
- </section>
-
- <section xml:id="comining-listeners-with-datachannelfilters">
- <title>Combining Listeners with DataChannelFilters</title>
- <para>A final touch in the listeners design is preserving the state of the listener within a
- single select or commit, so that events generated by multiple objects can be collected
- and processed all together. To do that you will need to implement a
- <code>DataChannelFilter</code>, and add some callback methods to it. They will store
- their state in a ThreadLocal variable of the filter. Here is an example filter that does
- something pretty meaningless - counts how many total objects were committed. However it
- demonstrates the important pattern of aggregating multiple events and presenting a
- combined
- result:<programlisting language="java">public class CommittedObjectCounter implements DataChannelFilter {
-
- private ThreadLocal<int[]> counter;
-
- @Override
- public void init(DataChannel channel) {
- counter = new ThreadLocal<int[]>();
- }
-
- @Override
- public QueryResponse onQuery(ObjectContext originatingContext, Query query, DataChannelFilterChain filterChain) {
- return filterChain.onQuery(originatingContext, query);
- }
-
- @Override
- public GraphDiff onSync(ObjectContext originatingContext, GraphDiff changes, int syncType,
- DataChannelFilterChain filterChain) {
-
- // init the counter for the current commit
- counter.set(new int[1]);
-
- try {
- return filterChain.onSync(originatingContext, changes, syncType);
- } finally {
-
- // process aggregated result and release the counter
- System.out.println("Committed " + counter.get()[0] + " object(s)");
- counter.set(null);
- }
- }
-
- @PostPersist(entityAnnotations = Tag.class)
- @PostUpdate(entityAnnotations = Tag.class)
- @PostRemove(entityAnnotations = Tag.class)
- void afterCommit(Persistent object) {
- counter.get()[0]++;
- }
-}</programlisting></para>
- <para>Now since this is both a filter and a listener, it needs to be registered as
- such:<programlisting language="java">CommittedObjectCounter counter = new CommittedObjectCounter();
-
-ServerRuntime runtime = ...
-DataDomain domain = runtime.getDataDomain();
-
-// register filter
-domain.addFilter(counter);
-
-// register listener
-domain.getEntityResolver().getCallbackRegistry().addListener(counter);</programlisting></para>
- </section>
-</chapter>
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7783cd34/docs/docbook/cayenne-guide/src/docbkx/orderings.xml
----------------------------------------------------------------------
diff --git a/docs/docbook/cayenne-guide/src/docbkx/orderings.xml b/docs/docbook/cayenne-guide/src/docbkx/orderings.xml
deleted file mode 100644
index f1b9c0e..0000000
--- a/docs/docbook/cayenne-guide/src/docbkx/orderings.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to you under the Apache License, Version
- 2.0 (the "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0 Unless required by
- applicable law or agreed to in writing, software distributed under the
- License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- CONDITIONS OF ANY KIND, either express or implied. See the License for
- the specific language governing permissions and limitations under the
- License.
--->
-<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink"
- version="5.0" xml:id="orderings">
- <title>Orderings</title>
- <para>An Ordering object defines how a list of objects should be ordered. Orderings are
- essentially path expressions combined with a sorting strategy. Creating an Ordering:
- <programlisting language="java">Ordering o = new Ordering(Painting.NAME_PROPERTY, SortOrder.ASENDING);</programlisting></para>
- <para>Like expressions, orderings are translated into SQL as parts of queries (and the sorting
- occurs in the database). Also like expressions, orderings can be used in memory, naturally -
- to sort
- objects:<programlisting language="java">Ordering o = new Ordering(Painting.NAME_PROPERTY, SortOrder.ASCENDING_INSENSITIVE);
-List<Painting> list = ...
-o.orderList(list);</programlisting>Note
- that unlike filtering with Expressions, ordering is performed in-place. This list object is
- reordered and no new list is created.</para>
-</chapter>
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7783cd34/docs/docbook/cayenne-guide/src/docbkx/part1.xml
----------------------------------------------------------------------
diff --git a/docs/docbook/cayenne-guide/src/docbkx/part1.xml b/docs/docbook/cayenne-guide/src/docbkx/part1.xml
deleted file mode 100644
index ac79499..0000000
--- a/docs/docbook/cayenne-guide/src/docbkx/part1.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to you under the Apache License, Version
- 2.0 (the "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0 Unless required by
- applicable law or agreed to in writing, software distributed under the
- License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- CONDITIONS OF ANY KIND, either express or implied. See the License for
- the specific language governing permissions and limitations under the
- License.
--->
-<part xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0"
- xml:id="cayenne-guide-part1" xmlns:xi="http://www.w3.org/2001/XInclude">
- <title>Object Relational Mapping with Cayenne</title>
- <xi:include href="setup.xml"/>
- <xi:include href="cayenne-mapping-structure.xml"/>
- <xi:include href="cayennemodeler-application.xml"/>
-</part>
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7783cd34/docs/docbook/cayenne-guide/src/docbkx/part2.xml
----------------------------------------------------------------------
diff --git a/docs/docbook/cayenne-guide/src/docbkx/part2.xml b/docs/docbook/cayenne-guide/src/docbkx/part2.xml
deleted file mode 100644
index c5f8201..0000000
--- a/docs/docbook/cayenne-guide/src/docbkx/part2.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to you under the Apache License, Version
- 2.0 (the "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0 Unless required by
- applicable law or agreed to in writing, software distributed under the
- License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- CONDITIONS OF ANY KIND, either express or implied. See the License for
- the specific language governing permissions and limitations under the
- License.
--->
-<part xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0"
- xml:id="cayenne-guide-part2" xmlns:xi="http://www.w3.org/2001/XInclude">
- <title>Cayenne Framework</title>
- <xi:include href="including-cayenne-in-project.xml"/>
- <xi:include href="starting-cayenne.xml"/>
- <xi:include href="persistent-objects-objectcontext.xml"/>
- <xi:include href="expressions.xml"/>
- <xi:include href="orderings.xml"/>
- <xi:include href="queries.xml"/>
- <xi:include href="lifecycle-events.xml"/>
- <xi:include href="performance-tuning.xml"/>
- <xi:include href="customizing-cayenne-runtime.xml"/>
-</part>
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7783cd34/docs/docbook/cayenne-guide/src/docbkx/part3.xml
----------------------------------------------------------------------
diff --git a/docs/docbook/cayenne-guide/src/docbkx/part3.xml b/docs/docbook/cayenne-guide/src/docbkx/part3.xml
deleted file mode 100644
index 75c60b3..0000000
--- a/docs/docbook/cayenne-guide/src/docbkx/part3.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to you under the Apache License, Version
- 2.0 (the "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0 Unless required by
- applicable law or agreed to in writing, software distributed under the
- License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- CONDITIONS OF ANY KIND, either express or implied. See the License for
- the specific language governing permissions and limitations under the
- License.
--->
-<part xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0"
- xml:id="cayenne-guide-part3" xmlns:xi="http://www.w3.org/2001/XInclude">
- <title>Cayenne Framework - Remote Object Persistence </title>
- <xi:include href="rop-introduction.xml"/>
- <xi:include href="rop-setup.xml"/>
- <xi:include href="implementing-rop-server.xml"/>
- <xi:include href="implementing-rop-client.xml"/>
- <xi:include href="rop-deployment.xml"/>
- <xi:include href="current-limitations.xml"/>
-</part>
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7783cd34/docs/docbook/cayenne-guide/src/docbkx/performance-tuning.xml
----------------------------------------------------------------------
diff --git a/docs/docbook/cayenne-guide/src/docbkx/performance-tuning.xml b/docs/docbook/cayenne-guide/src/docbkx/performance-tuning.xml
deleted file mode 100644
index 18bb0bc..0000000
--- a/docs/docbook/cayenne-guide/src/docbkx/performance-tuning.xml
+++ /dev/null
@@ -1,289 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to you under the Apache License, Version
- 2.0 (the "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0 Unless required by
- applicable law or agreed to in writing, software distributed under the
- License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- CONDITIONS OF ANY KIND, either express or implied. See the License for
- the specific language governing permissions and limitations under the
- License.
--->
-<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink"
- version="5.0" xml:id="performance-tuning">
- <title>Performance Tuning</title>
- <section xml:id="prefetching">
- <title>Prefetching</title>
- <para>Prefetching is a technique that allows to bring back in one query not only the queried
- objects, but also objects related to them. In other words it is a controlled eager
- relationship resolving mechanism. Prefetching is discussed in the "Performance Tuning"
- chapter, as it is a powerful performance optimization method. However another common
- application of prefetching is to refresh stale object relationships, so more generally
- it can be viewed as a technique for managing subsets of the object graph.</para>
- <para>Prefetching example:
- <programlisting language="java">SelectQuery query = new SelectQuery(Artist.class);
-
-// this instructs Cayenne to prefetch one of Artist's relationships
-query.addPrefetch("paintings");
-
-// query is expecuted as usual, but the resulting Artists will have
-// their paintings "inflated"
-List<Artist> artists = context.performQuery(query);</programlisting>All
- types of relationships can be preftetched - to-one, to-many, flattened. </para>
- <para>A prefetch can span multiple relationships:
- <programlisting language="java"> query.addPrefetch("paintings.gallery");</programlisting></para>
- <para>A query can have multiple
- prefetches:<programlisting language="java">query.addPrefetch("paintings");
-query.addPrefetch("paintings.gallery"); </programlisting></para>
- <para>If a query is fetching DataRows, all "disjoint" prefetches are ignored, only "joint"
- prefetches are executed (see prefetching semantics discussion below for what disjoint and
- joint prefetches mean).</para>
-
- <section xml:id="prefetching-semantics">
- <title>Prefetching Semantics</title>
- <para>Prefetching semantics defines a strategy to prefetch relationships. Depending on
- it, Cayenne would generate different types of queries. The end result is the same -
- query root objects with related objects fully resolved. However semantics can affect
- preformance, in some cases significantly. There are 3 types of prefetch semantics,
- all defined as constants in
- org.apache.cayenne.query.PrefetchTreeNode:<programlisting language="java">PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS
-PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS
-PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS</programlisting></para>
- <para>Each query has a default prefetch semantics, so generally users do not have to
- worry about changing it, except when performance is a concern, or a few special
- cases when a default sematics can't produce the correct result. SelectQuery uses
- DISJOINT_PREFETCH_SEMANTICS by default. Semantics can be changed as
- follows:<programlisting language="java">SelectQuery query = new SelectQuery(Artist.class);
-query.addPrefetch("paintings").setSemantics(
- PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS); </programlisting></para>
- <para>There's no limitation on mixing different types of semantics in the same
- SelectQuery. Multiple prefetches each can have its own semantics. </para>
- <para>SQLTemplate and ProcedureQuery are both using JOINT_PREFETCH_SEMANTICS and it can
- not be changed due to the nature of these two queries.</para>
- </section>
- <section xml:id="disjoint-prefetch-semantics">
- <title>Disjoint Prefetching Semantics</title>
- <para>This semantics (only applicable to SelectQuery) results in Cayenne generatiing one
- SQL statement for the main objects, and a separate statement for each prefetch path
- (hence "disjoint" - related objects are not fetched with the main query). Each
- additional SQL statement uses a qualifier of the main query plus a set of joins
- traversing the preftech path between the main and related entity. </para>
- <para>This strategy has an advantage of efficient JVM memory use, and faster overall
- result processing by Cayenne, but it requires (1+N) SQL statements to be executed,
- where N is the number of prefetched relationships.</para>
-
- </section>
- <section xml:id="disjoint-by-id-prefetch-semantics">
- <title>Disjoint-by-ID Prefetching Semantics</title>
- <para>This is a variation of disjoint prefetch where related objects are matched against
- a set of IDs derived from the fetched main objects (or intermediate objects in a
- multi-step prefetch). Cayenne limits the size of the generated WHERE clause, as most
- DBs can't parse arbitrary large SQL. So prefetch queries are broken into smaller
- queries. The size of is controlled by the DI property
- Constants.SERVER_MAX_ID_QUALIFIER_SIZE_PROPERTY (the default number of conditions in
- the generated WHERE clause is 10000). Cayenne will generate (1 + N * M) SQL
- statements for each query using disjoint-by-ID prefetches, where N is the number of
- relationships to prefetch, and M is the number of queries for a given prefetch that
- is dependent on the number of objects in the result (ideally M = 1).</para>
- <para>The advantage of this type of prefetch is that matching database rows by ID may be
- much faster than matching the qualifier of the original query. Moreover this is
- <emphasis role="bold">the only type of prefetch</emphasis> that can handle
- SelectQueries with <emphasis role="bold">fetch limit</emphasis>. Both joint and
- regular disjoint prefetches may produce invalid results or generate inefficient
- fetch-the-entire table SQL when fetch limit is in effect. </para>
- <para>The disadvantage is that query SQL can get unwieldy for large result sets, as each
- object will have to have its own condition in the WHERE clause of the generated
- SQL.</para>
- </section>
- <section xml:id="joint-prefetch-semantics">
- <title>Joint Prefetching Semantics</title>
- <para>Joint semantics results in a single SQL statement for root objects and any number
- of jointly prefetched paths. Cayenne processes in memory a cartesian product of the
- entities involved, converting it to an object tree. It uses OUTER joins to connect
- prefetched entities.</para>
- <para>Joint is the most efficient prefetch type of the three as far as generated SQL
- goes. There's always just 1 SQL query generated. Its downsides are the potentially
- increased amount of data that needs to get across the network between the
- application server and the database, and more data processing that needs to be done
- on the Cayenne side.</para>
- </section>
- </section>
- <section xml:id="datarows">
- <title>Data Rows</title>
- <para>Converting result set data to Persistent objects and registering these objects in the
- ObjectContext can be an expensive operation compareable to the time spent running the
- query (and frequently exceeding it). Internally Cayenne builds the result as a list of
- DataRows, that are later converted to objects. Skipping the last step and using data in
- the form of DataRows can significantly increase performance. </para>
- <para>DataRow is a simply a map of values keyed by their DB column name. It is a ubiqutous
- representation of DB data used internally by Cayenne. And it can be quite usable as is
- in the application in many cases. So performance sensitive selects should consider
- DataRows - it saves memory and CPU cycles. All selecting queries support DataRows
- option,
- e.g.:<programlisting language="java">SelectQuery query = new SelectQuery(Artist.class);
-query.setFetchingDataRows(true);
-
-List<DataRow> rows = context.performQuery(query); </programlisting><programlisting language="java">SQLTemplate query = new SQLTemplate(Artist.class, "SELECT * FROM ARTIST");
-query.setFetchingDataRows(true);
-
-List<DataRow> rows = context.performQuery(query);</programlisting></para>
- <para>Moreover DataRows may be converted to Persistent objects later as needed. So e.g. you
- may implement some in-memory filtering, only converting a subset of fetched
- objects:<programlisting language="java">// you need to cast ObjectContext to DataContext to get access to 'objectFromDataRow'
-DataContext dataContext = (DataContext) context;
-
-for(DataRow row : rows) {
- if(row.get("DATE_OF_BIRTH") != null) {
- Artist artist = dataContext.objectFromDataRow(Artist.class, row);
- // do something with Artist...
- ...
- }
-}</programlisting></para>
- </section>
- <section xml:id="iterated-queries">
- <title>Iterated Queries</title>
- <para>While contemporary hardware may easily allow applications to fetch hundreds of
- thousands or even millions of objects into memory, it doesn't mean this is always a good
- idea to do so. You can optimize processing of very large result sets with two techniques
- discussed in this and the following chapter - iterated and paginated queries. </para>
- <para>Iterated query is not actually a special query. Any selecting query can be executed in
- iterated mode by the DataContext (like in the previous example, a cast to DataContext is
- needed). DataContext returns an object called <code>ResultIterator</code> that is backed
- by an open ResultSet. Data is read from ResultIterator one row at a time until it is
- exhausted. Data comes as a DataRows regardless of whether the orginating query was
- configured to fetch DataRows or not. A ResultIterator must be explicitly closed to avoid
- JDBC resource leak.</para>
- <para>Iterated query provides constant memory performance for arbitrarily large ResultSets.
- This is true at least on the Cayenne end, as JDBC driver may still decide to bring the
- entire ResultSet into the JVM memory. </para>
- <para>Here is a full
- example:<programlisting language="java">// you need to cast ObjectContext to DataContext to get access to 'performIteratedQuery'
-DataContext dataContext = (DataContext) context;
-
-// create a regular query
-SelectQuery q = new SelectQuery(Artist.class);
-
-// ResultIterator operations all throw checked CayenneException
-// moreover 'finally' is required to close it
-try {
-
- ResultIterator it = dataContext.performIteratedQuery(q);
-
- try {
- while(it.hasNextRow()) {
- // normally we'd read a row, process its data, and throw it away
- // this gives us constant memory performance
- Map row = (Map) it.nextRow();
-
- // do something with the row...
- ...
- }
- }
- finally {
- it.close();
- }
-}
-catch(CayenneException e) {
- e.printStackTrace();
-}
-</programlisting>Also
- common sense tells us that ResultIterators should be processed and closed as soon as
- possible to release the DB connection. E.g. storing open iterators between HTTP requests
- and for unpredictable length of time would quickly exhaust the connection pool.</para>
- </section>
- <section xml:id="paginated-queries">
- <title>Paginated Queries</title>
- <para>Enabling query pagination allows to load very large result sets in a Java app with
- very little memory overhead (much smaller than even the DataRows option discussed
- above). Moreover it is completely transparent to the application - a user gets what
- appears to be a list of Persistent objects - there's no iterator to close or DataRows to
- convert to objects:</para>
- <para>
- <programlisting language="java">SelectQuery query = new SelectQuery(Artist.class);
-query.setPageSize(50);
-
-// the fact that result is paginated is transparent
-List<Artist> artists = ctxt.performQuery(query);</programlisting>
- </para>
- <para>Having said that, DataRows option can be combined with pagination, providing the best
- of both
- worlds:<programlisting language="java">SelectQuery query = new SelectQuery(Artist.class);
-query.setPageSize(50);
-query.setFetchingDataRows(true);
-
-List<DataRow> rows = ctxt.performQuery(query);</programlisting></para>
- <para>The way pagination works internally, it first fetches a list of IDs for the root
- entity of the query. This is very fast and initially takes very little memory. Then when
- an object is requested at an arbitrary index in the list, this object and adjacent
- objects (a "page" of objects that is determined by the query pageSize parameter) are
- fetched together by ID. Subsequent requests to the objects of this "page" are served
- from memory.</para>
- <para>An obvious limitation of pagination is that if you eventually access all objects in
- the list, the memory use will end up being the same as with no pagination. However it is
- still a very useful approach. With some lists (e.g. multi-page search results) only a
- few top objects are normally accessed. At the same time pagination allows to estimate
- the full list size without fetching all the objects. And again - it is completely
- transparent and looks like a normal query.</para>
- </section>
- <section xml:id="caching-and-fresh-data">
- <title>Caching and Fresh Data</title>
- <section xml:id="object-caching">
- <title>Object Caching</title>
- </section>
- <section xml:id="query-result-caching">
- <title>Query Result Caching</title>
- </section>
- </section>
- <section xml:id="turning-off-synchronization-of-objectcontexts">
- <title>Turning off Synchronization of ObjectContexts</title>
- <para>By default when a single ObjectContext commits its changes, all other contexts in the
- same runtime receive an event that contains all the committed changes. This allows them
- to update their cached object state to match the latest committed data. There are
- however many problems with this ostensibly helpful feature. In short - it works well in
- environments with few contexts and in unclustered scenarios, such as single user desktop
- applications, or simple webapps with only a few users. More specifically:<itemizedlist>
- <listitem>
- <para>The performance of synchronization is (probably worse than) O(N) where N
- is the number of peer ObjectContexts in the system. In a typical webapp N
- can be quite large. Besides for any given context, due to locking on
- synchronization, context own performance will depend not only on the queries
- that it runs, but also on external events that it does not control. This is
- unacceptable in most situations. </para>
- </listitem>
- <listitem>
- <para>Commit events are untargeted - even contexts that do not hold a given
- updated object will receive the full event that they will have to
- process.</para>
- </listitem>
- <listitem>
- <para>Clustering between JVMs doesn't scale - apps with large volumes of commits
- will quickly saturate the network with events, while most of those will be
- thrown away on the receiving end as mentioned above.</para>
- </listitem>
- <listitem>
- <para>Some contexts may not want to be refreshed. A refresh in the middle of an
- operation may lead to unpredictable results. </para>
- </listitem>
- <listitem>
- <para>Synchronization will interfere with optimistic locking. </para>
- </listitem>
- </itemizedlist>So we've made a good case for disabling synchronization in most webapps.
- To do that, set to "false" the following DI property -
- <code>Constants.SERVER_CONTEXTS_SYNC_PROPERTY</code>, using one of the standard
- Cayenne DI approaches. E.g. from command
- line:<programlisting language="java">java -Dcayenne.server.contexts_sync_strategy=false</programlisting>Or
- by changing the standard properties Map in a custom extensions
- module:<programlisting language="java">public class MyModule implements Module {
-
- @Override
- public void configure(Binder binder) {
- binder.bindMap(Constants.PROPERTIES_MAP).put(Constants.SERVER_CONTEXTS_SYNC_PROPERTY, "false");
- }
-}</programlisting></para>
- </section>
-</chapter>
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7783cd34/docs/docbook/cayenne-guide/src/docbkx/persistent-objects-objectcontext.xml
----------------------------------------------------------------------
diff --git a/docs/docbook/cayenne-guide/src/docbkx/persistent-objects-objectcontext.xml b/docs/docbook/cayenne-guide/src/docbkx/persistent-objects-objectcontext.xml
deleted file mode 100644
index 471cca1..0000000
--- a/docs/docbook/cayenne-guide/src/docbkx/persistent-objects-objectcontext.xml
+++ /dev/null
@@ -1,300 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to you under the Apache License, Version
- 2.0 (the "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0 Unless required by
- applicable law or agreed to in writing, software distributed under the
- License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- CONDITIONS OF ANY KIND, either express or implied. See the License for
- the specific language governing permissions and limitations under the
- License.
--->
-<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink"
- version="5.0" xml:id="persistent-objects-objectcontext">
- <title>Persistent Objects and ObjectContext</title>
- <section xml:id="objectcontext">
- <title>ObjectContext</title>
- <para>ObjectContext is an interface that users normally work with to access the database. It
- provides the API to execute database operations and to manage persistent objects. A
- context is obtained from the
- ServerRuntime:<programlisting language="java">ObjectContext context = runtime.getContext();</programlisting></para>
- <para>The call above creates a new instance of ObjectContext that can access the database via this
- runtime. ObjectContext is a single "work area" in Cayenne, storing persistent objects.
- ObjectContext guarantees that for each database row with a unique ID it will contain at
- most one instance of an object, thus ensuring object graph consistency between multiple
- selects (a feature called "uniquing"). At the same time different ObjectContexts will
- have independent copies of objects for each unique database row. This allows users to
- isolate object changes from one another by using separate ObjectContexts.</para>
- <para>These properties directly affect the strategies for scoping and sharing (or not
- sharing) ObjectContexts. Contexts that are only used to fetch objects from the database
- and whose objects are never modified by the application can be shared between mutliple
- users (and multiple threads). Contexts that store modified objects should be accessed
- only by a single user (e.g. a web application user might reuse a context instance
- between multiple web requests in the same HttpSession, thus carrying uncommitted changes
- to objects from request to request, until he decides to commit or rollback them). Even
- for a single user it might make sense to use mutliple ObjectContexts (e.g.
- request-scoped contexts to allow concurrent requests from the browser that change and
- commit objects independently).</para>
- <para>ObjectContext is serializable and does not permanently hold to any of the application
- resources. So it does not have to be closed. If the context is not used anymore, it
- should simply be allowed to go out of scope and get garbage collected, just like any
- other Java object.</para>
- </section>
- <section xml:id="persistent-lifecycle">
- <title>Persistent Object and its Lifecycle</title>
- <para>Cayenne can persist Java objects that implement <code>org.apache.cayenne.Persistent</code>
- interface. Generally persistent classes are generated from the model as described above,
- so users do not have to worry about superclass and property implementation details. </para>
- <para>Persistent interface provides access to 3 persistence-related properties - objectId,
- persistenceState and objectContext. All 3 are initialized by Cayenne runtime framework.
- Application code should not attempt to change them them. However it is allowed to read
- them, which provides valuable runtime information. E.g. ObjectId can be used for quick
- equality check of 2 objects, knowing persistence state would allow highlighting changed
- objects, etc.</para>
- <para>Each persistent object belongs to a single ObjectContext, and can be in one of the following
- persistence states (as defined in <code>org.apache.cayenne.PersistenceState</code>)
- :<table frame="void">
- <caption>Persistence States</caption>
- <col width="16%"/>
- <col width="84%"/>
- <tbody>
- <tr>
- <td>TRANSIENT</td>
- <td>The object is not registered with an ObjectContext and will not be
- persisted.</td>
- </tr>
- <tr>
- <td>NEW</td>
- <td>The object is freshly registered in an ObjectContext, but has not been
- saved to the database yet and there is no matching database row.</td>
- </tr>
- <tr>
- <td>COMMITTED</td>
- <td>The object is registered in an ObjectContext, there is a row in the
- database corresponding to this object, and the object state corresponds
- to the last known state of the matching database row.</td>
- </tr>
- <tr>
- <td>MODIFIED</td>
- <td>The object is registered in an ObjectContext, there is a row in the
- database corresponding to this object, but the object in-memory state
- has diverged from the last known state of the matching database
- row.</td>
- </tr>
- <tr>
- <td>HOLLOW</td>
- <td>The object is registered in an ObjectContext, there is a row in the
- database corresponding to this object, but the object state is unknown.
- Whenever an application tries to access a property of such object,
- Cayenne attempts reading its values from the database and "inflate" the
- object, turning it to COMMITED.</td>
- </tr>
- <tr>
- <td>DELETED</td>
- <td>The object is registered in an ObjectContext and has been marked for
- deletion in-memory. The corresponding row in the database will get
- deleted upon ObjectContext commit, and the object state will be turned
- into TRANSIENT.</td>
- </tr>
- </tbody>
- </table></para>
- </section>
- <section xml:id="persistent-operations">
- <title>ObjectContext Persistence API</title>
- <para>One of the first things users usually want to do with an ObjectContext is to select
- some objects from a database. This is done by calling "<emphasis role="italic"
- >performQuery</emphasis>"
- method:<programlisting language="java">SelectQuery query = new SelectQuery(Artist.class);
-List<Artist> artists = context.performQuery(query);</programlisting>We'll
- discuss queries in some detail in the following chapters. The example above is
- self-explanatory - we create a SelectQuery that matches all Artist objects present in
- the database, and then call "performQuery", getting a list of Artist objects.</para>
- <para>Some queries can be quite complex, returning multiple result sets or even updating the
- database. For such queries ObjectContext provides "<emphasis role="italic"
- >performGenericQuery</emphasis>"method. While not nearly as commonly-used as
- "performQuery", it is nevertheless important in some situations.
- E.g.:<programlisting language="java">Collection<Query> queries = ... // multiple queries that need to be run together
-QueryChain query = new QueryChain(queries);
-
-QueryResponse response = context.performGenericQuery(query);</programlisting></para>
- <para>An application might modify selected objects. E.g.:</para>
- <programlisting language="java">Artist selectedArtist = artists.get(0);
-selectedArtist.setName("Dali");</programlisting>
- <para>The first time the object property is changed, the object's state is automatically set
- to "MODIFIED" by Cayenne. Cayenne tracks all in-memory changes until a user calls
- "<emphasis role="italic"
- >commitChanges</emphasis>":<programlisting language="java">context.commitChanges();</programlisting>At
- this point all in-memory changes are analyzed and a minimal set of SQL statements is
- issued in a single transaction to synchronize the database with the in-memory state. In
- our example "commitChanges" commits just one object, but generally it can be any number
- of objects. </para>
- <para>If instead of commit, we wanted to reset all changed objects to the previously
- committed state, we'd call <emphasis>rollbackChanges</emphasis>
- instead:<programlisting language="java">context.rollbackChanges();</programlisting></para>
- <para>"<emphasis role="italic">newObject</emphasis>" method call creates a persistent object
- and sets its state to
- "NEW":<programlisting language="java">Artist newArtist = context.newObject(Artist.class);
-newArtist.setName("Picasso");</programlisting></para>
- <para>It will only exist in memory until "commitChanges" is issued. On commit Cayenne might
- generate a new primary key (unless a user set it explicitly, or a PK was inferred from a
- relationship) and issue an INSERT SQL statement to permanently store the object.</para>
- <para><emphasis>deleteObjects</emphasis> method takes one or more Persistent objects and
- marks them as
- "DELETED":<programlisting language="java">context.deleteObjects(artist1);
-context.deleteObjects(artist2, artist3, artist4);</programlisting>Additionally
- "deleteObjects" processes all delete rules modeled for the affected objects. This may
- result in implicitly deleting or modifying extra related objects. Same as insert and
- update, delete operations are sent to the database only when "commitChanges" is called.
- Similarly "rollbackChanges" will undo the effect of "newObject" and
- "deleteObjects".</para>
- <para><emphasis>localObject</emphasis> returns a copy of a given persistent object that is
- "local" to a given ObjectContext:</para>
- <para>Since an application often works with more than one context, "localObject" is a rather
- common operation. E.g. to improve performance a user might utilize a single shared
- context to select and cache data, and then occasionally transfer some selected objects
- to another context to modify and commit
- them:<programlisting language="java">ObjectContext editingContext = runtime.getContext();
-Artist localArtist = editingContext.localObject(artist);</programlisting></para>
- <para>Often an appliction needs to inspect mapping metadata. This information is stored in
- the EntityResolver object, accessible via the
- ObjectContext:<programlisting language="java">EntityResolver resolver = objectContext.getEntityResolver();</programlisting></para>
- <para>Here we discussed the most commonly used subset of the ObjectContext API. There are
- other useful methods, e.g. those allowing to inspect registered objects state en bulk,
- etc. Check the latest JavaDocs for details.</para>
- </section>
- <section xml:id="cayenne-helper-class">
- <title>Cayenne Helper Class</title>
- <para>There is a useful helper class called "Cayenne" (fully-qualified name
- <code>"org.apache.cayenne.Cayenne"</code>) that builds on ObjectContext API to
- provide a number of very common operations. E.g. get a primary key (most entities do not
- model PK as an object property)
- :<programlisting language="java">long pk = Cayenne.longPKForObject(artist);</programlisting></para>
- <para>It also provides the reverse operation - finding an object given a known
- PK:<programlisting language="java">Artist artist = Cayenne.objectForPK(context, Artist.class, 34579);</programlisting></para>
- <para>If a query is expected to return 0 or 1 object, Cayenne helper class can be used to find
- this object. It throws an exception if more than one object matched the
- query:<programlisting language="java">Artist artist = (Artist) Cayenne.objectForQuery(context, new SelectQuery(Artist.class));</programlisting></para>
- <para>Feel free to explore Cayenne class API for other useful methods.</para>
- </section>
- <section xml:id="objectcontext-nesting">
- <title>ObjectContext Nesting</title>
- <para>In all the examples shown so far an ObjectContext would directly connect to a database
- to select data or synchronize its state (either via commit or rollback). However another
- context can be used in all these scenarios instead of a database. This concept is called
- ObjectContext "nesting". Nesting is a parent/child relationship between two contexts,
- where child is a nested context and selects or commits its objects via a parent. </para>
- <para>Nesting is useful to create isolated object editing areas (child contexts) that need
- to all be committed to an intermediate in-memory store (parent context), or rolled back
- without affecting changes already recorded in the parent. Think cascading GUI dialogs,
- or parallel AJAX requests coming to the same session.</para>
- <para>In theory Cayenne supports any number of nesting levels, however applications should
- generally stay with one or two, as deep hierarchies will most certainly degrade the
- performance of the deeply nested child contexts. This is due to the fact that each
- context in a nesting chain has to update its own objects during most operations. </para>
- <para>Cayenne ROP is an extreme case of nesting when a child context is located in a
- separate JVM and communicates with its parent via a web service. ROP is discussed in
- details in the following chapters. Here we concentrate on the same-VM nesting.</para>
- <para>To create a nested context, use an instance of ServerRuntime, passing it the desired
- parent:<programlisting language="java">ObjectContext parent = runtime.getContext();
-ObjectContext nested = runtime.getContext((DataChannel) parent);</programlisting>From
- here a nested context operates just like a regular context (you can perform queries,
- create and delete objects, etc.). The only difference is that commit and rollback
- operations can either be limited to synchronization with the parent, or cascade all the
- way to the
- database:<programlisting language="java">// merges nested context changes into the parent context
-nested.commitChangesToParent();
-
-// regular 'commitChanges' cascades commit through the chain
-// of parent contexts all the way to the database
-nested.commitChanges();</programlisting><programlisting language="java">// unrolls all local changes, getting context in a state identical to parent
-nested.rollbackChangesLocally();
-
-// regular 'rollbackChanges' cascades rollback through the chain of contexts
-// all the way to the topmost parent
-nested.rollbackChanges();</programlisting></para>
- </section>
- <section xml:id="generic-persistent-objects">
- <title>Generic Persistent Objects</title>
- <para>As described in the CayenneModeler chapter, Cayenne supports mapping of completely
- generic classes to specific entities. Although for conveniece most applications should
- stick with entity-specific class mappings, the generic feature offers some interesting
- possibilities, such as creating mappings completely on the fly in a running application,
- etc.</para>
- <para>Generic objects are first class citizens in Cayenne, and all common persistent
- operations apply to them as well. There are some pecularities however, described
- below.</para>
- <para>When creating a new generic object, either cast your ObjectContext to DataContext
- (that provides "newObject(String)" API), or provide your object with an explicit
- ObjectId:<programlisting language="java">DataObject generic = ((DataContext) context).newObject("GenericEntity");</programlisting><programlisting language="java">DataObject generic = new CayenneDataObject();
-generic.setObjectId(new ObjectId("GenericEntity"));
-context.registerNewObject(generic);</programlisting>SelectQuery
- for generic object should be created passing entity name String in constructor, instead
- of a Java
- class:<programlisting language="java">SelectQuery query = new SelectQuery("GenericEntity");</programlisting>Use
- DataObject API to access and modify properties of a generic
- object:<programlisting language="java">String name = (String) generic.readProperty("name");
-generic.writeProperty("name", "New Name");</programlisting>This
- is how an application can obtain entity name of a generic
- object:<programlisting language="java">String entityName = generic.getObjectId().getEntityName();</programlisting></para>
- </section>
- <section xml:id="transactions">
- <title>Transactions</title>
- <para>Considering how much attention is given to managing transactions in most other ORMs,
- transactions have been conspicuously absent from the ObjectContext discussion till now.
- The reason is that transactions are seamless in Cayenne in all but a few special cases.
- ObjectContext is an in-memory container of objects that is disconnected from the
- database, except when it needs to run an operation. So it does not care about any
- surrounding transaction scope. Sure enough all database operations are transactional, so
- when an application does a commit, all SQL execution is wrapped in a database
- transaction. But this is done behind the scenes and is rarely a concern to the
- application code.</para>
- <para>Two cases where transactions need to be taken into consideration are container-managed
- and application-managed transactions. </para>
- <para>If you are using an EJB container (or some other JTA environment), you'll likely need
- to switch Cayenne runtime into "external transactions mode". This is either done in the
- Modeler (check DataDomain > 'Container-Managed Transactions' checkbox), or in the
- code:<programlisting language="java">runtime.getDataDomain().setUsingExternalTransactions(true);</programlisting>In
- this case Cayenne assumes that JDBC Connections obtained by runtime whenever that might
- happen are all coming from a transactional DataSource managed by the container. In this
- case Cayenne does not attempt to commit or rollback the connections, leaving it up to
- the container to do that when appropriate.</para>
- <para>In the second scenario, an application might need to define its own transaction scope
- that spans more than one Cayenne operation. E.g. two sequential commits that need to be
- rolled back together in case of failure. This can be done with an explicit thread-bound
- transaction that surrounds a set of operations. Application is responsible for
- committing or rolling it
- back:<programlisting language="java">Transaction tx = runtime.getDataDomain().createTransaction();
-Transaction.bindThreadTransaction(tx);
-
-try {
- // commit one or more contexts
- context1.commitChanges();
- context2.commitChanges();
- ....
- // after changing some objects in context1, commit again
- context1.commitChnages();
- ....
- // if no failures, commit
- tx.commit();
-}
-catch (Exception ex) {
- tx.setRollbackOnly();
-}
-finally {
- Transaction.bindThreadTransaction(null);
-
- if (tx.getStatus() == Transaction.STATUS_MARKED_ROLLEDBACK) {
- try {
- tx.rollback();
- }
- catch (Exception rollbackEx) {
- }
- }
-} </programlisting></para>
- </section>
-</chapter>