package org.hibernate.cfg.annotations;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.NamedStoredProcedureQuery;
import javax.persistence.ParameterMode;
import javax.persistence.StoredProcedureParameter;
import org.hibernate.MappingException;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.ResultSetMappingDefinition;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.procedure.ProcedureCallMemento;
import org.hibernate.procedure.internal.ProcedureCallMementoImpl;
import org.hibernate.procedure.internal.Util;
import org.hibernate.procedure.spi.ParameterStrategy;
import static org.hibernate.procedure.internal.ProcedureCallMementoImpl.ParameterMemento;
public class NamedProcedureCallDefinition {
private final String registeredName;
private final String procedureName;
private final Class[] resultClasses;
private final String[] resultSetMappings;
private final ParameterDefinitions parameterDefinitions;
private final Map<String, Object> hints;
NamedProcedureCallDefinition(NamedStoredProcedureQuery annotation) {
this.registeredName = annotation.name();
this.procedureName = annotation.procedureName();
this.hints = new QueryHintDefinition( annotation.hints() ).getHintsMap();
this.resultClasses = annotation.resultClasses();
this.resultSetMappings = annotation.resultSetMappings();
this.parameterDefinitions = new ParameterDefinitions( annotation.parameters(), hints );
final boolean specifiesResultClasses = resultClasses != null && resultClasses.length > 0;
final boolean specifiesResultSetMappings = resultSetMappings != null && resultSetMappings.length > 0;
if ( specifiesResultClasses && specifiesResultSetMappings ) {
throw new MappingException(
String.format(
"NamedStoredProcedureQuery [%s] specified both resultClasses and resultSetMappings",
registeredName
)
);
}
}
public String getRegisteredName() {
return registeredName;
}
public String getProcedureName() {
return procedureName;
}
public ProcedureCallMemento toMemento(
final SessionFactoryImpl sessionFactory,
final Map<String,ResultSetMappingDefinition> resultSetMappingDefinitions) {
final List<NativeSQLQueryReturn> collectedQueryReturns = new ArrayList<NativeSQLQueryReturn>();
final Set<String> collectedQuerySpaces = new HashSet<String>();
final boolean specifiesResultClasses = resultClasses != null && resultClasses.length > 0;
final boolean specifiesResultSetMappings = resultSetMappings != null && resultSetMappings.length > 0;
if ( specifiesResultClasses ) {
Util.resolveResultClasses(
new Util.ResultClassesResolutionContext() {
@Override
public SessionFactoryImplementor getSessionFactory() {
return sessionFactory;
}
@Override
public void addQueryReturns(NativeSQLQueryReturn... queryReturns) {
Collections.addAll( collectedQueryReturns, queryReturns );
}
@Override
public void addQuerySpaces(String... spaces) {
Collections.addAll( collectedQuerySpaces, spaces );
}
},
resultClasses
);
}
else if ( specifiesResultSetMappings ) {
Util.resolveResultSetMappings(
new Util.ResultSetMappingResolutionContext() {
@Override
public SessionFactoryImplementor getSessionFactory() {
return sessionFactory;
}
@Override
public ResultSetMappingDefinition findResultSetMapping(String name) {
return resultSetMappingDefinitions.get( name );
}
@Override
public void addQueryReturns(NativeSQLQueryReturn... queryReturns) {
Collections.addAll( collectedQueryReturns, queryReturns );
}
@Override
public void addQuerySpaces(String... spaces) {
Collections.addAll( collectedQuerySpaces, spaces );
}
},
resultSetMappings
);
}
return new ProcedureCallMementoImpl(
procedureName,
collectedQueryReturns.toArray( new NativeSQLQueryReturn[ collectedQueryReturns.size() ] ),
parameterDefinitions.getParameterStrategy(),
parameterDefinitions.toMementos( sessionFactory ),
collectedQuerySpaces,
hints
);
}
static class ParameterDefinitions {
private final ParameterStrategy parameterStrategy;
private final ParameterDefinition[] parameterDefinitions;
ParameterDefinitions(StoredProcedureParameter[] parameters, Map<String, Object> queryHintMap) {
if ( parameters == null || parameters.length == 0 ) {
parameterStrategy = ParameterStrategy.POSITIONAL;
parameterDefinitions = new ParameterDefinition[0];
}
else {
parameterStrategy = StringHelper.isNotEmpty( parameters[0].name() )
? ParameterStrategy.NAMED
: ParameterStrategy.POSITIONAL;
parameterDefinitions = new ParameterDefinition[ parameters.length ];
for ( int i = 0; i < parameters.length; i++ ) {
parameterDefinitions[i] = ParameterDefinition.from(
parameterStrategy,
parameters[i],
i+1,
queryHintMap
);
}
}
}
public ParameterStrategy getParameterStrategy() {
return parameterStrategy;
}
public List<ParameterMemento> toMementos(SessionFactoryImpl sessionFactory) {
final List<ParameterMemento> mementos = new ArrayList<ParameterMemento>();
for ( ParameterDefinition definition : parameterDefinitions ) {
mementos.add(definition.toMemento( sessionFactory ));
}
return mementos;
}
}
static class ParameterDefinition {
private final Integer position;
private final String name;
private final ParameterMode parameterMode;
private final Class type;
private final Boolean explicitPassNullSetting;
static ParameterDefinition from(
ParameterStrategy parameterStrategy,
StoredProcedureParameter parameterAnnotation,
int adjustedPosition,
Map<String, Object> queryHintMap) {
final Object explicitNullPassingHint;
if ( parameterStrategy == ParameterStrategy.NAMED ) {
explicitNullPassingHint = queryHintMap.get( AvailableSettings.PROCEDURE_NULL_PARAM_PASSING + '.' + parameterAnnotation.name() );
}
else {
explicitNullPassingHint = queryHintMap.get( AvailableSettings.PROCEDURE_NULL_PARAM_PASSING + '.' + adjustedPosition );
}
return new ParameterDefinition(
adjustedPosition,
parameterAnnotation,
interpretBoolean( explicitNullPassingHint )
);
}
private static Boolean interpretBoolean(Object value) {
if ( value == null ) {
return null;
}
if ( value instanceof Boolean ) {
return (Boolean) value;
}
return Boolean.valueOf( value.toString() );
}
ParameterDefinition(int position, StoredProcedureParameter annotation, Boolean explicitPassNullSetting) {
this.position = position;
this.name = normalize( annotation.name() );
this.parameterMode = annotation.mode();
this.type = annotation.type();
this.explicitPassNullSetting = explicitPassNullSetting;
}
@SuppressWarnings("UnnecessaryUnboxing")
public ParameterMemento toMemento(SessionFactoryImpl sessionFactory) {
final boolean initialPassNullSetting = explicitPassNullSetting != null
? explicitPassNullSetting.booleanValue()
: sessionFactory.getSessionFactoryOptions().isProcedureParameterNullPassingEnabled();
return new ParameterMemento(
position,
name,
parameterMode,
type,
sessionFactory.getTypeResolver().heuristicType( type.getName() ),
initialPassNullSetting
);
}
}
private static String normalize(String name) {
return StringHelper.isNotEmpty( name ) ? name : null;
}
}