diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/NamedSQLQueryDefinition.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/NamedSQLQueryDefinition.java index b3f675be17..2edc621a29 100755 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/NamedSQLQueryDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/NamedSQLQueryDefinition.java @@ -47,6 +47,7 @@ public class NamedSQLQueryDefinition extends NamedQueryDefinition { * code when a the result-set mapping information is explicitly * provided in the query definition (i.e., no resultset-mapping used) * + * @param name The name of named query * @param query The sql query string * @param queryReturns The in-lined query return definitions * @param querySpaces Any specified query spaces (used for auto-flushing) @@ -98,6 +99,7 @@ public class NamedSQLQueryDefinition extends NamedQueryDefinition { * This form used to construct a NamedSQLQueryDefinition from the binder * code when a resultset-mapping reference is used. * + * @param name The name of named query * @param query The sql query string * @param resultSetRef The resultset-mapping name * @param querySpaces Any specified query spaces (used for auto-flushing) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/global/QueryBinder.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/global/QueryBinder.java index 660843ff7a..1dfbcc98b8 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/global/QueryBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/global/QueryBinder.java @@ -23,13 +23,14 @@ */ package org.hibernate.metamodel.source.annotations.global; +import java.util.HashMap; import javax.persistence.NamedNativeQueries; import javax.persistence.NamedNativeQuery; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; -import java.util.HashMap; import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationValue; import org.jboss.jandex.Index; import org.jboss.logging.Logger; @@ -56,11 +57,14 @@ public class QueryBinder { QueryBinder.class.getName() ); + private QueryBinder() { + } + /** - * Binds all {@link NamedQuery}, {@link NamedQueries}, {@link NamedNativeQuery}, {{@link NamedNativeQueries}, - * {@link org.hibernate.annotations.NamedQuery} , {@link org.hibernate.annotations.NamedQueries}, - * {@link org.hibernate.annotations.NamedNativeQuery}, and {@link org.hibernate.annotations.NamedNativeQueries} annotations to - * the supplied metadata. + * Binds all {@link NamedQuery}, {@link NamedQueries}, {@link NamedNativeQuery}, {@link NamedNativeQueries}, + * {@link org.hibernate.annotations.NamedQuery}, {@link org.hibernate.annotations.NamedQueries}, + * {@link org.hibernate.annotations.NamedNativeQuery}, and {@link org.hibernate.annotations.NamedNativeQueries} + * annotations to the supplied metadata. * * @param metadata the global metadata * @param jandex the jandex index @@ -100,29 +104,42 @@ public class QueryBinder { } } + /** + * Binds {@link javax.persistence.NamedQuery} as well as {@link org.hibernate.annotations.NamedQuery}. + * + * @param metadata the current metadata + * @param annotation the named query annotation + */ private static void bindNamedQuery(MetadataImplementor metadata, AnnotationInstance annotation) { String name = JandexHelper.getValueAsString( annotation, "name" ); if ( StringHelper.isEmpty( name ) ) { throw new AnnotationException( "A named query must have a name when used in class or package level" ); } + String query = JandexHelper.getValueAsString( annotation, "query" ); + AnnotationInstance[] hints = JandexHelper.getValueAsArray( annotation, "hints" ); + String cacheRegion = getString( hints, QueryHints.CACHE_REGION ); if ( StringHelper.isEmpty( cacheRegion ) ) { cacheRegion = null; } + Integer timeout = getTimeout( hints, query ); if ( timeout != null && timeout < 0 ) { timeout = null; } + Integer fetchSize = getInteger( hints, QueryHints.FETCH_SIZE, name ); if ( fetchSize != null && fetchSize < 0 ) { fetchSize = null; } + String comment = getString( hints, QueryHints.COMMENT ); if ( StringHelper.isEmpty( comment ) ) { comment = null; } + metadata.addNamedQuery( new NamedQueryDefinition( name, @@ -140,29 +157,39 @@ public class QueryBinder { if ( StringHelper.isEmpty( name ) ) { throw new AnnotationException( "A named native query must have a name when used in class or package level" ); } + String query = JandexHelper.getValueAsString( annotation, "query" ); + String resultSetMapping = JandexHelper.getValueAsString( annotation, "resultSetMapping" ); + AnnotationInstance[] hints = JandexHelper.getValueAsArray( annotation, "hints" ); + boolean cacheable = getBoolean( hints, "org.hibernate.cacheable", name ); String cacheRegion = getString( hints, QueryHints.CACHE_REGION ); if ( StringHelper.isEmpty( cacheRegion ) ) { cacheRegion = null; } + Integer timeout = getTimeout( hints, query ); if ( timeout != null && timeout < 0 ) { timeout = null; } + Integer fetchSize = getInteger( hints, QueryHints.FETCH_SIZE, name ); if ( fetchSize != null && fetchSize < 0 ) { fetchSize = null; } + FlushMode flushMode = getFlushMode( hints, QueryHints.FLUSH_MODE, name ); CacheMode cacheMode = getCacheMode( hints, QueryHints.CACHE_MODE, name ); + boolean readOnly = getBoolean( hints, QueryHints.READ_ONLY, name ); + String comment = getString( hints, QueryHints.COMMENT ); if ( StringHelper.isEmpty( comment ) ) { comment = null; } + boolean callable = getBoolean( hints, QueryHints.CALLABLE, name ); NamedSQLQueryDefinition def; if ( StringHelper.isNotEmpty( resultSetMapping ) ) { @@ -175,32 +202,40 @@ public class QueryBinder { ); } else { - String resultClass = JandexHelper.getValueAsString( annotation, "resultClass" ); - if ( void.class.equals( resultClass ) ) { + AnnotationValue annotationValue = annotation.value( "resultClass" ); + if ( annotationValue == null ) { throw new NotYetImplementedException( "Pure native scalar queries are not yet supported" ); } - def = new NamedSQLQueryDefinition( - name, - query, new NativeSQLQueryRootReturn[] { + NativeSQLQueryRootReturn queryRoots[] = new NativeSQLQueryRootReturn[] { new NativeSQLQueryRootReturn( "alias1", - resultClass, - new HashMap(), + annotationValue.asString(), + new HashMap(), LockMode.READ ) - }, - null, cacheable, cacheRegion, timeout, fetchSize, flushMode, cacheMode, readOnly, - comment, null, callable + }; + def = new NamedSQLQueryDefinition( + name, + query, + queryRoots, + null, + cacheable, + cacheRegion, + timeout, + fetchSize, + flushMode, + cacheMode, + readOnly, + comment, + null, + callable ); - } metadata.addNamedNativeQuery( def ); LOG.debugf( "Binding named native query: %s => %s", name, query ); } - private static boolean getBoolean(AnnotationInstance[] hints, - String element, - String query) { + private static boolean getBoolean(AnnotationInstance[] hints, String element, String query) { String val = getString( hints, element ); if ( val == null || val.equalsIgnoreCase( "false" ) ) { return false; @@ -211,9 +246,7 @@ public class QueryBinder { throw new AnnotationException( "Not a boolean in hint: " + query + ":" + element ); } - private static CacheMode getCacheMode(AnnotationInstance[] hints, - String element, - String query) { + private static CacheMode getCacheMode(AnnotationInstance[] hints, String element, String query) { String val = getString( hints, element ); if ( val == null ) { return null; @@ -236,9 +269,7 @@ public class QueryBinder { throw new AnnotationException( "Unknown CacheMode in hint: " + query + ":" + element ); } - private static FlushMode getFlushMode(AnnotationInstance[] hints, - String element, - String query) { + private static FlushMode getFlushMode(AnnotationInstance[] hints, String element, String query) { String val = getString( hints, element ); if ( val == null ) { return null; @@ -261,12 +292,9 @@ public class QueryBinder { else { throw new AnnotationException( "Unknown FlushMode in hint: " + query + ":" + element ); } - } - private static Integer getInteger(AnnotationInstance[] hints, - String element, - String query) { + private static Integer getInteger(AnnotationInstance[] hints, String element, String query) { String val = getString( hints, element ); if ( val == null ) { return null; @@ -279,8 +307,7 @@ public class QueryBinder { } } - private static String getString(AnnotationInstance[] hints, - String element) { + private static String getString(AnnotationInstance[] hints, String element) { for ( AnnotationInstance hint : hints ) { if ( element.equals( JandexHelper.getValue( hint, "name" ) ) ) { return JandexHelper.getValueAsString( hint, "value" ); @@ -289,15 +316,11 @@ public class QueryBinder { return null; } - private static Integer getTimeout(AnnotationInstance[] hints, - String query) { + private static Integer getTimeout(AnnotationInstance[] hints, String query) { Integer timeout = getInteger( hints, QueryHints.TIMEOUT_JPA, query ); if ( timeout == null ) { return getInteger( hints, QueryHints.TIMEOUT_HIBERNATE, query ); // timeout is already in seconds } - return new Integer( (int) Math.round( timeout.doubleValue() / 1000.0 ) ); // convert milliseconds to seconds - } - - private QueryBinder() { + return ( ( timeout + 500 ) / 1000 ); // convert milliseconds to seconds (rounded) } } diff --git a/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/global/QueryBinderTest.java b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/global/QueryBinderTest.java new file mode 100644 index 0000000000..9256059831 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/global/QueryBinderTest.java @@ -0,0 +1,97 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.metamodel.source.annotations.global; + +import javax.persistence.NamedNativeQuery; + +import org.jboss.jandex.Index; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.hibernate.cfg.NotYetImplementedException; +import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn; +import org.hibernate.engine.query.spi.sql.NativeSQLQueryRootReturn; +import org.hibernate.engine.spi.NamedSQLQueryDefinition; +import org.hibernate.metamodel.MetadataSources; +import org.hibernate.metamodel.source.annotations.util.JandexHelper; +import org.hibernate.metamodel.source.internal.MetadataImpl; +import org.hibernate.service.ServiceRegistryBuilder; +import org.hibernate.service.classloading.spi.ClassLoaderService; +import org.hibernate.service.internal.BasicServiceRegistryImpl; +import org.hibernate.testing.junit4.BaseUnitTestCase; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; + +/** + * @author Hardy Ferentschik + */ +public class QueryBinderTest extends BaseUnitTestCase { + + private BasicServiceRegistryImpl serviceRegistry; + private ClassLoaderService service; + private MetadataImpl meta; + + @Before + public void setUp() { + serviceRegistry = (BasicServiceRegistryImpl) new ServiceRegistryBuilder().buildServiceRegistry(); + service = serviceRegistry.getService( ClassLoaderService.class ); + meta = (MetadataImpl) new MetadataSources( serviceRegistry ).buildMetadata(); + } + + @After + public void tearDown() { + serviceRegistry.destroy(); + } + + @Test(expected = NotYetImplementedException.class) + public void testNoResultClass() { + @NamedNativeQuery(name = "fubar", query = "SELECT * FROM FOO") + class Foo { + } + Index index = JandexHelper.indexForClass( service, Foo.class ); + QueryBinder.bind( meta, index ); + } + + @Test + public void testResultClass() { + @NamedNativeQuery(name = "fubar", query = "SELECT * FROM FOO", resultClass = Foo.class) + class Foo { + } + Index index = JandexHelper.indexForClass( service, Foo.class ); + QueryBinder.bind( meta, index ); + + NamedSQLQueryDefinition namedQuery = meta.getNamedNativeQuery( "fubar" ); + assertNotNull( namedQuery ); + NativeSQLQueryReturn queryReturns[] = namedQuery.getQueryReturns(); + assertTrue( "Wrong number of returns", queryReturns.length == 1 ); + assertTrue( "Wrong query return type", queryReturns[0] instanceof NativeSQLQueryRootReturn ); + NativeSQLQueryRootReturn rootReturn = (NativeSQLQueryRootReturn) queryReturns[0]; + assertEquals( "Wrong result class", Foo.class.getName(), rootReturn.getReturnEntityName() ); + } +} + +