mirror of https://github.com/apache/openjpa.git
339 lines
14 KiB
XML
339 lines
14 KiB
XML
|
|
||
|
<chapter id="jpa_overview_sqlquery">
|
||
|
<title>SQL Queries</title>
|
||
|
<indexterm zone="jpa_overview_sqlquery">
|
||
|
<primary>SQL queries</primary>
|
||
|
<seealso>Query</seealso>
|
||
|
</indexterm>
|
||
|
<indexterm>
|
||
|
<primary>Query</primary>
|
||
|
<secondary>SQL</secondary>
|
||
|
<see>SQL queries</see>
|
||
|
</indexterm>
|
||
|
<indexterm>
|
||
|
<primary>SQL</primary>
|
||
|
<secondary>queries</secondary>
|
||
|
<see>SQL queries</see>
|
||
|
</indexterm>
|
||
|
<indexterm>
|
||
|
<primary>Native</primary>
|
||
|
<secondary>queries</secondary>
|
||
|
<see>SQL queries</see>
|
||
|
</indexterm>
|
||
|
<para>
|
||
|
JPQL is a powerful query language, but there are times when it is
|
||
|
not enough. Maybe you're migrating a JDBC application to JPA
|
||
|
on a strict deadline, and you don't have time to translate your existing
|
||
|
SQL selects to JPQL. Or maybe a certain query requires
|
||
|
database-specific SQL your JPA implementation doesn't support.
|
||
|
Or maybe your DBA has spent hours crafting the perfect select statement
|
||
|
for a query in your application's critical path. Whatever the reason, SQL
|
||
|
queries can remain an essential part of an application.
|
||
|
</para>
|
||
|
<para>
|
||
|
You are probably familiar with executing SQL queries by obtaining a
|
||
|
<classname>java.sql.Connection</classname>, using the JDBC APIs to create
|
||
|
a <classname>Statement</classname>, and executing that <classname>Statement
|
||
|
</classname> to obtain a <classname>ResultSet</classname>. And of course,
|
||
|
you are free to continue using this low-level approach to SQL execution in
|
||
|
your JPA applications. However, JPA also supports executing SQL queries
|
||
|
through the <classname>javax.persistence.Query</classname>
|
||
|
interface introduced in <xref linkend="jpa_overview_query"/>.
|
||
|
Using a JPA SQL query, you can retrieve either persistent objects
|
||
|
or projections of column values. The following sections detail each use.
|
||
|
</para>
|
||
|
<section id="jpa_overview_sqlquery_create">
|
||
|
<title>Creating SQL Queries</title>
|
||
|
<indexterm zone="jpa_overview_sqlquery_create">
|
||
|
<primary>SQL queries</primary>
|
||
|
<secondary>creating</secondary>
|
||
|
</indexterm>
|
||
|
<para>
|
||
|
The <classname>EntityManager</classname> has two factory methods
|
||
|
suitable for creating SQL queries:
|
||
|
</para>
|
||
|
<programlisting format="linespecific">
|
||
|
public Query createNativeQuery (String sqlString, Class resultClass);
|
||
|
public Query createNativeQuery (String sqlString, String resultSetMapping);
|
||
|
</programlisting>
|
||
|
<para>
|
||
|
The first method is used to create a new <classname>Query</classname>
|
||
|
instance that will return instances of the specified class.
|
||
|
</para>
|
||
|
<para>
|
||
|
The second method uses a <literal>SqlResultSetMapping</literal>
|
||
|
to determine the type of object or objects to return.
|
||
|
The example below shows these methods in action.
|
||
|
</para>
|
||
|
<example id="jpa_overview_sqlquery_createex">
|
||
|
<title>Creating a SQL Query</title>
|
||
|
<programlisting format="linespecific">
|
||
|
EntityManager em = ...;
|
||
|
Query query = em.createNativeQuery ("SELECT * FROM MAG", Magazine.class);
|
||
|
processMagazines (query.getResultList ());
|
||
|
</programlisting>
|
||
|
</example>
|
||
|
<note>
|
||
|
<para><indexterm><primary>SQL queries</primary><secondary>stored procedures</secondary></indexterm><indexterm><primary>stored procedures</primary><secondary>as queries</secondary><seealso>Query</seealso></indexterm>
|
||
|
In addition to SELECT statements, OpenJPA supports stored procedure
|
||
|
invocations as SQL queries. OpenJPA will assume any SQL that does
|
||
|
not begin with the <literal>SELECT</literal> keyword (ignoring
|
||
|
case) is a stored procedure call, and invoke it as such at the
|
||
|
JDBC level.
|
||
|
</para>
|
||
|
</note>
|
||
|
</section>
|
||
|
<section id="jpa_overview_sqlquery_obj">
|
||
|
<title>Retrieving Persistent Objects with SQL</title>
|
||
|
<indexterm zone="jpa_overview_sqlquery_obj">
|
||
|
<primary>SQL queries</primary>
|
||
|
<secondary>retrieving persistent objects</secondary>
|
||
|
</indexterm>
|
||
|
<indexterm zone="jpa_overview_sqlquery_obj">
|
||
|
<primary>persistent objects</primary>
|
||
|
<secondary>retrieving with SQL</secondary>
|
||
|
<seealso>SQL queries</seealso>
|
||
|
</indexterm>
|
||
|
<para>
|
||
|
When you give a SQL <classname>Query</classname> a candidate class, it
|
||
|
will return persistent instances of that class. At a minimum, your
|
||
|
SQL must select the
|
||
|
class' primary key columns, discriminator column (if mapped), and
|
||
|
version column (also if mapped). The JPA runtime uses the values
|
||
|
of the primary key columns to construct each result object's identity,
|
||
|
and possibly to match it with a persistent object already in the
|
||
|
<classname>EntityManager</classname>'s cache. When an object is
|
||
|
not already cached, the
|
||
|
implementation creates a new object to represent the current result
|
||
|
row. It might use the discriminator column value to make sure it
|
||
|
constructs an object of the correct subclass. Finally, the query
|
||
|
records available version column data for use in optimistic concurrency
|
||
|
checking, should you later change the result object and flush it back
|
||
|
to the database.
|
||
|
</para>
|
||
|
<para>
|
||
|
Aside from the primary key, discriminator, and version columns, any
|
||
|
columns you select are used to populate the persistent fields of each
|
||
|
result object. JPA implementations will compete on how effectively
|
||
|
they map your selected data to your persistent instance fields.
|
||
|
</para>
|
||
|
<para>
|
||
|
Let's make the discussion above concrete with an example. It uses
|
||
|
the following simple mapping between a class and the database:
|
||
|
</para>
|
||
|
<mediaobject>
|
||
|
<imageobject>
|
||
|
<!-- PNG image data, 320 x 149 (see README) -->
|
||
|
<imagedata fileref="img/sqlquery-model.png" width="213px"/>
|
||
|
</imageobject>
|
||
|
</mediaobject>
|
||
|
<example id="jpa_overview_sqlquery_objex">
|
||
|
<title>Retrieving Persistent Objects</title>
|
||
|
<programlisting format="linespecific">
|
||
|
Query query = em.createNativeQuery ("SELECT ISBN, TITLE, PRICE, "
|
||
|
+ "VERS FROM MAG WHERE PRICE > 5 AND PRICE < 10", Magazine.class);
|
||
|
List<Magazine> results = query.getResultList ();
|
||
|
for (Magazine mag : results)
|
||
|
processMagazine (mag);
|
||
|
</programlisting>
|
||
|
</example>
|
||
|
<para>
|
||
|
The query above works as advertised, but isn't very flexible. Let's
|
||
|
update it to take in parameters for the minimum and maximum price,
|
||
|
so we can reuse it to find magazines in any price range:
|
||
|
</para>
|
||
|
<example id="jpa_overview_sqlquery_obj_paramex">
|
||
|
<title>SQL Query Parameters</title>
|
||
|
<programlisting format="linespecific">
|
||
|
Query query = em.createNativeQuery ("SELECT ISBN, TITLE, PRICE, "
|
||
|
+ "VERS FROM MAG WHERE PRICE > ?1 AND PRICE < ?2", Magazine.class);
|
||
|
|
||
|
query.setParameter (1, 5d);
|
||
|
query.setParameter (2, 10d);
|
||
|
|
||
|
List<Magazine> results = query.getResultList ();
|
||
|
for (Magazine mag : results)
|
||
|
processMagazine (mag);
|
||
|
</programlisting>
|
||
|
</example>
|
||
|
<para><indexterm><primary>SQL queries</primary><secondary>parameters</secondary></indexterm><indexterm><primary>parameters</primary><secondary>in SQL queries</secondary><seealso>SQL queries</seealso></indexterm>
|
||
|
Like JDBC prepared statements, SQL queries represent parameters with
|
||
|
question marks, but are followed by an integer to represent its
|
||
|
index.
|
||
|
</para>
|
||
|
</section>
|
||
|
<!--
|
||
|
<section id="jpa_overview_sqlquery_proj">
|
||
|
<title>SQL Projections</title>
|
||
|
<indexterm zone="jpa_overview_sqlquery_proj">
|
||
|
<primary>SQL queries</primary>
|
||
|
<secondary>projections</secondary>
|
||
|
</indexterm>
|
||
|
<indexterm zone="jpa_overview_sqlquery_proj">
|
||
|
<primary>projections</primary>
|
||
|
<secondary>of column data</secondary>
|
||
|
<seealso>SQL queries</seealso>
|
||
|
</indexterm>
|
||
|
<para>
|
||
|
SQL queries without a candidate class are treated as projections of
|
||
|
column data. If you select a single column, the query returns
|
||
|
a list of <classname>Object</classname>s. If you select multiple
|
||
|
columns, it returns a list of <classname>Object[]</classname>s.
|
||
|
In either case, each column value is obtained using the
|
||
|
<methodname>java.sql.ResultSet.getObject</methodname> method. The
|
||
|
following example demonstrates a query for the values of the
|
||
|
<literal>ISBN</literal> and <literal>VERS</literal> columns of all
|
||
|
<literal>MAG</literal> table records, using the data model we
|
||
|
defined in <xref linkend="jpa_overview_sqlquery_obj"/>.
|
||
|
</para>
|
||
|
<example id="jpa_overview_sqlquery_projex">
|
||
|
<title>Column Projection</title>
|
||
|
<programlisting>
|
||
|
Query query = em.newQuery ("javax.persistence.query.SQL",
|
||
|
"SELECT ISBN, VERS FROM MAG");
|
||
|
List results = query.getResultList ();
|
||
|
for (Iterator itr = results.iterator (); itr.hasNext ();)
|
||
|
{
|
||
|
Object[] data = (Object[]) results.next ();
|
||
|
processISBNAndVersion (data[0], data[1]);
|
||
|
}
|
||
|
</programlisting>
|
||
|
<para>
|
||
|
Notice that in the code above, we did not set a candidate class.
|
||
|
Therefore, the query is treated as a projection.
|
||
|
</para>
|
||
|
</example>
|
||
|
<para>
|
||
|
<indexterm>
|
||
|
<primary>SQL queries</primary>
|
||
|
<secondary>result class</secondary>
|
||
|
</indexterm>
|
||
|
Our discussion of JPQL query result classes in
|
||
|
<xref linkend="jpa_overview_query_resultcls"/> also
|
||
|
applies to SQL queries. As with JPQL queries, SQL queries can
|
||
|
automatically pack their results into objects of a specified type.
|
||
|
JPA uses the <methodname>java.sql.ResultSetMetaData.getColumnLabel
|
||
|
</methodname> method to match each column alias to the result class'
|
||
|
public fields and JavaBean setter methods. Here is a modification of
|
||
|
our example above that packs the selected column values into JavaBean
|
||
|
instances.
|
||
|
</para>
|
||
|
<example id="jpa_overview_sqlquery_proj_labelex">
|
||
|
<title>Result Class</title>
|
||
|
<programlisting>
|
||
|
public class Identity
|
||
|
{
|
||
|
private String id;
|
||
|
private int versionNumber;
|
||
|
|
||
|
public void setId (String id)
|
||
|
{
|
||
|
this.id = id;
|
||
|
}
|
||
|
|
||
|
public String getId ()
|
||
|
{
|
||
|
return id;
|
||
|
}
|
||
|
|
||
|
public void setVersionNumber (int versionNumber)
|
||
|
{
|
||
|
this.versionNumber = versionNumber;
|
||
|
}
|
||
|
|
||
|
public int getVersionNumber ()
|
||
|
{
|
||
|
return versionNumber;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Query query = em.createNativeQuery ("javax.persistence.query.SQL",
|
||
|
"SELECT ISBN AS id, VERS AS versionNumber FROM MAG", Identity.class);
|
||
|
List results = query.getResultList ();
|
||
|
for (Iterator itr = results.iterator (); itr.hasNext ();)
|
||
|
processIdentity ((Identity) itr.next ());
|
||
|
</programlisting>
|
||
|
</example>
|
||
|
</section>
|
||
|
<section id="jpa_overview_sqlquery_named">
|
||
|
<title>Named SQL Queries</title>
|
||
|
<indexterm zone="jpa_overview_sqlquery_named">
|
||
|
<primary>SQL queries</primary>
|
||
|
<secondary>named</secondary>
|
||
|
<see>named queries</see>
|
||
|
</indexterm>
|
||
|
<indexterm zone="jpa_overview_sqlquery_named">
|
||
|
<primary>named queries</primary>
|
||
|
<secondary>SQL</secondary>
|
||
|
</indexterm>
|
||
|
<para>
|
||
|
We discussed how to write named JPQL queries in
|
||
|
<xref linkend="jpa_overview_query_named"/>. Named queries, however,
|
||
|
are not limited to JPQL. By setting the <literal>query</literal>
|
||
|
element's <literal>language</literal> attribute to <literal>
|
||
|
javax.persistence.query.SQL</literal>, you can define a named SQL query. A
|
||
|
named SQL query within a <literal>class</literal> element queries for
|
||
|
instances of that class; a named SQL query outside of a <literal>class
|
||
|
</literal> element acts as a column data projection.
|
||
|
</para>
|
||
|
<example id="jpa_overview_sqlquery_namedex">
|
||
|
<title>Named SQL Queries</title>
|
||
|
<programlisting>
|
||
|
<![CDATA[<?xml version="1.0"?>
|
||
|
<jdoquery>
|
||
|
<query name="salesReport" language="javax.persistence.query.SQL">
|
||
|
SELECT TITLE, PRICE * COPIES FROM MAG
|
||
|
</query>
|
||
|
<package name="org.mag">
|
||
|
<class name="Magazine">
|
||
|
<query name="findByTitle" language="javax.persistence.query.SQL">
|
||
|
SELECT * FROM MAG WHERE TITLE = ?
|
||
|
</query>
|
||
|
</class>
|
||
|
</package>
|
||
|
</jdoquery>]]>
|
||
|
</programlisting>
|
||
|
<para>
|
||
|
The <literal>salesReport</literal> query above returns the title
|
||
|
and revenue generated for each <classname>Magazine</classname>.
|
||
|
Because it is a projection, it does not have a candidate class, and
|
||
|
so we specify it at the root level.
|
||
|
</para>
|
||
|
<para>
|
||
|
The <literal>findByTitle</literal> query returns the <classname>
|
||
|
Magazine</classname> with the title given on execution. The code
|
||
|
below executes both queries.
|
||
|
</para>
|
||
|
<programlisting>
|
||
|
EntityManager em = ...;
|
||
|
Query query = em.newNamedQuery (null, "salesReport");
|
||
|
List sales = query.getResultList ();
|
||
|
for (Iterator itr = sales.iterator (); itr.hasNext ();)
|
||
|
{
|
||
|
Object[] salesData = (Object[]) itr.next ();
|
||
|
processSalesData ((String) salesData[0], (Number) salesData[1]);
|
||
|
}
|
||
|
|
||
|
query = em.newNamedQuery (Magazine.class, "findByTitle");
|
||
|
query.setUnique (true);
|
||
|
Magazine jdj = (Magazine) query.execute ("JDJ");
|
||
|
</programlisting>
|
||
|
</example>
|
||
|
</section>
|
||
|
<section id="jpa_overview_sqlquery_conclusion">
|
||
|
<title>Conclusion</title>
|
||
|
<para>
|
||
|
If you've used relational databases extensively, you might be tempted
|
||
|
to perform all your JPA queries with SQL. Try to resist this
|
||
|
temptation. SQL queries tie your application to the particulars of
|
||
|
your current table model and database vendor. If you stick with JPQL,
|
||
|
on the other hand, you can port your application to other schemas and
|
||
|
database vendors without any changes to your code. Additionally,
|
||
|
most JPA implementations already produce highly optimized SQL from
|
||
|
your JPQL filters, and many are able to cache JPQL query results
|
||
|
for added performance.
|
||
|
</para>
|
||
|
</section>
|
||
|
-->
|
||
|
</chapter>
|