From c27f31a107aa9a9a94dedb2281a02a36c4c9b134 Mon Sep 17 00:00:00 2001 From: Pinaki Poddar Date: Wed, 4 Feb 2009 22:06:47 +0000 Subject: [PATCH] OPENJPA-899: Initial support for new JPA 2.0 Query.getHints() API method. git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@740911 13f79535-47bb-0310-9956-ffa450edef68 --- .../kernel/DelegatingFetchConfiguration.java | 13 +- .../openjpa/kernel/FetchConfiguration.java | 8 + .../kernel/FetchConfigurationImpl.java | 11 + .../jdbc/common/apps/mappingApp/Entity1.java | 7 +- .../jdbc/mapping/TestNativeQueries.java | 268 +++++++++--------- .../apache/openjpa/persistence/QueryImpl.java | 8 +- 6 files changed, 169 insertions(+), 146 deletions(-) diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java index dcbe31ecc..36acce7bc 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java @@ -19,6 +19,7 @@ package org.apache.openjpa.kernel; import java.util.Collection; +import java.util.Map; import java.util.Set; import org.apache.openjpa.lib.rop.ResultList; @@ -436,8 +437,16 @@ public class DelegatingFetchConfiguration return _fetch.getHint(name); } catch (RuntimeException re) { throw translate(re); - } - } + } + } + + public Map getHints() { + try { + return _fetch.getHints(); + } catch (RuntimeException re) { + throw translate(re); + } + } public int requiresFetch(FieldMetaData fmd) { try { diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java index 1fdfce43a..0012a9664 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java @@ -20,6 +20,7 @@ package org.apache.openjpa.kernel; import java.io.Serializable; import java.util.Collection; +import java.util.Map; import java.util.Set; import org.apache.openjpa.lib.rop.ResultList; @@ -305,6 +306,13 @@ public interface FetchConfiguration * @since 0.4.0 */ public Object getHint (String name); + + /** + * Returns an immutable view of the currently active hints and their values. + * + * @since 2.0.0 + */ + public Map getHints(); /** * Root classes for recursive operations. This set is not thread safe. diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java index b9d2fc07c..a1a355567 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java @@ -29,6 +29,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import org.apache.commons.lang.StringUtils; import org.apache.openjpa.conf.OpenJPAConfiguration; @@ -463,6 +464,16 @@ public class FetchConfigurationImpl public Object getHint(String name) { return (_state.hints == null) ? null : _state.hints.get(name); } + + public Map getHints() { + if (_state.hints == null) + return (Map)Collections.EMPTY_MAP; + Map result = new TreeMap(); + for (Object key : _state.hints.keySet()) { + result.put(key.toString(), _state.hints.get(key)); + } + return result; + } public Set getRootClasses() { return (_state.rootClasses == null) ? Collections.EMPTY_SET diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/common/apps/mappingApp/Entity1.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/common/apps/mappingApp/Entity1.java index adc2b7a34..64d0feef0 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/common/apps/mappingApp/Entity1.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/common/apps/mappingApp/Entity1.java @@ -28,7 +28,9 @@ import javax.persistence.EntityResult; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; +import javax.persistence.NamedNativeQuery; import javax.persistence.OneToOne; +import javax.persistence.QueryHint; import javax.persistence.SqlResultSetMapping; import javax.persistence.Table; import javax.persistence.Version; @@ -37,7 +39,10 @@ import javax.persistence.Version; @Entity @Table(name="entity_1") @Inheritance(strategy=InheritanceType.JOINED) -@SqlResultSetMapping(name="NativeTestResult", entities=@EntityResult(entityClass=Entity1.class)) +@SqlResultSetMapping(name="NativeTestResult", + entities=@EntityResult(entityClass=Entity1.class)) +@NamedNativeQuery(name="SQLWithHints", query="SELECT * FROM ENTITY_1", + hints={@QueryHint(name="XYZ", value="abc")}) public class Entity1 implements Serializable { private static final long serialVersionUID = 2882935803066041165L; diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/TestNativeQueries.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/TestNativeQueries.java index 809b8c7bf..d0afc8dcc 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/TestNativeQueries.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/TestNativeQueries.java @@ -18,153 +18,139 @@ */ package org.apache.openjpa.persistence.jdbc.mapping; +import java.util.List; -import javax.persistence.*; -import java.util.*; +import javax.persistence.EntityManager; +import javax.persistence.Query; -import org.apache.openjpa.persistence.jdbc.common.apps.mappingApp.*; -import org.apache.openjpa.persistence.common.utils.*; -import junit.framework.*; +import org.apache.openjpa.kernel.QueryLanguages; +import org.apache.openjpa.persistence.OpenJPAPersistence; +import org.apache.openjpa.persistence.jdbc.common.apps.mappingApp.Entity1; +import org.apache.openjpa.persistence.jdbc.common.apps.mappingApp.Entity2; +import org.apache.openjpa.persistence.test.SingleEMFTestCase; +public class TestNativeQueries extends SingleEMFTestCase { + private static final String TABLE_NAME = "entity_1"; + private static final String CONST_NAME = "testSimple"; + private static final int CONST_INT = 42; + + private EntityManager em; + + public void setUp() { + super.setUp(CLEAR_TABLES, Entity1.class, Entity2.class); -public class TestNativeQueries extends AbstractTestCase -{ - - public TestNativeQueries(String name) - { - super(name, "jdbccactusapp"); - } + em = emf.createEntityManager(); + em.getTransaction().begin(); + em.persist(new Entity1(1, CONST_NAME, CONST_INT)); + em.persist(new Entity1(2, CONST_NAME+" Changed", CONST_INT+1)); + em.persist(new Entity1(3, CONST_NAME+" Changed 2", CONST_INT+2)); + em.getTransaction().commit(); + em.getTransaction().begin(); + } - public void setUp () - { - deleteAll (Entity1.class); - } + public void testNoParameter() { + String sql = "SELECT * FROM " + TABLE_NAME; + assertSize(3, em.createNativeQuery(sql, Entity1.class).getResultList()); + } + + public void testLiteral() { + String sql = "SELECT * FROM " + TABLE_NAME + + " WHERE INTFIELD = " + CONST_INT; + assertSize(1, em.createNativeQuery(sql, Entity1.class).getResultList()); + } + + public void testParameter() { + String sql = "SELECT * FROM " + TABLE_NAME + + " WHERE INTFIELD = ?1"; + assertSize(1, em.createNativeQuery(sql, Entity1.class) + .setParameter(1, CONST_INT) + .getResultList()); + } + + public void testOutOfOrderParameter() { + String sql = "SELECT * FROM " + TABLE_NAME + + " WHERE INTFIELD = ?2 AND STRINGFIELD = ?1"; + assertSize(1, em.createNativeQuery(sql, Entity1.class) + .setParameter(2, CONST_INT) + .setParameter(1, CONST_NAME) + .getResultList()); + } + + public void testDuplicateParameter() { + String sql = "SELECT * FROM " + TABLE_NAME + + " WHERE INTFIELD = ?1 AND INTFIELD = ?1"; + assertSize(1, em.createNativeQuery(sql, Entity1.class) + .setParameter(1, CONST_INT) + .getResultList()); + } + + public void testDifferentParameterToSameField() { + String sql = "SELECT * FROM " + TABLE_NAME + + " WHERE INTFIELD = ?1 OR INTFIELD = ?2"; + assertSize(2, em.createNativeQuery(sql, Entity1.class) + .setParameter(1, CONST_INT) + .setParameter(2, CONST_INT+1) + .getResultList()); + } - public void testSimple () - { - deleteAll (Entity1.class); + public void testQuoteParameterIgnored() { + String sql = "SELECT * FROM " + TABLE_NAME + + " WHERE INTFIELD = ?1 OR STRINGFIELD = '?2'"; + assertSize(1, em.createNativeQuery(sql, Entity1.class) + .setParameter(1, CONST_INT) + .getResultList()); + } + + public void testParameterMarkerWithoutSpaces() { + String sql = "SELECT * FROM " + TABLE_NAME + + " WHERE INTFIELD=?1"; + assertSize(1, em.createNativeQuery(sql, Entity1.class) + .setParameter(1, CONST_INT) + .getResultList()); + } + + public void testZeroBasedParameterSettingFails() { + try { + String sql = "SELECT * FROM " + TABLE_NAME + + " WHERE INTFIELD = ?1"; + em.createNativeQuery(sql, Entity1.class) + .setParameter(0, 12); + fail("Expected to fail with 0 parameter index"); + } catch (Exception e) { + // as expected + } + } - // test create - { - EntityManager em = currentEntityManager( ); - startTx(em); - em.persist (new Entity1 (0, "testSimple", 12)); - endTx(em); - endEm(em); - } + public void testNamedParameterFails() { + /* + * Named parameters are not supported according to Section 3.6.8 of + * JPA 2.0 (pp 100) public draft Oct 31, 2008: + * "The use of named parameters is not defined for native queries. + * Only positional parameter binding for SQL queries may be used by + * portable applications." + */ + String sql = "SELECT * FROM " + TABLE_NAME + " WHERE INTFIELD = :p"; + try { + em.createNativeQuery (sql, Entity1.class) + .setParameter ("p", 12); + fail("Expected to fail with NAMED parameter"); + } catch (IllegalArgumentException ex) { + // good + } + } + + public void testHintsAreProcessed() { + Query q = em.createNamedQuery("SQLWithHints"); + assertEquals(QueryLanguages.LANG_SQL, + OpenJPAPersistence.cast(q).getLanguage()); + String hintKey = "XYZ"; + assertTrue(q.getHints().containsKey(hintKey)); + assertEquals("abc", q.getHints().get(hintKey)); + + } - // test Query - { -/* JDBCConfiguration conf = (JDBCConfiguration)getConfiguration (); - DBDictionary dict = conf.getDBDictionaryInstance ();*/ - -/* String tableName = dict.getFullName (conf.getMappingRepository (). - getMapping (Entity1.class, getClass ().getClassLoader (), true). - getTable (), false);*/ - - EntityManager em = currentEntityManager( ); - startTx(em); - String tableName = "entity_1"; - assertSize (1, em.createNativeQuery - ("SELECT * FROM " + tableName, Entity1.class). - getResultList ()); - assertSize (1, em.createNativeQuery ("SELECT * FROM " + tableName - + " WHERE INTFIELD = 12", Entity1.class). - getResultList ()); - - assertSize (1, em.createNativeQuery ("SELECT * FROM " + tableName - + " WHERE INTFIELD = ?1", Entity1.class). - setParameter (1, 12). - getResultList ()); - - // make sure that out-of-order parameters work - assertSize (1, em.createNativeQuery ("SELECT * FROM " + tableName - + " WHERE INTFIELD = ?2 AND STRINGFIELD = ?1", Entity1.class). - setParameter (2, 12). - setParameter (1, "testSimple"). - getResultList ()); - - // make sure duplicate parameters work - assertSize (1, em.createNativeQuery ("SELECT * FROM " + tableName - + " WHERE INTFIELD = ?1 AND INTFIELD = ?1", Entity1.class). - setParameter (1, 12). - getResultList ()); - - assertSize (1, em.createNativeQuery ("SELECT * FROM " + tableName - + " WHERE INTFIELD = ?1 OR INTFIELD = ?2", Entity1.class). - setParameter (1, 12). - setParameter (2, 13). - getResultList ()); - - // make sure that quoted parameters are ignored as expected - assertSize (1, em.createNativeQuery ("SELECT * FROM " + tableName - + " WHERE INTFIELD = ?1 OR STRINGFIELD = '?5'", Entity1.class). - setParameter (1, 12). - getResultList ()); - - // test without spaces - assertSize (1, em.createNativeQuery ("SELECT * FROM " + tableName - + " WHERE INTFIELD=?1 OR STRINGFIELD='?5'", Entity1.class). - setParameter (1, 12). - getResultList ()); - -/* assertSize (1, ((QueryImpl)em.createNativeQuery - ("SELECT * FROM " + tableName - + " WHERE INTFIELD = ?1 OR INTFIELD = ?2", Entity1.class)). - setParameters (12, 1). - getResultList ()); - - assertSize (0, ((QueryImpl)em.createNativeQuery - ("SELECT * FROM " + tableName - + " WHERE INTFIELD = ?1 AND INTFIELD = ?2", Entity1.class)). - setParameters (12, 1). - getResultList ()); -*/ - assertSize (0, em.createNativeQuery ("SELECT * FROM " + tableName - + " WHERE INTFIELD = ?1 AND INTFIELD = ?2", Entity1.class). - setParameter (1, 12). - setParameter (2, 13). - getResultList ()); - - try - { - em.createNativeQuery ("SELECT * FROM " + tableName - + " WHERE INTFIELD = ?1", Entity1.class). - setParameter (0, 12). - getResultList (); - fail ("Should not have been able to use param index 0"); - } - catch (Exception e) - { - // as expected - } - - - /* - * Named parameters are not supported according to 19 June 3.5.2: - * - * The use of named parameters is not defined for - * native queries. Only positional parameter binding - * for SQL queries may be used by portable applications. - * - assertSize (1, em.createNativeQuery ("SELECT * FROM " + tableName - + " WHERE INTFIELD = :p", Entity1.class). - setParameter ("p", 12). - getResultList ()); - assertSize (1, em.createNativeQuery ("SELECT * FROM " + tableName - + " WHERE INTFIELD = :p OR INTFIELD = :p", Entity1.class). - setParameter ("p", 12). - getResultList ()); - */ - - endTx(em); - endEm(em); - } - } - - public boolean assertSize(int num, List l) - { - return(num == l.size()); - } + public void assertSize(int num, List l) { + assertNotNull(l); + assertEquals(num, l.size()); + } } - diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java index 0c9edf09c..21d628ef5 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java @@ -601,9 +601,13 @@ public class QueryImpl implements OpenJPAQuerySPI, Serializable { return _query.equals(((QueryImpl) other)._query); } + /** + * Get all the active hints and their values. + * + */ + //TODO: JPA 2.0 Hints that are not set to FetchConfiguration public Map getHints() { - throw new UnsupportedOperationException( - "JPA 2.0 - Method not yet implemented"); + return _query.getFetchConfiguration().getHints(); } public LockModeType getLockMode() {