You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ojb-dev@db.apache.org by ar...@apache.org on 2004/04/24 13:30:34 UTC

cvs commit: db-ojb/forrest/src/documentation/content/xdocs/docu query.xml

arminw      2004/04/24 04:30:34

  Added:       forrest/src/documentation/content/xdocs/docu query.xml
  Log:
  adapted doc
  
  Revision  Changes    Path
  1.1                  db-ojb/forrest/src/documentation/content/xdocs/docu/query.xml
  
  Index: query.xml
  ===================================================================
  <?xml version="1.0" encoding="UTF-8"?>
  <!--
    Copyright 2002-2004 The Apache Software Foundation
  
    Licensed 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.
  -->
  <!-- @version $Id: query.xml,v 1.1 2004/04/24 11:30:34 arminw Exp $ -->
  <!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.2//EN" "document-v12.dtd">
  
  <document>
      <header>
          <title>OJB Queries</title>
          <authors>
              <person name="Jakob Braeuchi" email="jbraeuchi@hotmail.com"/>
          </authors>
      </header>
  
      <body>
          <section>
              <title>Introduction</title>
              <p>
                  This tutorial describes the use of the different queries mechanisms.
                  The sample code shown here is taken mainly from JUnit test classes. The junit test
                  source can be found under
                  <code>[db-ojb]/src/test</code> in the source distribution.
              </p>
          </section>
  
          <section>
              <title>Query by Criteria</title>
              <anchor id="query-by-criteria"/>
              <p>
                  In this section you will learn how to use the query by criteria.
                  The classes are located in the package
                  <code>org.apache.ojb.broker.query</code>.
                  Using query by criteria you can either
                  <link href="#querying-for-objects">query for whole objects</link>
                  (ie. person) or you can use
                  <link href="#report-queries">report queries</link> returning row data.
              </p>
              <p>
                  A query consists mainly of the following parts:
              </p>
              <ol>
                  <li>the class of the objects to be retrieved</li>
                  <li>a list of criteria</li>
                  <li>a DISTINCT flag</li>
                  <li>additional ORDER BY and GROUP BY</li>
              </ol>
              <p>
                  OJB offers a QueryFactory to create a new Query. Although the
                  constructors of the query classes are public using the QueryFactory
                  is the preferred way to create a new query.
              </p>
              <source><![CDATA[
  Query q = QueryFactory.newQuery(Person.class, crit);]]></source>
              <p>
                  To create a DISTINCT-Query, simply add
                  <strong>true</strong> as third parameter.
             </p>
                  <source><![CDATA[
  Query q = QueryFactory.newQuery(Person.class, crit, true);]]></source>
              <p>
                  Each criterion stands for a column in the SQL-WHERE-clause.
              </p>
                  <source><![CDATA[
  Criteria crit = new Criteria();
  crit.addEqualTo("upper(firstname)", "TOM");
  crit.addEqualTo("lastname", "hanks");
  Query q = QueryFactory.newQuery(Person.class, crit);]]></source>
                  <p>
                      This query will generate an SQL statement like this:
                  </p>
                  <source><![CDATA[
  SELECT ... FROM PERSON WHERE upper(FIRSTNAME) = "TOM" AND LASTNAME = "hanks";]]></source>
              <p>
                  OJB supports
                  <strong>functions</strong> in field criteria ie. upper(firstname). When converting a field
                  name to a database column name, the function is added to the generated sql. OJB does not and can not verify the correctness of the specified function, an illegal function will produce an SqlException.
              </p>
  
              <section>
                  <title>Query Criteria</title>
                  <p>
                      OJB provides selection criteria for almost any SQL-comparator.
                      In most cases you do not have to deal directly with the implementing classes
                      like
                      <em>EqualToCriteria</em>.
                      The
                      <em>Criteria</em> class provides factory methods for the appropriate classes.
                      There are four kinds of factory methods:
                      </p>
                      <ul>
                          <li>create criteria to compare a field to a value: ie. addEqualTo("firstname", "tom");</li>
                          <li>create criteria to compare a field to another field: ie. addEqualToField("firstname", "other_field");</li>
                          <li>create criteria to check null value: ie. addIsNull("firstname");</li>
                          <li>create a raw sql criteria: ie: addSql("REVERSE(name) like 're%'");</li>
                      </ul>
                      <p>
                      The following list shows some of the factory methods to compare a field to a value:
                          </p>
                      <ul>
                          <li>addEqualTo</li>
                          <li>addLike</li>
                          <li>addGreaterOrEqualThan</li>
                          <li>addGreaterThan</li>
                          <li>addLike</li>
                          <li>addBetween , this methods has two value parameters</li>
                          <li>addIn , this method uses a Collection as value parameter</li>
                          <li>and of course there negative forms</li>
                      </ul>
                      <p>
                      This list shows some factory methods to compare a field to another field, all those methods end on ...field:
                          </p>
                      <ul>
                          <li>addEqualToField</li>
                          <li>addGreaterThanField</li>
                          <li>and of course there negative forms</li>
                      </ul>
  
                  <section>
                      <title>in / not in</title>
                      <anchor id="in-not-in"/>
                      <p>
                          Some databases limit the number of parameters in an IN-statement.
                          <br/>
                          If the limit is reached OJB will split up the IN-Statement into multiple Statements,
                          the limit is set to 3 for the following sample:
                      </p>
                          <source><![CDATA[
  SELECT ... FROM Artikel A0 WHERE A0.Kategorie_Nr IN ( ? , ? , ? )
  OR A0.Kategorie_Nr IN ( ? , ? ) ORDER BY 7 DESC]]></source>
                          <p>
                          The IN-limit for prefetch can be defined in OJB.properties:
                              </p>
                          <source><![CDATA[
  ...
  # The SqlInLimit entry limits the number of values in IN-sql
  # statement, -1 for no limits. This hint is used in Criteria.
  SqlInLimit=200
  ...]]></source>
                  </section>
  
                  <section>
                      <title>and / or</title>
                      <p>
                          All selection criteria added to a criteria set using the above factory methods
                          will be
                          <strong>AND</strong>ed in the WHERE-clause.
                          To get an
                          <strong>OR</strong> combination two criteria sets are needed.
                          These sets are combined using addOrCriteria:
                      </p>
                          <source><![CDATA[
  Criteria crit1 = new Criteria();
  crit1.addLike("firstname", "%o%");
  crit1.addLike("lastname", "%m%");
  Criteria crit2 = new Criteria();
  crit2.addEqualTo("firstname", "hank");
  
  crit1.addOrCriteria(crit2);
  Query q = QueryFactory.newQuery(Person.class, crit1);
  
  Collection results = broker.getCollectionByQuery(q);]]></source>
                          <p>
                          This query will generate an SQL statement like this:
                          </p>
                          <source><![CDATA[
  SELECT ... WHERE (FIRSTNAME LIKE "%o%") AND LASTNAME
  LIKE "%m%" OR FIRSTNAME = "hank"]]></source>
                  </section>
  
                  <section>
                      <title>negating the criteria</title>
                      <p>
                          A criteria can be negated to obtain
                          <strong>NOT</strong> in the WHERE-clause:
                      </p>
                          <source><![CDATA[
  Criteria crit1 = new Criteria();
  crit1.addLike("firstname", "%o%");
  crit1.addLike("lastname", "%m%");
  crit1.setNegative(true);
  
  Collection results = broker.getCollectionByQuery(q);]]></source>
                          <p>
                          This query will generate an SQL statement like this:
                          </p>
                          <source><![CDATA[
  SELECT ... WHERE NOT (FIRSTNAME LIKE "%o%" AND LASTNAME LIKE "%m%")]]></source>
                  </section>
              </section>
  
              <section>
                  <title>ordering and grouping</title>
                  <p>
                      The following methods of QueryByCriteria are used for ordering and grouping:
                      </p>
                      <ul>
                          <li>addOrderByAscending(String anAttributeName);</li>
                          <li>addOrderByDescending(String anAttributeName);</li>
                          <li>addGroupBy(String anAttributeName); this method is used for
                              <link href="#report-queries">report queries</link>
                          </li>
                      </ul>
                  <p>
                      You can of course have multiple order by and group by clauses,
                      simply repeat the addOrderBy.
                 </p>
                      <source><![CDATA[
  crit = new Criteria();
  query = new QueryByCriteria(Person.class, crit);
  query.addOrderByDescending("id");
  query.addOrderByAscending("lastname");
  broker.getCollectionByQuery(query);]]></source>
                  <p>
                      The code snippet will query all Persons and order them by
                      <strong>attribute</strong> "id"
                      descending and "lastname" ascending.
                      The query will produce the following SQL-statement using column numbers
                      in the ORDER BY clause:
                  </p>
                      <source><![CDATA[
  SELECT A0.ID,A0.FIRSTNAME,A0.LASTNAME FROM
  PERSON A0 ORDER BY 1 DESC, 3]]></source>
                      <p>
                      When you use the
                      <strong>column</strong> name "LASTNAME" instead of the
                      <strong>attribute</strong> name
                      "lastname" (query.addOrderBy("LASTNAME");), an additional column named "LASTNAME" without alias will be
                      added.
                      </p>
                      <source><![CDATA[
  SELECT A0.ID,A0.FIRSTNAME,A0.LASTNAME,LASTNAME FROM
  PERSON A0 ORDER BY 1 DESC,4]]></source>
                  <p>
                      If there are multiple tables with a column "LASTNAME" the SQL-Statement will produce an error,
                      so it's better to always use attribute names.
                  </p>
              </section>
  
              <section>
                  <title>subqueries</title>
                  <p>
                      Subqueries can be used instead of values in selection criteria.
                      The subquery should thus be a ReportQuery.
                      <br/>
                      The following example queries all articles having a price greator or equal than the
                      average price of articles named 'A%':
                  </p>
                      <source><![CDATA[
  ReportQueryByCriteria subQuery;
  Criteria subCrit = new Criteria();
  Criteria crit = new Criteria();
  
  subCrit.addLike("articleName", "A%");
  subQuery = QueryFactory.newReportQuery(Article.class, subCrit);
  subQuery.setAttributes(new String[] { "avg(price)" });
  
  crit.addGreaterOrEqualThan("price", subQuery);
  Query q = QueryFactory.newQuery(Article.class, crit);
  
  Collection results = broker.getCollectionByQuery(q);]]></source>
                  <p>
                      It's also possible to build a subquery with attributes referencing the enclosing query.
                      These attributes have to use a special prefix
                      <strong>Criteria.PARENT_QUERY_PREFIX</strong>.
                      <br/>
                      The following example queries all product groups having more than 10 articles:
                      </p>
                      <source><![CDATA[
  ReportQueryByCriteria subQuery;
  Criteria subCrit = new Criteria();
  Criteria crit = new Criteria();
  
  subCrit.addEqualToField("productGroupId", Criteria.PARENT_QUERY_PREFIX + "groupId");
  subQuery = QueryFactory.newReportQuery(Article.class, subCrit);
  subQuery.setAttributes(new String[] { "count(productGroupId)" });
  
  crit.addGreaterThan(subQuery, "10"); // MORE than 10 articles
  crit.addLessThan("groupId", new Integer(987654));
  Query q = QueryFactory.newQuery(ProductGroup.class, crit);
  
  Collection results = broker.getCollectionByQuery(q);]]></source>
              </section>
  
              <section>
                  <title>joins</title>
                  <p>
                      Joins resulting from
                      <strong>path expressions</strong> ("relationship.attribute") in criteria are automatically handled by OJB.
                      Path expressions are supported for all relationships 1:1, 1:n and m:n (decomposed and non-decomposed)
                      and can be nested.
                  </p>
                      <p>
                      The following sample looks for all articles belonging to the product group "Liquors".
                      Article and product group are linked by the relationship "productGroup" in class Article:
                  </p>
                      <source><![CDATA[
  <!-- Definitions for org.apache.ojb.ojb.broker.Article -->
     <class-descriptor
           class="org.apache.ojb.broker.Article"
           proxy="dynamic"
           table="Artikel"
     >
        ...
        <reference-descriptor
           name="productGroup"
           class-ref="org.apache.ojb.broker.ProductGroup"
        >
           <foreignkey field-ref="productGroupId"/>
        </reference-descriptor>
     </class-descriptor>
  
     <class-descriptor
           class="org.apache.ojb.broker.ProductGroup"
           proxy="org.apache.ojb.broker.ProductGroupProxy"
           table="Kategorien"
     >
        ...
        <field-descriptor
           name="groupName"
           column="KategorieName"
           jdbc-type="VARCHAR"
        />
        ...
     </class-descriptor>]]></source>
                      <p>
                      The path expression includes the 1:1 relationship "productGroup" and the attribute "groupName":
                      </p>
                      <source><![CDATA[
  Criteria crit = new Criteria();
  crit.addEqualTo("productGroup.groupName", "Liquors");
  Query q = QueryFactory.newQuery(Article.class, crit);
  
  Collection results = broker.getCollectionByQuery(q);]]></source>
                  <p>
                      If path expressions refer to a class having
                      <strong>extents</strong>, the tables of the extent classes participate in the JOIN and the criteria is ORed. The shown sample queries all ProductGroups having an Article named 'F%'.
                      The path expression 'allArticlesInGroup' refers to the class Articles which has two extents: Books and CDs.
                  </p>
                      <source><![CDATA[
  Criteria crit = new Criteria();
  crit.addLike("allArticlesInGroup.articleName", "F%");
  QueryByCriteria q = QueryFactory.newQuery(ProductGroup.class, crit, true);
  
  Collection results = broker.getCollectionByQuery(q);
  ]]></source>
  
                  <p>
                      This sample produces the following SQL:
                  </p>
                  <source><![CDATA[
  SELECT DISTINCT A0.KategorieName,A0.Kategorie_Nr,A0.Beschreibung
  FROM Kategorien A0
  INNER JOIN Artikel A1 ON A0.Kategorie_Nr=A1.Kategorie_Nr
  LEFT OUTER JOIN BOOKS A1E0 ON A0.Kategorie_Nr=A1E0.Kategorie_Nr
  LEFT OUTER JOIN CDS A1E1 ON A0.Kategorie_Nr=A1E1.Kategorie_Nr
  WHERE A1.Artikelname LIKE  'F%'  OR
  A1E0.Artikelname LIKE  'F%'  OR
  A1E1.Artikelname LIKE  'F%']]></source>
                  <p>
                  OJB tries to do it's best to automatically use
                  <strong>outer</strong> joins where needed.
                  This is currently the case for classes having extents and ORed criteria.
                  But you can force the SQLGenerator to use outer joins where you find it useful.
                  <br/>
                  This is done by the method
                  <em>QueryByCriteria#setPathOuterJoin(String)</em>.
                  </p>
                  <source><![CDATA[
  ReportQueryByCriteria query;
  Criteria crit;
  Iterator result1, result2;
  
  crit = new Criteria();
  
  query = new ReportQueryByCriteria(Person.class, crit);
  query.setAttributes(new String[] { "id", "name", "vorname", "sum(konti.saldo)" });
  query.addGroupBy(new String[]{ "id", "name", "vorname" });
  
  result1 = broker.getReportQueryIteratorByQuery(query);
  
  query.setPathOuterJoin("konti");
  result2 = broker.getReportQueryIteratorByQuery(query);]]></source>
                  <p>
                  The first query will use an inner join for relationship "konti", the second an outer join.
                  </p>
              </section>
  
  
              <section>
                  <title>user defined alias</title>
                  <p>
                      This feature allows to have
                      <strong>multiple</strong> aliases for the same table. The standard behaviour of OJB is to build one alias for one relationship.
                      </p>
                      <p>
                      Suppose you have two classes Issue and Keyword and there is a 1:N relationship
                      between them. Now you want to retrieve Issues by querying on Keywords.
                      Suppose you want to retrieve all Issues with keywords 'JOIN' and 'ALIAS'. If these values
                      are stored in the attribute 'value' of Keyword, OJB generates a query that contains
                      " A1.value = 'JOIN' AND A1.value = 'ALIAS' " in the where-clause. Obviously,
                      this will not work, no hits will occur because A1.value can not have
                      more then 1 value at the time !
                  </p>
  
                  <p>
                      For the examples below, suppose you have the following classes (pseudo-code):
                      </p>
                      <source><![CDATA[
  class Container
      int id
      Collection allAbstractAttributes
  
  class AbstractAttribute
      int id
      inf ref_id
      String name
      String value
      Collection allAbstractAttributes]]></source>
                      <p>
                      OJB maps these classes to separate tables where it maps
                      allAbstractAttributes using a collectiondescriptor to AbstractAttribute
                      using ref_id as inverse foreignkey on Container for the collection
                      descriptor.
                      <br/>
                      For demo purposes : AbstractAttribute also has a collection of abstract attributes.
                      </p>
                      <source><![CDATA[
  Criteria crit1 = new Criteria();
  crit1.setAlias("company");			// set an alias
  crit1.addEqualTo("allAbstractAttributes.name", "companyName");
  crit1.addEqualTo("allAbstractAttributes.value", "iBanx");
  
  Criteria crit2 = new Criteria();
  crit2.setAlias("contact");			// set an alias
  crit2.addEqualTo("allAbstractAttributes.name", "contactPerson");
  crit2.addLike("allAbstractAttributes.value", "janssen");
  
  Criteria crit3 = new Criteria();
  crit3.addEqualTo("allAbstractAttributes.name", "size");
  crit3.addGreaterThan("allAbstractAttributes.value", new Integer(500));
  
  crit1.addAndCriteria(crit2);
  crit1.addAndCriteria(crit3);
  
  q = QueryFactory.newQuery(Container.class, crit1);
  q.addOrderBy("company.value");	    // user alias]]></source>
                      <p>
                      The generated query will be as follows. Note that the alias name 'company' does not show up in the SQL.
                      </p>
                      <source><![CDATA[
  SELECT DISTINCT A0.ID, A1.VALUE
  FROM CONTAINER A0 INNER JOIN ABSTRACT_ATTRIBUTE A1
       ON A0.ID=A1.REF_ID
       INNER JOIN ABSTRACT_ATTRIBUTE A2
       ON A0.ID=A2.REF_ID
       INNER JOIN ABSTRACT_ATTRIBUTE A3
       ON A0.ID=A3.REF_ID
  WHERE (( A0.NAME =  'companyName' ) AND  (A0.VALUE =  'iBanx' )) AND
        (( A1.NAME =  'contactPerson' ) AND  (A1.VALUE LIKE '%janssen%' )) AND
        (( A2.NAME =  'size' ) AND  (A2.VALUE =  '500' ))
  ORDER BY 2]]></source>
                      <p>
                      The next example uses a report query.
                      </p>
  
                      <source><![CDATA[
  Criteria crit1 = new Criteria();
  crit1.setAlias("ALIAS1");
  crit1.addEqualTo("allAbstractAttributes.allAbstractAttributes.name", "xxxx");
  crit1.addEqualTo("allAbstractAttributes.allAbstractAttributes.value", "hello");
  
  Criteria crit2 = new Criteria();
  crit2.setAlias("ALIAS2");
  crit2.addEqualTo("allAbstractAttributes.name", "yyyy");
  crit2.addLike("allAbstractAttributes.value", "");
  
  crit1.addAndCriteria(crit2);
  
  q = QueryFactory.newReportQuery(Container.class, crit1);
  
  String[] cols = { id, "ALIAS2.name", "ALIAS2.name", "ALIAS1.name", "ALIAS1.name" };
  q.setAttributes(cls);]]></source>
                      <p>
                      The generated query will be:
                      </p>
                      <source><![CDATA[
  SELECT DISTINCT A0.ID, A1.NAME, A1.VALUE, A2.NAME, A2.VALUE
  FROM CONTAINER A0 INNER JOIN ABSTRACT_ATTRIBUTE A1
       ON A0.ID=A1.REF_ID
       INNER JOIN ABSTRACT_ATTRIBUTE A2
       ON A1.ID=A2.REF_ID
  WHERE (( A2.NAME =  'xxxx' ) AND  (A2.VALUE =  'hello' )) AND
        (( A1.NAME =  'yyyy' ) AND  (A2.VALUE LIKE '%%' )) AND
  ORDER BY 2]]></source>
                  <note>
                      When you define an alias for a criteria,
                      you have to make sure that
                      <em>all</em> attributes used in this criteria belong to the
                      <em>same</em> class.
                      If you break this rule OJB will probably use a wrong ClassDescriptor to resolve your attributes !
                  </note>
              </section>
  
              <section>
                  <title>prefetched relationships</title>
                  <p>
                      This feature can help to minimize the number of queries when reading objects with relationships.
                      In our Testcases we have ProductGroups with a one to many relationship to Articles.
                      When reading the ProductGroups one query is executed to get the ProductGroups and for
                      <strong>each</strong> ProductGroup
                      another query is executed to retrieve the Articles.
                      </p>
                      <p>
                      With prefetched relationships OJB tries to read all Articles belonging to the ProductGroups in
                      <strong>one</strong> query.
                      See further down why one query is not always possible.
                      </p>
                      <source><![CDATA[
  Criteria crit = new Criteria();
  crit.addLessOrEqualThan("groupId", new Integer(5));
  
  QueryByCriteria q = QueryFactory.newQuery(ProductGroup.class, crit);
  q.addOrderByDescending("groupId");
  q.addPrefetchedRelationship("allArticlesInGroup");
  
  Collection results = broker.getCollectionByQuery(q);]]></source>
                      <p>
                      The first query reads all matching ProductGroups:
                          </p>
                      <source><![CDATA[
  SELECT ... FROM Kategorien A0 WHERE
  A0.Kategorie_Nr <= ? ORDER BY 3 DESC]]></source>
                  <p>
                      The second query retrieves Articles belonging to the ProductGroups read by the first query:
                      </p>
                      <source><![CDATA[
  SELECT ... FROM Artikel A0 WHERE A0.Kategorie_Nr
  IN ( ? , ? , ? , ? , ? ) ORDER BY 7 DESC]]></source>
                      <p>
                      After reading all Articles they are associated with their ProductGroup.
                      </p>
                      <note>
                          This function is not yet supported for relationships using Arrays.
                      </note>
                      <p>
                      Some databases limit the number of parameters in an IN-statement.
                      If the limit is reached OJB will split up the second query into multiple queries,
                      the limit is set to 3 for the following sample:
                      </p>
                      <source><![CDATA[
  SELECT ... FROM Artikel A0 WHERE A0.Kategorie_Nr
  IN ( ? , ? , ? ) ORDER BY 7 DESC
  SELECT ... FROM Artikel A0 WHERE A0.Kategorie_Nr
  IN ( ? , ? ) ORDER BY 7 DESC]]></source>
                      <p>
                      The IN-limit for prefetch can be defined in OJB.properties
                      <link href="#in-not-in">SqlInLimit</link>.
                  </p>
              </section>
  
  
              <section>
                  <title>querying for objects</title>
                  <anchor id="querying-for-objects"/>
                  <p>
                  OJB queries return
                  <strong>complete</strong> objects, that means all instance variables are filled
                  and all 'auto-retrieve' relationships are loaded. Currently there's no way to retrieve partially
                      loaded objects (ie. only first- and lastname of a person).
                  </p>
                  <p>
                      More info about <link href="site:metadata">manipulation of metadata setting here</link>.
                  </p>
              </section>
  
  
              <section>
                  <title>report queries</title>
                  <anchor id="report-queries"/>
                  <p>
                  Report queries are used to retrieve row data, not 'real' business objects. A row is an array of Object.
                  With these queries you can define what attributes of an object you want to have in the row.
                  The attribute names may also contain path expressions like 'owner.address.street'.
                  To define the attributes use ReportQuery
                  <strong>#setAttributes(String[] attributes)</strong>.
                  </p>
                  <p>
                  The following ReportQuery summarizes the number of articles in stock and their price for each ProductGroup:
                      </p>
                  <source><![CDATA[
  Criteria crit = new Criteria();
  Collection results = new Vector();
  
  ReportQueryByCriteria q = QueryFactory.newReportQuery(
                              ProductGroup.class, crit);
  // define the 'columns' of the report
  q.setAttributes(new String[] { "groupName",
                              "sum(allArticlesInGroup.stock)",
                              "sum(allArticlesInGroup.price)" });
  q.addGroupBy("groupName");
  
  Iterator iter = broker.getReportQueryIteratorByQuery(q);]]></source>
                  <p>
                  The ReportQuery returns an Iterator over a Collection of
                  Object[3] ([String, Integer, Double]).
                  </p>
              </section>
          </section>
  
          <section>
              <title>ODMG OQL</title>
          </section>
  
  
          <section>
              <title>JDO queries</title>
          </section>
      </body>
  </document>
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: ojb-dev-unsubscribe@db.apache.org
For additional commands, e-mail: ojb-dev-help@db.apache.org