Chapter 8. Queries

Hibernate supports a wide variety of options for retrieving objects from a database. These include the Hibernate Query Language (HQL), the Criteria API, and ordinary Structured Query Language (SQL).

In this chapter, we will take a look at each of these three retrieval mechanisms, and conclude with a mechanism for storing HQL and SQL queries directly in your *.hbm.xml mapping files.

HQL

The first thing you should know about HQL is that it is exclusively a query language. Unlike SQL, which offers statements of different forms for SELECT, UPDATE, DELETE, and INSERT, HQL is designed exclusively for retrieval. As a result, the syntax is most similar to the SELECT statement. There is no such thing as HQL for updating or inserting data, and the exact same HQL can be used for both SELECT and DELETE operations.

Some of the examples in Chapter 3 and Chapter 4 give an introduction to the use of HQL, and the methods for executing HQL are shown in Chapter 6. Nonetheless, HQL is most easily explored in the context of the Hibern8 IDE, a simple tool for executing HQL statements on the fly in a graphical environment.

Using Hibern8 IDE

The Hibern8 IDE is distributed with the Hibernate Extensions package. To use it, simply launch it using the command java net.sf.hibern8ide.Hibern8IDE (found in the hibern8ide.jar), with the class path set to include the various required Hibernate and Hibernate Extensions libraries as well as the persistent classes used by your application. Launching Hibern8 IDE will display the interface shown in Figure 8.1.

Starting Hibern8 IDE

Figure 8.1. Starting Hibern8 IDE

To establish the Hibernate configuration, click on the ... button next to either the Properties field (to select a hibernate.properties file) or the Config File field (to select a hibernate.cfg.xml file).

Click the Add button to add one or more *.hbm.xml mapping files. After adding all of the needed files, click the Apply button.

Using the hibernate.properties and *.hbm.xml files and classes from Chapter 3, Hibern8 IDE will look as shown in Figure 8.2.

Hibern8 IDE Configured

Figure 8.2. Hibern8 IDE Configured

Clicking on the HQL Commander tab, you can drag and drop objects or object properties from the left-side object display to the HQL entry field so that Hibern8 IDE can automatically generate HQL for you. Pressing control-enter will cause Hibern8 IDE to execute the HQL and display the results. Figure 8.3 shows an example of retrieving the artifact.id values.

Artifact ID Values

Figure 8.3. Artifact ID Values

Hibern8 IDE allows you to inspect the values of returned results, tunneling in through the returned object hierarchy. Figure 8.4 shows an example of this, allowing you to see in detail the objects returned by a request.

Inspecting Hibern8 IDE Results

Figure 8.4. Inspecting Hibern8 IDE Results

As described in Chapter 9, you can use Hibern8 in conjunction with a tool like IronTrack SQL to easily monitor the SQL generated by a given HQL statement. This can be very useful as you experiment with HQL using Hibern8.

It is easy to explore the capabilities of HQL using Hibern8, but to truly take advantage of Hibernate you need to understand the structure of HQL in more detail. You are encouraged to work through this reference in conjunction with Hibern8 and the various mapping files in the rest of the book to fully understand HQL.

HQL Reference

[select] from [where] [group by] [having] [order by];

This section serves as a road map for understanding the HQL syntax. It can be used to diagram a HQL statement, much as you diagram a sentence in English (circling the noun, the verb, etc.).

A HQL command is composed of several clauses, as shown above. The from clause is the only portion of a query that is mandatory. For example, the simple command from com.cascadetg.ch03.Owner will return all of the owner table records as Owner objects.

The select clause is used to limit the returned data to specific properties or to control the format for the returned data. It will be used to return either an aggregate value (e.g., the number of records returned) or a subset of the data (e.g., just the first name of the student objects).

The from clause is used to widen the returned data to additional classes. The tables used are as determined by the class mappings (defined by your *.hbm.xml files). HQL requires at least one class to form the basis of the query. If you wish to use more than one table, you may have to use join statements (described below) to control how the tables are returned.

The where, group by, and having clauses are used to cull the data returned. You can use a combination of logical expressions and Boolean expressions to compose your where clause. In addition, you can use collection commands to work with collections in your where statements.

The order by clause is used to sort the returned data.

Notation Reference

The clauses given below are expected to be formatted with a particular set of tokens. The format of this HQL guide is based on the notation shown in Table 8.1.

