HHH-8246 Implement XML binding of NamedStoredProcedureQuery

This commit is contained in:
Strong Liu 2013-05-20 20:37:16 -07:00
parent ce77f15974
commit a03d44f290
16 changed files with 522 additions and 207 deletions

View File

@ -272,7 +272,7 @@ public final class AnnotationBinder {
(List<NamedStoredProcedureQuery>) defaults.get( NamedStoredProcedureQuery.class );
if ( annotations != null ) {
for ( NamedStoredProcedureQuery annotation : annotations ) {
QueryBinder.bindNamedStoredProcedureQuery( annotation, mappings );
bindNamedStoredProcedureQuery( mappings, annotation );
}
}
}
@ -281,9 +281,7 @@ public final class AnnotationBinder {
(List<NamedStoredProcedureQueries>) defaults.get( NamedStoredProcedureQueries.class );
if ( annotations != null ) {
for ( NamedStoredProcedureQueries annotation : annotations ) {
for ( NamedStoredProcedureQuery queryAnnotation : annotation.value() ) {
QueryBinder.bindNamedStoredProcedureQuery( queryAnnotation, mappings );
}
bindNamedStoredProcedureQueries( mappings, annotation );
}
}
}
@ -394,24 +392,29 @@ public final class AnnotationBinder {
}
// NamedStoredProcedureQuery handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
final NamedStoredProcedureQuery annotation = annotatedElement.getAnnotation( NamedStoredProcedureQuery.class );
if ( annotation != null ) {
QueryBinder.bindNamedStoredProcedureQuery( annotation, mappings );
}
}
bindNamedStoredProcedureQuery( mappings, annotatedElement.getAnnotation( NamedStoredProcedureQuery.class ) );
// NamedStoredProcedureQueries handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
final NamedStoredProcedureQueries annotation = annotatedElement.getAnnotation( NamedStoredProcedureQueries.class );
if ( annotation != null ) {
for ( NamedStoredProcedureQuery queryAnnotation : annotation.value() ) {
QueryBinder.bindNamedStoredProcedureQuery( queryAnnotation, mappings );
}
bindNamedStoredProcedureQueries(
mappings,
annotatedElement.getAnnotation( NamedStoredProcedureQueries.class )
);
}
private static void bindNamedStoredProcedureQueries(Mappings mappings, NamedStoredProcedureQueries annotation) {
if ( annotation != null ) {
for ( NamedStoredProcedureQuery queryAnnotation : annotation.value() ) {
bindNamedStoredProcedureQuery( mappings, queryAnnotation );
}
}
}
private static void bindNamedStoredProcedureQuery(Mappings mappings, NamedStoredProcedureQuery annotation) {
if ( annotation != null ) {
QueryBinder.bindNamedStoredProcedureQuery( annotation, mappings );
}
}
private static IdGenerator buildIdGenerator(java.lang.annotation.Annotation ann, Mappings mappings) {
IdGenerator idGen = new IdGenerator();
if ( mappings.getSchemaName() != null ) {

View File

@ -303,7 +303,7 @@ public class Configuration implements Serializable {
namedSqlQueries = new HashMap<String,NamedSQLQueryDefinition>();
sqlResultSetMappings = new HashMap<String, ResultSetMappingDefinition>();
namedEntityGraphMap = new HashMap<String, NamedEntityGraphDefinition>();
namedProcedureCallMap = new HashMap<String, NamedProcedureCallDefinition>( );
typeDefs = new HashMap<String,TypeDef>();
filterDefinitions = new HashMap<String, FilterDefinition>();
fetchProfiles = new HashMap<String, FetchProfile>();

View File

@ -25,25 +25,20 @@ package org.hibernate.cfg.annotations;
import javax.persistence.NamedStoredProcedureQuery;
import javax.persistence.ParameterMode;
import javax.persistence.QueryHint;
import javax.persistence.StoredProcedureParameter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.engine.ResultSetMappingDefinition;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryRootReturn;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.procedure.ProcedureCallMemento;
import org.hibernate.procedure.internal.ParameterStrategy;
import org.hibernate.procedure.internal.ProcedureCallMementoImpl;
@ -65,7 +60,7 @@ public class NamedProcedureCallDefinition {
private final Class[] resultClasses;
private final String[] resultSetMappings;
private final ParameterDefinitions parameterDefinitions;
private final Map<String,Object> hints;
private final Map<String, Object> hints;
NamedProcedureCallDefinition(NamedStoredProcedureQuery annotation) {
this.registeredName = annotation.name();
@ -73,7 +68,7 @@ public class NamedProcedureCallDefinition {
this.resultClasses = annotation.resultClasses();
this.resultSetMappings = annotation.resultSetMappings();
this.parameterDefinitions = new ParameterDefinitions( annotation.parameters() );
this.hints = extract( annotation.hints() );
this.hints = new QueryHintDefinition( annotation.hints() ).getHintsMap();
final boolean specifiesResultClasses = resultClasses != null && resultClasses.length > 0;
final boolean specifiesResultSetMappings = resultSetMappings != null && resultSetMappings.length > 0;
@ -88,17 +83,6 @@ public class NamedProcedureCallDefinition {
}
}
private Map<String, Object> extract(QueryHint[] hints) {
if ( hints == null || hints.length == 0 ) {
return Collections.emptyMap();
}
final Map<String,Object> hintsMap = new HashMap<String, Object>();
for ( QueryHint hint : hints ) {
hintsMap.put( hint.name(), hint.value() );
}
return hintsMap;
}
public String getRegisteredName() {
return registeredName;
}
@ -201,7 +185,7 @@ public class NamedProcedureCallDefinition {
public List<ParameterMemento> toMementos(SessionFactoryImpl sessionFactory) {
final List<ParameterMemento> mementos = new ArrayList<ParameterMemento>();
for ( ParameterDefinition definition : parameterDefinitions ) {
definition.toMemento( sessionFactory );
mementos.add(definition.toMemento( sessionFactory ));
}
return mementos;
}

View File

@ -23,13 +23,11 @@
*/
package org.hibernate.cfg.annotations;
import java.util.HashMap;
import javax.persistence.LockModeType;
import javax.persistence.NamedNativeQueries;
import javax.persistence.NamedNativeQuery;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.NamedStoredProcedureQuery;
import javax.persistence.QueryHint;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.SqlResultSetMappings;
@ -40,7 +38,6 @@ import org.hibernate.AssertionFailure;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.annotations.CacheModeType;
import org.hibernate.annotations.FlushModeType;
import org.hibernate.annotations.QueryHints;
@ -54,7 +51,6 @@ import org.hibernate.engine.spi.NamedQueryDefinitionBuilder;
import org.hibernate.engine.spi.NamedSQLQueryDefinition;
import org.hibernate.engine.spi.NamedSQLQueryDefinitionBuilder;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.LockModeConverter;
/**
* Query binder
@ -70,19 +66,19 @@ public abstract class QueryBinder {
throw new AnnotationException( "A named query must have a name when used in class or package level" );
}
//EJBQL Query
QueryHint[] hints = queryAnn.hints();
QueryHintDefinition hints = new QueryHintDefinition( queryAnn.hints() );
String queryName = queryAnn.query();
NamedQueryDefinition queryDefinition = new NamedQueryDefinitionBuilder( queryAnn.name() )
.setLockOptions( determineLockOptions( queryAnn, hints ) )
.setLockOptions( hints.determineLockOptions( queryAnn ) )
.setQuery( queryName )
.setCacheable( getBoolean( queryName, "org.hibernate.cacheable", hints ) )
.setCacheRegion( getString( queryName, "org.hibernate.cacheRegion", hints ) )
.setTimeout( getTimeout( queryName, hints ) )
.setFetchSize( getInteger( queryName, "org.hibernate.fetchSize", hints ) )
.setFlushMode( getFlushMode( queryName, hints ) )
.setCacheMode( getCacheMode( queryName, hints ) )
.setReadOnly( getBoolean( queryName, "org.hibernate.readOnly", hints ) )
.setComment( getString( queryName, "org.hibernate.comment", hints ) )
.setCacheable( hints.getBoolean( queryName, QueryHints.CACHEABLE ) )
.setCacheRegion( hints.getString( queryName, QueryHints.CACHE_REGION ) )
.setTimeout( hints.getTimeout( queryName ) )
.setFetchSize( hints.getInteger( queryName, QueryHints.FETCH_SIZE ) )
.setFlushMode( hints.getFlushMode( queryName ) )
.setCacheMode( hints.getCacheMode( queryName ) )
.setReadOnly( hints.getBoolean( queryName, QueryHints.READ_ONLY ) )
.setComment( hints.getString( queryName, QueryHints.COMMENT ) )
.setParameterTypes( null )
.createNamedQueryDefinition();
@ -97,17 +93,7 @@ public abstract class QueryBinder {
}
}
private static LockOptions determineLockOptions(NamedQuery namedQueryAnnotation, QueryHint[] hints) {
LockModeType lockModeType = namedQueryAnnotation.lockMode();
Integer lockTimeoutHint = getInteger( namedQueryAnnotation.name(), "javax.persistence.lock.timeout", hints );
LockOptions lockOptions = new LockOptions( LockModeConverter.convertToLockMode( lockModeType ) );
if ( lockTimeoutHint != null ) {
lockOptions.setTimeOut( lockTimeoutHint );
}
return lockOptions;
}
public static void bindNativeQuery(NamedNativeQuery queryAnn, Mappings mappings, boolean isDefault) {
if ( queryAnn == null ) return;
@ -116,22 +102,22 @@ public abstract class QueryBinder {
throw new AnnotationException( "A named query must have a name when used in class or package level" );
}
String resultSetMapping = queryAnn.resultSetMapping();
QueryHint[] hints = queryAnn.hints();
QueryHintDefinition hints = new QueryHintDefinition( queryAnn.hints() );
String queryName = queryAnn.query();
NamedSQLQueryDefinitionBuilder builder = new NamedSQLQueryDefinitionBuilder( queryAnn.name() )
.setQuery( queryName )
.setQuerySpaces( null )
.setCacheable( getBoolean( queryName, "org.hibernate.cacheable", hints ) )
.setCacheRegion( getString( queryName, "org.hibernate.cacheRegion", hints ) )
.setTimeout( getTimeout( queryName, hints ) )
.setFetchSize( getInteger( queryName, "org.hibernate.fetchSize", hints ) )
.setFlushMode( getFlushMode( queryName, hints ) )
.setCacheMode( getCacheMode( queryName, hints ) )
.setReadOnly( getBoolean( queryName, "org.hibernate.readOnly", hints ) )
.setComment( getString( queryName, "org.hibernate.comment", hints ) )
.setCacheable( hints.getBoolean( queryName, QueryHints.CACHEABLE ) )
.setCacheRegion( hints.getString( queryName, QueryHints.CACHE_REGION ) )
.setTimeout( hints.getTimeout( queryName ) )
.setFetchSize( hints.getInteger( queryName, QueryHints.FETCH_SIZE ) )
.setFlushMode( hints.getFlushMode( queryName ) )
.setCacheMode( hints.getCacheMode( queryName ) )
.setReadOnly( hints.getBoolean( queryName, QueryHints.READ_ONLY ) )
.setComment( hints.getString( queryName, QueryHints.COMMENT ) )
.setParameterTypes( null )
.setCallable( getBoolean( queryName, "org.hibernate.callable", hints ) );
.setCallable( hints.getBoolean( queryName, QueryHints.CALLABLE ) );
if ( !BinderHelper.isEmptyAnnotationValue( resultSetMapping ) ) {
//sql result set usage
@ -359,109 +345,5 @@ public abstract class QueryBinder {
mappings.addSecondPass( new ResultsetMappingSecondPass( ann, mappings, isDefault ) );
}
private static CacheMode getCacheMode(String query, QueryHint[] hints) {
for (QueryHint hint : hints) {
if ( "org.hibernate.cacheMode".equals( hint.name() ) ) {
if ( hint.value().equalsIgnoreCase( CacheMode.GET.toString() ) ) {
return CacheMode.GET;
}
else if ( hint.value().equalsIgnoreCase( CacheMode.IGNORE.toString() ) ) {
return CacheMode.IGNORE;
}
else if ( hint.value().equalsIgnoreCase( CacheMode.NORMAL.toString() ) ) {
return CacheMode.NORMAL;
}
else if ( hint.value().equalsIgnoreCase( CacheMode.PUT.toString() ) ) {
return CacheMode.PUT;
}
else if ( hint.value().equalsIgnoreCase( CacheMode.REFRESH.toString() ) ) {
return CacheMode.REFRESH;
}
else {
throw new AnnotationException( "Unknown CacheMode in hint: " + query + ":" + hint.name() );
}
}
}
return null;
}
private static FlushMode getFlushMode(String query, QueryHint[] hints) {
for (QueryHint hint : hints) {
if ( "org.hibernate.flushMode".equals( hint.name() ) ) {
if ( hint.value().equalsIgnoreCase( FlushMode.ALWAYS.toString() ) ) {
return FlushMode.ALWAYS;
}
else if ( hint.value().equalsIgnoreCase( FlushMode.AUTO.toString() ) ) {
return FlushMode.AUTO;
}
else if ( hint.value().equalsIgnoreCase( FlushMode.COMMIT.toString() ) ) {
return FlushMode.COMMIT;
}
else if ( hint.value().equalsIgnoreCase( FlushMode.NEVER.toString() ) ) {
return FlushMode.MANUAL;
}
else if ( hint.value().equalsIgnoreCase( FlushMode.MANUAL.toString() ) ) {
return FlushMode.MANUAL;
}
else {
throw new AnnotationException( "Unknown FlushMode in hint: " + query + ":" + hint.name() );
}
}
}
return null;
}
private static boolean getBoolean(String query, String hintName, QueryHint[] hints) {
for (QueryHint hint : hints) {
if ( hintName.equals( hint.name() ) ) {
if ( hint.value().equalsIgnoreCase( "true" ) ) {
return true;
}
else if ( hint.value().equalsIgnoreCase( "false" ) ) {
return false;
}
else {
throw new AnnotationException( "Not a boolean in hint: " + query + ":" + hint.name() );
}
}
}
return false;
}
private static String getString(String query, String hintName, QueryHint[] hints) {
for (QueryHint hint : hints) {
if ( hintName.equals( hint.name() ) ) {
return hint.value();
}
}
return null;
}
private static Integer getInteger(String query, String hintName, QueryHint[] hints) {
for (QueryHint hint : hints) {
if ( hintName.equals( hint.name() ) ) {
try {
return Integer.decode( hint.value() );
}
catch (NumberFormatException nfe) {
throw new AnnotationException( "Not an integer in hint: " + query + ":" + hint.name(), nfe );
}
}
}
return null;
}
private static Integer getTimeout(String queryName, QueryHint[] hints) {
Integer timeout = getInteger( queryName, "javax.persistence.query.timeout", hints );
if ( timeout != null ) {
// convert milliseconds to seconds
timeout = (int)Math.round(timeout.doubleValue() / 1000.0 );
}
else {
// timeout is already in seconds
timeout = getInteger( queryName, "org.hibernate.timeout", hints );
}
return timeout;
}
}

View File

@ -0,0 +1,152 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.cfg.annotations;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.LockModeType;
import javax.persistence.NamedQuery;
import javax.persistence.QueryHint;
import org.hibernate.AnnotationException;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.annotations.QueryHints;
import org.hibernate.internal.util.LockModeConverter;
/**
* @author Strong Liu <stliu@hibernate.org>
*/
public class QueryHintDefinition {
private final Map<String, Object> hintsMap;
public QueryHintDefinition(final QueryHint[] hints) {
if ( hints == null || hints.length == 0 ) {
hintsMap = Collections.emptyMap();
}
else {
final Map<String, Object> hintsMap = new HashMap<String, Object>();
for ( QueryHint hint : hints ) {
hintsMap.put( hint.name(), hint.value() );
}
this.hintsMap = hintsMap;
}
}
public CacheMode getCacheMode(String query) {
String hitName = QueryHints.CACHE_MODE;
String value =(String) hintsMap.get( hitName );
if ( value == null ) {
return null;
}
try {
return CacheMode.interpretExternalSetting( value );
}
catch ( MappingException e ) {
throw new AnnotationException( "Unknown CacheMode in hint: " + query + ":" + hitName, e );
}
}
public FlushMode getFlushMode(String query) {
String hitName = QueryHints.FLUSH_MODE;
String value =(String) hintsMap.get( hitName );
if ( value == null ) {
return null;
}
try {
return FlushMode.interpretExternalSetting( value );
}
catch ( MappingException e ) {
throw new AnnotationException( "Unknown FlushMode in hint: " + query + ":" + hitName, e );
}
}
public boolean getBoolean(String query, String hintName) {
String value =(String) hintsMap.get( hintName );
if ( value == null ) {
return false;
}
if ( value.equalsIgnoreCase( "true" ) ) {
return true;
}
else if ( value.equalsIgnoreCase( "false" ) ) {
return false;
}
else {
throw new AnnotationException( "Not a boolean in hint: " + query + ":" + hintName );
}
}
public String getString(String query, String hintName) {
return (String) hintsMap.get( hintName );
}
public Integer getInteger(String query, String hintName) {
String value = (String) hintsMap.get( hintName );
if ( value == null ) {
return null;
}
try {
return Integer.decode( value );
}
catch ( NumberFormatException nfe ) {
throw new AnnotationException( "Not an integer in hint: " + query + ":" + hintName, nfe );
}
}
public Integer getTimeout(String queryName) {
Integer timeout = getInteger( queryName, QueryHints.TIMEOUT_JPA );
if ( timeout != null ) {
// convert milliseconds to seconds
timeout = (int) Math.round( timeout.doubleValue() / 1000.0 );
}
else {
// timeout is already in seconds
timeout = getInteger( queryName, QueryHints.TIMEOUT_HIBERNATE );
}
return timeout;
}
public LockOptions determineLockOptions(NamedQuery namedQueryAnnotation) {
LockModeType lockModeType = namedQueryAnnotation.lockMode();
Integer lockTimeoutHint = getInteger( namedQueryAnnotation.name(), "javax.persistence.lock.timeout" );
LockOptions lockOptions = new LockOptions( LockModeConverter.convertToLockMode( lockModeType ) );
if ( lockTimeoutHint != null ) {
lockOptions.setTimeOut( lockTimeoutHint );
}
return lockOptions;
}
public Map<String, Object> getHintsMap() {
return hintsMap;
}
}

View File

@ -33,6 +33,7 @@ import java.util.Map;
import javax.persistence.EntityListeners;
import javax.persistence.NamedNativeQuery;
import javax.persistence.NamedQuery;
import javax.persistence.NamedStoredProcedureQuery;
import javax.persistence.SequenceGenerator;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.TableGenerator;
@ -49,6 +50,7 @@ import org.hibernate.internal.util.ReflectHelper;
*
* @author Emmanuel Bernard
*/
@SuppressWarnings("unchecked")
public class JPAMetadataProvider implements MetadataProvider, Serializable {
private transient MetadataProvider delegate = new JavaMetadataProvider();
private transient Map<Object, Object> defaults;
@ -152,6 +154,16 @@ public class JPAMetadataProvider implements MetadataProvider, Serializable {
element, xmlDefaults
);
sqlResultSetMappings.addAll( currentSqlResultSetMappings );
List<NamedStoredProcedureQuery> namedStoredProcedureQueries = (List<NamedStoredProcedureQuery>)defaults.get( NamedStoredProcedureQuery.class );
if(namedStoredProcedureQueries==null){
namedStoredProcedureQueries = new ArrayList<NamedStoredProcedureQuery>( );
defaults.put( NamedStoredProcedureQuery.class, namedStoredProcedureQueries );
}
List<NamedStoredProcedureQuery> currentNamedStoredProcedureQueries = JPAOverriddenAnnotationReader.buildNamedStoreProcedureQueries(
element, xmlDefaults
);
namedStoredProcedureQueries.addAll( currentNamedStoredProcedureQueries );
}
}
return defaults;

View File

@ -92,10 +92,12 @@ import javax.persistence.NamedNativeQueries;
import javax.persistence.NamedNativeQuery;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.NamedStoredProcedureQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import javax.persistence.OrderColumn;
import javax.persistence.ParameterMode;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
@ -111,6 +113,7 @@ import javax.persistence.SecondaryTables;
import javax.persistence.SequenceGenerator;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.SqlResultSetMappings;
import javax.persistence.StoredProcedureParameter;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.persistence.Temporal;
@ -1743,6 +1746,84 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader {
}
}
public static List<NamedStoredProcedureQuery> buildNamedStoreProcedureQueries(Element element, XMLContext.Default defaults) {
if ( element == null ) {
return new ArrayList<NamedStoredProcedureQuery>();
}
List namedStoredProcedureElements = element.elements( "named-stored-procedure-query" );
List<NamedStoredProcedureQuery> namedStoredProcedureQueries = new ArrayList<NamedStoredProcedureQuery>();
for ( Object obj : namedStoredProcedureElements ) {
Element subElement = (Element) obj;
AnnotationDescriptor ann = new AnnotationDescriptor( NamedStoredProcedureQuery.class );
copyStringAttribute( ann, subElement, "name", true );
copyStringAttribute( ann, subElement, "procedure-name", true );
List<Element> elements = subElement.elements( "parameter" );
List<StoredProcedureParameter> storedProcedureParameters = new ArrayList<StoredProcedureParameter>();
for ( Element parameterElement : elements ) {
AnnotationDescriptor parameterDescriptor = new AnnotationDescriptor( StoredProcedureParameter.class );
copyStringAttribute( parameterDescriptor, parameterElement, "name", false );
String modeValue = parameterElement.attributeValue( "mode" );
if ( modeValue == null ) {
parameterDescriptor.setValue( "mode", ParameterMode.IN );
}
else {
parameterDescriptor.setValue( "mode", ParameterMode.valueOf( modeValue.toUpperCase() ) );
}
String clazzName = parameterElement.attributeValue( "class" );
Class clazz;
try {
clazz = ReflectHelper.classForName(
XMLContext.buildSafeClassName( clazzName, defaults ),
JPAOverriddenAnnotationReader.class
);
}
catch ( ClassNotFoundException e ) {
throw new AnnotationException( "Unable to find entity-class: " + clazzName, e );
}
parameterDescriptor.setValue( "type", clazz );
storedProcedureParameters.add( (StoredProcedureParameter) AnnotationFactory.create( parameterDescriptor ) );
}
ann.setValue(
"parameters",
storedProcedureParameters.toArray( new StoredProcedureParameter[storedProcedureParameters.size()] )
);
elements = subElement.elements( "result-class" );
List<Class> returnClasses = new ArrayList<Class>();
for ( Element classElement : elements ) {
String clazzName = classElement.getTextTrim();
Class clazz;
try {
clazz = ReflectHelper.classForName(
XMLContext.buildSafeClassName( clazzName, defaults ),
JPAOverriddenAnnotationReader.class
);
}
catch ( ClassNotFoundException e ) {
throw new AnnotationException( "Unable to find entity-class: " + clazzName, e );
}
returnClasses.add( clazz );
}
ann.setValue( "resultClasses", returnClasses.toArray( new Class[returnClasses.size()] ) );
elements = subElement.elements( "result-set-mapping" );
List<String> resultSetMappings = new ArrayList<String>();
for ( Element resultSetMappingElement : elements ) {
resultSetMappings.add( resultSetMappingElement.getTextTrim() );
}
ann.setValue( "resultSetMappings", resultSetMappings.toArray( new String[resultSetMappings.size()] ) );
elements = subElement.elements( "hint" );
buildQueryHints( elements, ann );
namedStoredProcedureQueries.add( (NamedStoredProcedureQuery) AnnotationFactory.create( ann ) );
}
return namedStoredProcedureElements;
}
public static List<SqlResultSetMapping> buildSqlResultsetMappings(Element element, XMLContext.Default defaults) {
if ( element == null ) {
return new ArrayList<SqlResultSetMapping>();
@ -1910,6 +1991,25 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader {
}
}
private static void buildQueryHints(List<Element> elements, AnnotationDescriptor ann){
List<QueryHint> queryHints = new ArrayList<QueryHint>( elements.size() );
for ( Element hint : elements ) {
AnnotationDescriptor hintDescriptor = new AnnotationDescriptor( QueryHint.class );
String value = hint.attributeValue( "name" );
if ( value == null ) {
throw new AnnotationException( "<hint> without name. " + SCHEMA_VALIDATION );
}
hintDescriptor.setValue( "name", value );
value = hint.attributeValue( "value" );
if ( value == null ) {
throw new AnnotationException( "<hint> without value. " + SCHEMA_VALIDATION );
}
hintDescriptor.setValue( "value", value );
queryHints.add( (QueryHint) AnnotationFactory.create( hintDescriptor ) );
}
ann.setValue( "hints", queryHints.toArray( new QueryHint[queryHints.size()] ) );
}
public static List buildNamedQueries(Element element, boolean isNative, XMLContext.Default defaults) {
if ( element == null ) {
return new ArrayList();
@ -1931,22 +2031,7 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader {
}
copyStringElement( queryElt, ann, "query" );
List<Element> elements = subelement.elements( "hint" );
List<QueryHint> queryHints = new ArrayList<QueryHint>( elements.size() );
for ( Element hint : elements ) {
AnnotationDescriptor hintDescriptor = new AnnotationDescriptor( QueryHint.class );
String value = hint.attributeValue( "name" );
if ( value == null ) {
throw new AnnotationException( "<hint> without name. " + SCHEMA_VALIDATION );
}
hintDescriptor.setValue( "name", value );
value = hint.attributeValue( "value" );
if ( value == null ) {
throw new AnnotationException( "<hint> without value. " + SCHEMA_VALIDATION );
}
hintDescriptor.setValue( "value", value );
queryHints.add( (QueryHint) AnnotationFactory.create( hintDescriptor ) );
}
ann.setValue( "hints", queryHints.toArray( new QueryHint[queryHints.size()] ) );
buildQueryHints( elements, ann );
String clazzName = subelement.attributeValue( "result-class" );
if ( StringHelper.isNotEmpty( clazzName ) ) {
Class clazz;

View File

@ -56,7 +56,7 @@ public class NamedQueryRepository {
Iterable<NamedQueryDefinition> namedQueryDefinitions,
Iterable<NamedSQLQueryDefinition> namedSqlQueryDefinitions,
Iterable<ResultSetMappingDefinition> namedSqlResultSetMappings,
List<ProcedureCallMemento> namedProcedureCalls) {
Map<String, ProcedureCallMemento> namedProcedureCalls) {
final HashMap<String, NamedQueryDefinition> namedQueryDefinitionMap = new HashMap<String, NamedQueryDefinition>();
for ( NamedQueryDefinition namedQueryDefinition : namedQueryDefinitions ) {
namedQueryDefinitionMap.put( namedQueryDefinition.getName(), namedQueryDefinition );
@ -75,6 +75,7 @@ public class NamedQueryRepository {
namedSqlResultSetMappingMap.put( resultSetMappingDefinition.getName(), resultSetMappingDefinition );
}
this.namedSqlResultSetMappingMap = Collections.unmodifiableMap( namedSqlResultSetMappingMap );
this.procedureCallMementoMap = Collections.unmodifiableMap( namedProcedureCalls );
}

View File

@ -580,13 +580,13 @@ public final class SessionFactoryImpl
this.observer.sessionFactoryCreated( this );
}
private List<ProcedureCallMemento> toProcedureCallMementos(
private Map<String, ProcedureCallMemento> toProcedureCallMementos(
Map<String, NamedProcedureCallDefinition> definitions,
Map<String, ResultSetMappingDefinition> resultSetMappingMap) {
final List<ProcedureCallMemento> rtn = new ArrayList<ProcedureCallMemento>();
final Map<String, ProcedureCallMemento> rtn = new HashMap<String, ProcedureCallMemento>();
if ( definitions != null ) {
for ( NamedProcedureCallDefinition definition : definitions.values() ) {
rtn.add( definition.toMemento( this, resultSetMappingMap ) );
for (String name : definitions.keySet()){
rtn.put( name, definitions.get( name ).toMemento( this, resultSetMappingMap ));
}
}
return rtn;
@ -871,7 +871,7 @@ public final class SessionFactoryImpl
metadata.getNamedQueryDefinitions(),
metadata.getNamedNativeQueryDefinitions(),
metadata.getResultSetMappingDefinitions(),
null
new HashMap<String, ProcedureCallMemento>( )
);
imports = new HashMap<String,String>();

View File

@ -49,7 +49,7 @@ public class ProcedureCallMementoImpl implements ProcedureCallMemento {
private final Set<String> synchronizedQuerySpaces;
private final Map<String,Object> hintsMap;
private final Map<String, Object> hintsMap;
/**
* Constructs a ProcedureCallImpl
@ -67,7 +67,7 @@ public class ProcedureCallMementoImpl implements ProcedureCallMemento {
ParameterStrategy parameterStrategy,
List<ParameterMemento> parameterDeclarations,
Set<String> synchronizedQuerySpaces,
Map<String,Object> hintsMap) {
Map<String, Object> hintsMap) {
this.procedureName = procedureName;
this.queryReturns = queryReturns;
this.parameterStrategy = parameterStrategy;

View File

@ -24,6 +24,7 @@
package org.hibernate.jpa;
import java.util.HashSet;
import java.util.Set;
import static org.hibernate.annotations.QueryHints.*;
/**
* Defines the supported JPA query hints
@ -37,52 +38,52 @@ public class QueryHints {
* @deprecated use {@link #SPEC_HINT_TIMEOUT} instead
*/
@Deprecated
public static final String HINT_TIMEOUT = "org.hibernate.timeout";
public static final String HINT_TIMEOUT = TIMEOUT_HIBERNATE;
/**
* The hint key for specifying a query timeout per JPA, which defines the timeout in milliseconds
*/
public static final String SPEC_HINT_TIMEOUT = "javax.persistence.query.timeout";
public static final String SPEC_HINT_TIMEOUT = TIMEOUT_JPA;
/**
* The hint key for specifying a comment which is to be embedded into the SQL sent to the database.
*/
public static final String HINT_COMMENT = "org.hibernate.comment";
public static final String HINT_COMMENT = COMMENT;
/**
* The hint key for specifying a JDBC fetch size, used when executing the resulting SQL.
*/
public static final String HINT_FETCH_SIZE = "org.hibernate.fetchSize";
public static final String HINT_FETCH_SIZE = FETCH_SIZE;
/**
* The hint key for specifying whether the query results should be cached for the next (cached) execution of the
* "same query".
*/
public static final String HINT_CACHEABLE = "org.hibernate.cacheable";
public static final String HINT_CACHEABLE = CACHEABLE;
/**
* The hint key for specifying the name of the cache region (within Hibernate's query result cache region)
* to use for storing the query results.
*/
public static final String HINT_CACHE_REGION = "org.hibernate.cacheRegion";
public static final String HINT_CACHE_REGION = CACHE_REGION;
/**
* The hint key for specifying that objects loaded into the persistence context as a result of this query execution
* should be associated with the persistence context as read-only.
*/
public static final String HINT_READONLY = "org.hibernate.readOnly";
public static final String HINT_READONLY = READ_ONLY;
/**
* The hint key for specifying the cache mode ({@link org.hibernate.CacheMode}) to be in effect for the
* execution of the hinted query.
*/
public static final String HINT_CACHE_MODE = "org.hibernate.cacheMode";
public static final String HINT_CACHE_MODE = CACHE_MODE;
/**
* The hint key for specifying the flush mode ({@link org.hibernate.FlushMode}) to be in effect for the
* execution of the hinted query.
*/
public static final String HINT_FLUSH_MODE = "org.hibernate.flushMode";
public static final String HINT_FLUSH_MODE = FLUSH_MODE;
private static final Set<String> HINTS = buildHintsSet();

View File

@ -0,0 +1,67 @@
package org.hibernate.jpa.test.procedure;
import java.util.List;
import javax.persistence.EntityManager;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.procedure.internal.ParameterStrategy;
import org.hibernate.procedure.internal.ProcedureCallMementoImpl;
import org.hibernate.type.IntegerType;
import org.hibernate.type.LongType;
import org.hibernate.type.StringType;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* @author Strong Liu <stliu@hibernate.org>
*/
public abstract class AbstractStoredProcedureTest extends BaseEntityManagerFunctionalTestCase {
@Test
public void testNamedStoredProcedureBinding() {
EntityManager em = getOrCreateEntityManager();
SessionFactoryImplementor sf = em.getEntityManagerFactory().unwrap( SessionFactoryImplementor.class );
final ProcedureCallMementoImpl m1 = (ProcedureCallMementoImpl) sf.getNamedQueryRepository()
.getNamedProcedureCallMemento( "s1" );
assertNotNull( m1 );
assertEquals( "p1", m1.getProcedureName() );
assertEquals( ParameterStrategy.NAMED, m1.getParameterStrategy() );
List<ProcedureCallMementoImpl.ParameterMemento> list = m1.getParameterDeclarations();
assertEquals( 2, list.size() );
ProcedureCallMementoImpl.ParameterMemento memento = list.get( 0 );
assertEquals( "p11", memento.getName() );
assertEquals( javax.persistence.ParameterMode.IN, memento.getMode() );
assertEquals( IntegerType.INSTANCE, memento.getHibernateType() );
assertEquals( Integer.class, memento.getType() );
memento = list.get( 1 );
assertEquals( "p12", memento.getName() );
assertEquals( javax.persistence.ParameterMode.IN, memento.getMode() );
assertEquals( IntegerType.INSTANCE, memento.getHibernateType() );
assertEquals( Integer.class, memento.getType() );
final ProcedureCallMementoImpl m2 = (ProcedureCallMementoImpl) sf.getNamedQueryRepository()
.getNamedProcedureCallMemento( "s2" );
assertNotNull( m2 );
assertEquals( "p2", m2.getProcedureName() );
assertEquals( ParameterStrategy.POSITIONAL, m2.getParameterStrategy() );
list = m2.getParameterDeclarations();
memento = list.get( 0 );
assertEquals( Integer.valueOf( 0 ), memento.getPosition() );
assertEquals( javax.persistence.ParameterMode.INOUT, memento.getMode() );
assertEquals( StringType.INSTANCE, memento.getHibernateType() );
assertEquals( String.class, memento.getType() );
memento = list.get( 1 );
assertEquals( Integer.valueOf( 1 ), memento.getPosition() );
assertEquals( javax.persistence.ParameterMode.INOUT, memento.getMode() );
assertEquals( LongType.INSTANCE, memento.getHibernateType() );
assertEquals( Long.class, memento.getType() );
}
}

View File

@ -0,0 +1,11 @@
package org.hibernate.jpa.test.procedure;
/**
* @author Strong Liu <stliu@hibernate.org>
*/
public class AnnotationTest extends AbstractStoredProcedureTest {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { User.class };
}
}

View File

@ -0,0 +1,11 @@
package org.hibernate.jpa.test.procedure;
/**
* @author Strong Liu <stliu@hibernate.org>
*/
public class OrmTest extends AbstractStoredProcedureTest{
@Override
public String[] getEjb3DD() {
return new String[]{"org/hibernate/jpa/test/procedure/orm.xml"};
}
}

View File

@ -0,0 +1,77 @@
package org.hibernate.jpa.test.procedure;
import javax.persistence.Entity;
import javax.persistence.EntityResult;
import javax.persistence.FieldResult;
import javax.persistence.Id;
import javax.persistence.NamedStoredProcedureQueries;
import javax.persistence.NamedStoredProcedureQuery;
import javax.persistence.ParameterMode;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.StoredProcedureParameter;
/**
* @author Strong Liu <stliu@hibernate.org>
*/
@Entity
@NamedStoredProcedureQueries(
{
@NamedStoredProcedureQuery(
name = "s1",
procedureName = "p1",
parameters = {
@StoredProcedureParameter(name = "p11",
mode = ParameterMode.IN,
type = Integer.class),
@StoredProcedureParameter(name = "p12",
mode = ParameterMode.IN,
type = Integer.class
)
},
resultClasses = { User.class }
),
@NamedStoredProcedureQuery(
name = "s2",
procedureName = "p2",
parameters = {
@StoredProcedureParameter(
mode = ParameterMode.INOUT,
type = String.class),
@StoredProcedureParameter(
mode = ParameterMode.INOUT,
type = Long.class)
},
resultSetMappings = { "srms" }
)
}
)
@SqlResultSetMapping(name = "srms",
entities = {
@EntityResult(entityClass = User.class, fields = {
@FieldResult(name = "id", column = "order_id"),
@FieldResult(name = "name", column = "order_item")
})
}
)
public class User {
@Id
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="2.1"
>
<package>org.hibernate.jpa.test.procedure</package>
<entity class="User" metadata-complete="true">
<named-stored-procedure-query name="s1" procedure-name="p1">
<parameter class="java.lang.Integer" mode="IN" name="p11"/>
<parameter class="java.lang.Integer" mode="IN" name="p12"/>
</named-stored-procedure-query>
<named-stored-procedure-query name="s2" procedure-name="p2">
<parameter class="java.lang.String" mode="INOUT"/>
<parameter class="java.lang.Long" mode="INOUT"/>
</named-stored-procedure-query>
<sql-result-set-mapping name="srms">
<entity-result entity-class="User">
<field-result name="id" column="order_id"/>
<field-result name="name" column="order_item"/>
</entity-result>
</sql-result-set-mapping>
<attributes>
<id name="id">
<column name="fld_id"/>
</id>
<basic name="name"/>
</attributes>
</entity>
</entity-mappings>