Enterprise Java Development@TOPIC@
In the previous chapter we formed queries based on JPAQL -- which are based on entity and property names and the relationships defined within the entity class structure. In a real application, there is also a need to form queries that go outside the boundaries of the entity class model -- but should still be pushed to the database to be performed. JPA provides us an escape hatch to execute raw SQL queries. Unlike JPAQL, SQL queries need not have a direct relation to the JPA entity model. We will start with something simple and then move to more complex usage of the SQL query capability within JPA.
In this section you will setup a JUnit test case to do work within this chapter.
package myorg.queryex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.junit.Test;
public class SQLQueryTest extends QueryBase {
private static final Logger log = LoggerFactory.getLogger(SQLQueryTest.class);
@Test
public void test() {}
}
Build the module and run the test case.
$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.queryex.SQLQueryTest ... Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS
You may remove the sample @Test at this time since we will be adding more tests below.
In this section you will create a simple SQL Query using the entity manager.
@Test
public void testSQLQuery() {
log.info("*** testSQLQuery ***");
@SuppressWarnings("unchecked")
List<String> titles = em.createNativeQuery(
"select title from queryex_movie " +
"order by title ASC").getResultList();
for (String title : titles) {
log.debug(title);
}
assertEquals("unexpected number of titles", 7, titles.size());
}
Run the new test method and observe the database query that resulted.
$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.queryex.SQLQueryTest#testSQLQuery ... -*** testSQLQuery *** Hibernate: select title from queryex_movie order by title ASC -Animal House -Apollo 13 -Diner -Footloose -Sleepers -Tremors -Wag The Dog ... [INFO] BUILD SUCCESS
Notice the query issued to the database is exactly what you entered.
@Test
public void testSQLResultMapping() {
log.info("*** testSQLResultMapping ***");
@SuppressWarnings("unchecked")
List<Movie> movies = em.createNativeQuery(
"select m.* from queryex_movie m " +
"join queryex_person p on p.id = m.director_id " +
"where p.first_name = 'Ron'" +
"order by title ASC", Movie.class).getResultList();
log.debug("result=" + movies);
for (Movie movie: movies) {
log.debug("em.contains(" + movie + ")=" + em.contains(movie));
assertTrue(movie + " not managed", em.contains(movie));
}
assertEquals("unexpected number of movies", 2, movies.size());
}
$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.queryex.SQLQueryTest#testSQLResultMapping -*** testSQLResultMapping *** Hibernate: select m.* from queryex_movie m join queryex_person p on p.id = m.director_id where p.first_name = 'Ron' order by title ASC -result=[Apollo 13 (1995), Tremors (1990)] -em.contains(Apollo 13 (1995))=true -em.contains(Tremors (1990))=true ... [INFO] BUILD SUCCESS
Add the following lines to access an object related to the returned entity.
log.debug("checking unmapped entity name"); assertEquals("unexpected director first name", "Ron", movies.get(0).getDirector().getPerson().getFirstName());
$ mvn clean test -Ph2db -Dtest=myorg.queryex.SQLQueryTest#testSQLResultMapping ... -result=[Apollo 13 (1995), Tremors (1990)] -em.contains(Apollo 13 (1995))=true -em.contains(Tremors (1990))=true -checking unmapped entity name Hibernate: select director0_.PERSON_ID as PERSON1_2_0_ from QUERYEX_DIRECTOR director0_ where director0_.PERSON_ID=? Hibernate: select person0_.ID as ID0_0_, person0_.BIRTH_DATE as BIRTH2_0_0_, person0_.FIRST_NAME as FIRST3_0_0_, person0_.LAST_NAME as LAST4_0_0_ from QUERYEX_PERSON person0_ where person0_.ID=? ... [INFO] BUILD SUCCESS
@Entity
...
@SqlResultSetMappings({
@SqlResultSetMapping(name="Movie.movieMapping", entities={
@EntityResult(entityClass=Movie.class),
@EntityResult(entityClass=Director.class),
@EntityResult(entityClass=Person.class)
})
})
public class Movie implements Comparable<Movie>{
Add the following test method to your existing test case.
@Test
public void testSQLMultiResultMapping() {
log.info("*** testSQLMultiResultMapping ***");
@SuppressWarnings("unchecked")
List<Object[]> results = em.createNativeQuery(
"select * from queryex_movie m " +
"join queryex_director dir on dir.person_id = m.director_id " +
"join queryex_person p on p.id = dir.person_id " +
"where p.first_name = 'Ron'" +
"order by title ASC", "Movie.movieMapping").getResultList();
log.debug("query returned " + results.size() + " results");
for (Object[] result: results) {
Movie movie = (Movie)result[0];
Director director = (Director) result[1];
Person person = (Person)result[2];
log.debug("em.contains(" + movie + ")=" + em.contains(movie));
log.debug("em.contains(" + director + ")=" + em.contains(director));
log.debug("em.contains(" + person + ")=" + em.contains(person));
assertTrue(movie + " not managed", em.contains(movie));
assertTrue(director + " not managed", em.contains(director));
assertTrue(person + " not managed", em.contains(person));
}
assertEquals("unexpected number of movies", 2, results.size());
}
Run the test method and observe the result.
$ mvn clean test -Ph2db -Dtest=myorg.queryex.SQLQueryTest#testSQLMultiResultMapping ... -*** testSQLMultiResultMapping *** Hibernate: select * from queryex_movie m join queryex_director dir on dir.person_id = m.director_id join queryex_person p on p.id = dir.person_id where p.first_name = 'Ron' order by title ASC
Hibernate: select person0_.ID as ID0_0_, person0_.BIRTH_DATE as BIRTH2_0_0_, person0_.FIRST_NAME as FIRST3_0_0_, person0_.LAST_NAME as LAST4_0_0_ from QUERYEX_PERSON person0_ where person0_.ID=? Hibernate: select person0_.ID as ID0_0_, person0_.BIRTH_DATE as BIRTH2_0_0_, person0_.FIRST_NAME as FIRST3_0_0_, person0_.LAST_NAME as LAST4_0_0_ from QUERYEX_PERSON person0_ where person0_.ID=? -query returned 2 results -em.contains(Apollo 13 (1995))=true -em.contains(Ron Howard)=true -em.contains(Ron Howard)=true -em.contains(Tremors (1990))=true -em.contains(Ron Underwood)=true -em.contains(Ron Underwood)=true ... [INFO] BUILD SUCCESS
List<Object[]> results = em.createNativeQuery(
"select " +
"m.id, m.minutes, m.rating, m.release_date, m.title, m.director_id, " +
"dir.person_id, " +
"p.id, p.first_name, p.last_name, p.birth_date " +
"from queryex_movie m " +
"join queryex_director dir on dir.person_id = m.director_id " +
"join queryex_person p on p.id = dir.person_id " +
"where p.first_name = 'Ron'" +
"order by title ASC", "Movie.movieMapping").getResultList();
Notice there are two columns labeled "ID"; MOVIE.ID and PERSON.ID.
$ mvn clean test -Ph2db -Dtest=myorg.queryex.SQLQueryTest#testSQLMultiResultMapping1 -*** testSQLMultiResultMapping *** Hibernate: select m.id, m.minutes, m.rating, m.release_date, m.title, m.director_id, dir.person_id, p.id, p.first_name, p.last_name, p.birth_date from queryex_movie m join queryex_director dir on dir.person_id = m.director_id join queryex_person p on p.id = dir.person_id where p.first_name = 'Ron' order by title ASC Hibernate: select person0_.ID as ID0_0_, ... Hibernate: select person0_.ID as ID0_0_, ... -query returned 2 results -em.contains(Apollo 13 (1995))=true -em.contains(Ron Howard)=true -em.contains(Ron Howard)=true -em.contains(Tremors (1990))=true -em.contains(Ron Underwood)=true -em.contains(Ron Underwood)=true [INFO] BUILD SUCCESS
@Entity
...
@SqlResultSetMappings({
...
@SqlResultSetMapping(name="Movie.movieMapping2", entities={
@EntityResult(entityClass=Movie.class),
@EntityResult(entityClass=Director.class),
@EntityResult(entityClass=Person.class, fields={
@FieldResult(name="id", column="p_id"),
@FieldResult(name="firstName", column="first_name"),
@FieldResult(name="lastName", column="last_name"),
@FieldResult(name="birthDate", column="birth_date")
})
})
})
public class Movie implements Comparable<Movie>{
List<Object[]> results = em.createNativeQuery(
"select " +
"m.id, m.minutes, m.rating, m.release_date, m.title, m.director_id, " +
"dir.person_id, " +
"p.id as p_id, " + //NOTICE: the alias for PERSON.ID
"p.first_name, p.last_name, p.birth_date " +
"from queryex_movie m " +
"join queryex_director dir on dir.person_id = m.director_id " +
"join queryex_person p on p.id = dir.person_id " +
"where p.first_name = 'Ron'" +
"order by title ASC", "Movie.movieMapping2").getResultList();
$ mvn clean test -Ph2db -Dtest=myorg.queryex.SQLQueryTest#testSQLMultiResultMapping2 ... -*** testSQLMultiResultMapping *** Hibernate: select m.id, m.minutes, m.rating, m.release_date, m.title, m.director_id, dir.person_id, p.id as p_id, p.first_name, p.last_name, p.birth_date from queryex_movie m join queryex_director dir on dir.person_id = m.director_id join queryex_person p on p.id = dir.person_id where p.first_name = 'Ron' order by title ASC -query returned 2 results -em.contains(Apollo 13 (1995))=true -em.contains(Ron Howard)=true -em.contains(Ron Howard)=true -em.contains(Tremors (1990))=true -em.contains(Ron Underwood)=true -em.contains(Ron Underwood)=true [INFO] BUILD SUCCESS
log.debug("checking unmapped entity name");
assertEquals("unexpected director first name",
"Ron", ((Movie)((Object[])results.get(0))[0]).getDirector().getPerson().getFirstName());
$ mvn clean test -Ph2db -Dtest=myorg.queryex.SQLQueryTest#testSQLMultiResultMapping2 -query returned 2 results -em.contains(Apollo 13 (1995))=true -em.contains(Ron Howard)=true -em.contains(Ron Howard)=true -em.contains(Tremors (1990))=true -em.contains(Ron Underwood)=true -em.contains(Ron Underwood)=true -checking unmapped entity name ... [INFO] BUILD SUCCESS
You have completed querying the main points for querying for entities using native SQL.