Table 8.1. HQL Grammar Notation

[ ... ]

Indicates an optional term.

( ... )

Indicates a grouping of terms.

... | ...

Indicates that one side or the other of the | is expected.

*

Indicates that zero, one, or many terms are expected.

?

Indicates that zero or one terms expected.

UPPERCASE

Used to indicate an expected token.

lowercase

Used to indicate additional text, as specified by another clause or some other text.

This reference uses uppercase to indicate HQL terms, but in actual use HQL terms are case-insensitive.

Obviously, HQL uses some of the symbols shown in Table 8.1. Parentheses, for example, are used to indicate precedence in HQL queries as well as in the notation. Therefore, the tokens in Table 8.2 should be translated to these terms when writing actual HQL queries.

Table 8.2. HQL Grammar Symbols

Notation

Symbol

COMMA

,

OPEN

(

CLOSE

)

OPEN_BRACKET

[

CLOSE_BRACKET

]

CONCAT

||

PLUS

+

MINUS

-

DIV

/

STAR

*

path

An identifier to a particular class or property

Note that the path elements are case-sensitive. For example, select owner from Owner as owner and SELECT owner FROM Owner AS owner are equivalent, whereas SELECT FROM OWNER is invalid (because OWNER doesn't match the case of the name Owner given in the mapping file).

Select

SELECT DISTINCT? selectedPropertiesList | ( NEW className OPEN selectedPropertiesList CLOSE );

The optional distinct keyword can be used to reduce the result set to unique results (no result is returned more than once). This can be useful when you wish to limit the results. For example, the statement select student.id from Student as student, Student as student2 returns the full product of every record in the Student table as matched with every other student record (literally, the records in the student table multiplied by the records in the student table). The distinct keyword can be used to limit these results back to the individual student records. For example, the statement select distinct student.id from Student as student, Student as student2 will return the same results as a from Student.

If one or more properties (selectedProperties) are specified, the query will return them as mapped to objects. For example, the HQL select owner.id, owner.name from com.cascadetg.ch03.Owner as owner will return a set of java.lang.Long and java.lang.String objects.

Instead of specifying that the properties should be returned as a set of different objects, you may wish to use the new className statement instead. This will return an arbitrary class, calling the constructor as the result of a query. For example, the HQL command select new java.lang.StringBuffer (owner.name) from Owner as owner returns a set of java.lang.StringBuffer objects, initialized with the owner.name property.

Selected Properties List

( path | aggregate ) ( COMMA path | aggregate )*

The properties returned by a SELECT statement can be either a set of properties or an aggregate function. For example, the HQL select max(result.score), min(result.score) from Examresult as result is valid, as is select result from Examresult as result. The statement select result, max(result.score), min(result.score) from Examresult as result, however, is NOT valid.

Aggregate

COUNT OPEN path CLOSE
SUM OPEN path CLOSE
AVG OPEN path CLOSE
MAX OPEN path CLOSE
MIN OPEN path CLOSE
COUNT OPEN STAR CLOSE
COUNT OPEN DISTINCT | ALL path CLOSE

The aggregate functions are used to return statistical data on a set of records rather than the records themselves.

  • avg(path) calculates the mean average value.

  • count(*) returns the number of items in a group, including null values and duplicates.

  • count(ALL path) returns the number of non-null values.

  • count(DISTINCT path) returns the number of unique, non-null values.

  • max(path) finds the maximum value.

  • min(path) finds the minimum value.

  • sum(path) adds the values specified.

Collection Properties

( OPEN query CLOSE ) | ( 'elements'|'indices' OPEN path CLOSE )

Collections have several special properties in addition to the obvious properties that are dependent on the type of collection. All collections can refer to the elements property, which is simply a result of all the possible objects.

For example, select student.examresults.elements.score from com.cascadetg.ch04.Student as student returns the score values from the examresult table as java.lang.Integer, where examresult.studentID is bound to a valid student.id.

As another example, select student.examresults.elements from com.cascadetg.ch04.Student as student returns the set of examresult records, bound as com.cascadetg.ch04.Examresult.

Table 8.3 shows the various collection properties.

Table 8.3. Collection Properties

Property

Meaning

elements

The objects in the collection.

indices

The index values of the collection (only available if the collection is indexed; see Chapter 7).

size

The size of the collection. Useful if you wish to know the number of associated objects without fetching the entire collection.

maxIndex

The maximum index value (only available if the collection is indexed).

minIndex

The minimum index value (only available if the collection is indexed).

maxElement

The element corresponding to the maximum index value (only available if the collection is indexed).

minElement

The element corresponding to the maximum index value (only available if the collection is indexed).

index [...]

Used to find elements in an indexed collection. The expression inside the [ ] tokens is evaluated to determine the resulting elements against the index.

You can extend the path of a collection past the elements property. For example, the statement select student.examresults.elements.score from Student as student returns the scores of the student as java.lang.Integer objects.

From

FROM className AS? identifier (  ( COMMA className AS? identifier ) | ( joinType classname
From AS? identifier ) )*

The from clause is used to identify the source of the data to be returned. The tables are specified by the classname (com.cascadetg.ch04.Student, or just by Student if the auto-import attribute of the hibernate-mapping tag is set to true as described in Chapter 5.

An identifier must be assigned to the class for queries that need to refer to a class property. For example, com.cascadetg.ch04.Student is typically assigned an identifier such as student. This identifier is required in order to refer to the class in other clauses of the query. For example, select first Name from Student is invalid HQL; the valid statement would be select student.firstName from Student as student.

Example 8.5. Student Data

mysql> select * from student;
+----+-----------+----------+-------------+
| ID | firstName | lastName | idString    |
+----+-----------+----------+-------------+
|  1 | Bob       | Smith    | 123-45-6789 |
|  2 | John      | Stevens  | 456-78-9012 |
|  3 | Betty     | Almara   | 098-76-5432 |
+----+-----------+----------+-------------+
3 rows in set (0.00 sec)

The identifier is particularly useful when you are writing a complex query that needs to refer to the same table with two different meanings; for example, if you wish to join a class against itself.

The AS token is optional, but some prefer it to enhance readability. The statements select owner from Owner as owner and select owner from Owner owner are equivalent.

The values of the joinType token are as shown below.

Join Types

( ( 'left'|'right' 'outer'? ) | 'full' | 'inner' )? JOIN FETCH?

The join syntax is used to control the product of the result of joining two tables together. To consider the results of the various types of join, let's start with a bit of data as shown in Figures 8.5 and 8.6.

Example 8.6. Exam Result Data

mysql> select * from examresult;
+----+-------+-----------+--------+
| ID | score | studentID | examID |
+----+-------+-----------+--------+
|  1 |    85 |         1 |      1 |
|  2 |    89 |         2 |      1 |
|  4 |    75 |         1 |      2 |
|  5 |    83 |         2 |      2 |
+----+-------+-----------+--------+
4 rows in set (0.01 sec)

Looking at the data, we see that there are three students, but only students 1 and 2 have actually taken any exams. Joins allow us to glue together the data items from Figure 8.5 and Figure 8.6 in different ways to obtain different results (the heart of the notion of a relational database).

WARNING

Some databases do not support all of the various forms of join supported by Hibernate. Try to avoid being too clever with your joins if possible. Even if the join works, make sure that it performs well. See Chapter 10 for more information on managing performance.

Inner Join

From basic SQL, we know that the query select result, student from Student as student, Examresult as result will result in a multiplied set of returned data, as shown in Figure 8.7. This is what is referred to as an inner join.

Unrestricted Inner Join Results

Figure 8.7. Unrestricted Inner Join Results

A where clause is used to cull the data down. The statement select result, student from Student as student, Examresult as result where result.student=student restricts our query to the students who have taken exams, as shown in Figure 8.8.

Bound Inner Join Results

Figure 8.8. Bound Inner Join Results

Inner joins are the most basic form of join, and the easiest to understand.

Left/Right Joins

Now, imagine that we wish to retrieve all of the students even if they haven't taken a test—in other words, return the records even of a student for whom there are no examresult records. We have to use an outer join to retrieve all of the records. Outer joins come in two flavors, right join and left join. The use of the right or left term depends on the table you wish to use as the “master” table.

The statement select result, student from Student as student, Examresult as result right join result.student joinedstudent where student=joinedstudent returns all the students, including the student with a null examresult as shown in Figure 8.9.

Right Outer Join

Figure 8.9. Right Outer Join

The join is performed and retrieves all of the records from the student table, even though the record may not be present in the right table (the student table). In effect, you are asking for all records from the right table, in this case student, even if there are no corresponding entries present in the examresult table.

Conversely, using the left join statement instead, as in select result, student from Student as student, Examresult as result left join result.student joinedstudent where student=joinedstudent returns the joined records from the perspective of the examresult table (the left table), as shown in Figure 8.10. In effect, you are asking for all of the records in the examresult table, even if not present in the student table. As there are no records in the examresult table that do not point to a student (that would be a foreign-key violation), you simply get the exam results.

Left Outer Join

Figure 8.10. Left Outer Join

Full Joins

A full join will return all records from both tables, regardless of any matches between the tables. Note that this command is not supported on all databases (in particular, MySQL doesn't offer support for the full join command).

Outer as Inverse of Inner

It's worth pointing out that you may be specifically seeking the records that do not match a regular join. For example, you may only be interested in the students who haven't taken an exam. To perform this query, you can use the is null modifier, as in select result, student from Student as student, Examresult as result right join result.student joinedstudent where student=joinedstudent and result is null, which produces the results shown in Figure 8.11.

Using an Outer Join to Obtain the Inverse of an Inner Join

Figure 8.11. Using an Outer Join to Obtain the Inverse of an Inner Join

Fetch Joins

The fetch join is a useful shortcut used to fetch parent and child objects in a single select. For example, the statement select student from Student as student left join fetch student.examresults returns all of the students and their Examresult objects, regardless of whether the student has any associated exam results. If there are any examresults, they are already loaded into Examresult objects, associated with the Student object (accessible via the Student.getExamresults() method. If you know that you will be reading the exam results of the student objects, but have the exam result relationship set to lazy by default, this is an excellent way to retrieve all of the needed data in a single statement.

Similarly, the statement select distinct student from Student as student inner join fetch student.examresults retrieves the student objects with an exam record. As in the left join fetch, the Student and Examresult objects are loaded, but only students with an Examresult are returned.

The fetch statement may only be used as an inner join or a left outer join.

As of the release of Hibernate Extensions-2.1, the Hibern8 IDE does not support the fetch statement. To test fetch joins, you'll need to use the HQL in your own sample code.

Where

WHERE expression

The where clause is used to cull the set of returned rows.

A huge variety of possible expressions, some of them database-dependent, are allowed in a WHERE clause. Regardless of the expression, parentheses ( ) are used to indicate grouping and precedence. Some of the possible elements in a where clause are shown in Table 8.4.

Table 8.4. Where Operations

Element

Examples

Mathematical operators

+, -, *, /

Binary comparison operators

=, >=, <=, <>, !=, like

Logical operations

and, or

String concatenation

||

JDBC IN parameters

?

Named parameters

:name, :start_date, :x1

SQL scalar functions

upper(), lower()

SQL literals

'foo', 69, '1970-01-01 10:00:01.0'

Java public static final constants

java.lang.Integer.MAX_VALUE

The interpretation of SQL functions is database-dependent. Hibernate will automatically convert the SQL literal ' delimiters to the proper characters for the database.

A where expression may include mathematical operations, logical operations, boolean operations, or a quantified expression (in SQL terms, a subselect).

Logical Operations

Two logical operations are commonly used in a where statement.

The and operator requires both sides to evaluate to true, otherwise it evaluates to false.

The or operator evaluates both sides. If either side evaluates to true, it will return true; otherwise it evaluates to false.

For example, the statement select student from Student as student where student.id=1 or student.id=3 will return two records, with an id of 1 and 2.

Boolean Operations

Table 8.5 lists the boolean operations supported by the majority of databases.

Table 8.5. Boolean Operations

Operator

Meaning

Inverse

!=

Is not equal

=

<

Less than

>=

<=

Less than or equal

>

<>

Is not equal

=

=

Equals

!= or <>

>

Greater than

<=

>=

Greater than or equal

<

between

Inclusive query (used with the and operator). For example, select student from Student as student where id between 1 and 3 will return students 1, 2, and 3.

not between

ilike (case-insensitive)

Case-insensitive like (not supported on all databases).

not ilike

in

Used to limit in conjunction with a collection. Alternatively, used in statements like select student from Student as student where first Name in ('Bob', 'John').

not in

is

Used for queries such as is null or is not null

is not

is not

Used as part of the is not null

is

l ike

Like

not like

not between

Reverse of between

between

not ilike (case-insensitive)

Reverse of ilike

llike

not in

Reverse of in

in

not like

Reverse of like

like

When using the like and ilike statements, the % (percent) character is used as a multiple-character wildcard, and the _ (underscore) character is used to indicate a single character. For example, the statement select student.firstName from Student as student where firstName like '_ob' returns the value Bob. The statement select student.firstName from Student as student where firstName like 'B%' returns Bob and Betty.

The tokens #, ~, !#, !~, =>, =<, !<, and !> are reserved by Hibernate as boolean operators but have no meaning in standard SQL. The meaning of these tokens (if any) is database-dependent. Consult your database manual for more information.

Quantified Expression

'exists' | ( expression 'in' ) | ( expression OPERATION 'any' | 'some' ) collection

Valid options for OPERATION are like, =, <, >, <>, !=, ^=, <=, and >=.

A qualified expression is an HQL mechanism for expressing a SQL subselect of a collection. A subselect describes using the results of a SELECT statement as part of the WHERE clause of another statement—in effect, a nested select. The subselect must return a single column, which is compared against an expression using an operation. The evaluation of this operation determines whether the resulting record should be returned. A subselect is described by the term any, some, all, exists, or not exists.

any

some or any returns true when the comparison specified is true for any of the values returned by the subselect (typically a collection column). Imagine a subselect that returns the numbers 1 through 10. Therefore, the statement 5 < any (subselect expression) would evaluate to true.

some

Synonym for any.

all

Returns true when the comparison specified is true for all pairs (scalar_expression, x) where x is a value in the single-column set; otherwise returns false. Imagine a subselect that returns the numbers 1 through 10. Therefore, the statement 5 < any (subselect expression) would evaluate to false, because some of the results of the subselect are greater than or equal to 5.

exists

Returns true if the subselect matches elements from the select.

not exists

Reverse of exists.

Parameter Binding

When working with a WHERE clause, you may be tempted to include constants directly in the string. For example, you may wish to use a string such as select student from Student as student where firstName='Bob'. Instead, you are advised (and in some instances required) to use parameter binding. HQL supports two styles of parameter binding, JDBC and named. JDBC uses the ? character to indicate bindings, and named uses the : character to preface a named parameter substitution.

Use the Session and Query classes to set parameters, as described in Chapter 6. Listing 8.1 shows an example of the use of a named parameter.

Example 8.1. Named Parameter Binding

Query myQuery = hibernateSession.
 createQuery("from Student as student where student.firstName =
  :name");
myQuery.setString("name", "Bob");
Iterator students = myQuery.iterate();

Listing 8.2 shows an example of the use of a JDBC-style bound parameter. Note that the parameters are set based on an index of 0, not 1.

Example 8.2. JDBC Parameter Binding

Query myQuery = hibernateSession.
 createQuery("from Student as student where student.firstName =
  ?");
myQuery.setString(0, "Bob");
Iterator students = myQuery.iterate();

Group By

GROUP BY path ( COMMA path )*

The command GROUP BY is used to restrict the results to a single result per table. This command is useful in a variety of situations, such as when you are using aggregate functions. For example, the command select student, max(result.score) from Student as student, Examresult as result where student=result.student group by student returns a set of students and the corresponding java.lang.Integer value for each student's high score.

Having

HAVING expression

A HAVING clause is used in the same way as a WHERE clause except for one key difference. WHERE selects rows before the groups and aggregates are calculated, whereas HAVING selects rows after the groups and aggregates have been calculated.

In practice, there is no reason to use a HAVING clause unless the HAVING clause has an aggregate function (otherwise, it's equivalent to a WHERE clause). Unfortunately, certain databases (such as MySQL) don't support an aggregate in the HAVING clause. For those databases, HAVING and WHERE are the same.

Order By

ORDER_BY selectedPropertiesList (ASC | DESC)?

The order by command allows you to specify the order in which the data should be returned, as performed by the database. By default, the values are sorted in ascending (ASC) order. This order can be reversed using the DESC token.

As an example, the statement select student from Student as student order by student.firstName returns the students sorted by first name, starting with the letter A, whereas the statement select student from Student as student order by student.firstName desc returns the students starting with the letter Z.

The ordering is performed by the database, and the precise results are dependent on its sorting mechanisms.

Criteria Queries

Hibernate supports the Criteria API, an object-based query mechanism that constructs a query from a set of objects. The Criteria API is perhaps best understood in the context of an ordinary HQL statement, as described earlier in this chapter.

HQL Clause

Criteria Equivalent[s]

select

Not available

from

Criteria criteria = session.create

Criteria(Student.class);

from joins

Criteria.setFetchMode()for associations.

where

net.sf.hibernate.expression.Expression to obtain built-in net.sf.hibernate.expression.Criterion

group by

Not available

having

Not available

order by

Criteria.addOrder() to add net.sf.hibernate.expression.Order

The Criteria interface, as shown in Figure 8.12, is returned by a simple Session.createCriteria() call.

Criteria Interface

Figure 8.12. Criteria Interface

Normally, you will use the Expression factory, as shown in Figure 8.13, to obtain a criterion that you wish to apply.

Expression Factory

Figure 8.13. Expression Factory

Figure 8.14 shows the hierarchy of built-in criteria. Normally, you won't instantiate these directly, and instead will use the built-in Expression factory.

Built-in Criteria Hierarchy

Figure 8.14. Built-in Criteria Hierarchy

The code shown in Listing 8.3 returns the same result set as the HQL select student.firstName from Student as student where student.firstName like 'B%'.

Example 8.3. Criteria Query

Criteria myQuery =
       hibernateSession.createCriteria(Student.class);
myQuery.add(Expression.like("firstName", "B%"));

Method Chaining

In addition to the standard method execution shown in Listing 8.3, the Criteria API supports the notion of method chaining. Simply put, most of the methods on the Criteria interface return the calling object (i.e., they conclude with the statement return this). This allows you to rewrite the code shown in Listing 8.3 as shown in Listing 8.4.

Example 8.4. Chained Criteria Query

Criteria myQuery =
     hibernateSession.createCriteria(Student.class).add(
          Expression.like("firstName", "B%"));

As can be seen, this may lead to different (but equivalent) ways of expressing more complex statements. For example, the code shown in Listing 8.5 is equivalent to the code in Listing 8.6.

Example 8.5. Complex Criteria

Criteria myQuery =
                hibernateSession.createCriteria(Student.class);
myQuery.add(Expression.like("firstName", "B%"));
myQuery.add(Expression.like("lastName", "S%"));
myQuery.setFetchMode("examresults", FetchMode.EAGER);
myQuery.setMaxResults(10);
myQuery.setTimeout(1000);

Example 8.6. Complex Criteria with Method Chaining

Criteria myQuery =
     hibernateSession
          .createCriteria(Student.class)
          .add(Expression.like("firstName", "B%"))
          .add(Expression.like("lastName", "S%"))
          .setFetchMode("examresults", FetchMode.EAGER)
          .setMaxResults(10)
          .setTimeout(1000);

In certain instances, the more verbose method shown in Listing 8.5 is easier to understand and debug, but the choice of syntax is largely a matter of style.

Easily Override Lazy Settings

One helpful use of the Criteria API is the ability to easily override a lazy="true" setting in a Hibernate mapping with the use of the setFetchMode() method. For example, as shown in Listing 8.6, the method setFetchMode()overrides the lazy="true" setting in the Student mapping of the examresults collection.

Put another way, the setFetchMode() is a convenient way to express an HQL join statement.

Native SQL Queries

In addition to HQL and Criteria, Hibernate allows you to retrieve data from the database using native SQL. Describing SQL would go beyond the scope of this text, but it should be noted here that Hibernate requires aliases to be used when working with native SQL. The aliases are used to bind the SQL to the returned objects. Listing 8.7 shows an example of the execution of a native SQL statement binding to the return objects.

Example 8.7. Executing Native SQL

session.createSQLQuery("SELECT {student.*} FROM student AS {student}
WHERE ID<10", "student", Student.class).list();

If you simply wish to execute raw SQL against the database (and not retrieve a set of objects bound to a query), you will probably need to query the session for the underlying JDBC connection. An example of the use of raw SQL execution from a Hibernate session can be found in Listing 2.7 (the setInnoDB() method).

Keep in mind that SQL statements mixed with other statements may not execute in the order desired unless you use the Session.flush() method. For more on this, see Chapter 9.

The full scope and theory of SQL is beyond the scope of this book (especially considering the wide variety of proprietary extensions). Consult your database documentation or another text on SQL for more information on native SQL queries.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.172.146