HHH-8530 - Align JPA "positional parameter" handling in javax.persistence.Parameter impl
This commit is contained in:
parent
99be129200
commit
bf0eed907f
|
@ -612,9 +612,9 @@ public interface CoreMessageLogger extends BasicLogger {
|
|||
@Message(value = "Package not found or wo package-info.java: %s", id = 194)
|
||||
void packageNotFound(String packageName);
|
||||
|
||||
@LogMessage(level = WARN)
|
||||
@Message(value = "Parameter position [%s] occurred as both JPA and Hibernate positional parameter", id = 195)
|
||||
void parameterPositionOccurredAsBothJpaAndHibernatePositionalParameter(Integer position);
|
||||
// @LogMessage(level = WARN)
|
||||
// @Message(value = "Parameter position [%s] occurred as both JPA and Hibernate positional parameter", id = 195)
|
||||
// void parameterPositionOccurredAsBothJpaAndHibernatePositionalParameter(Integer position);
|
||||
|
||||
@LogMessage(level = ERROR)
|
||||
@Message(value = "Error parsing XML (%s) : %s", id = 196)
|
||||
|
|
|
@ -108,4 +108,13 @@ public interface EntityManagerMessageLogger extends CoreMessageLogger {
|
|||
"as the entity (type=%s, id=%s) does not exist", id = 15013 )
|
||||
void ignoringEntityNotFound( String entityName, String identifier);
|
||||
|
||||
@LogMessage( level = WARN )
|
||||
@Message(
|
||||
value = "DEPRECATION - attempt to refer to JPA positional parameter [?%1$s] using String name [\"%1$s\"] " +
|
||||
"rather than int position [%1$s] (generally in Query#setParameter, Query#getParameter or " +
|
||||
"Query#getParameterValue calls). Hibernate previously allowed such usage, but it is considered " +
|
||||
"deprecated.",
|
||||
id = 15014
|
||||
)
|
||||
void deprecatedJpaPositionalParameterAccess(Integer jpaPositionalParameter);
|
||||
}
|
||||
|
|
|
@ -23,10 +23,13 @@
|
|||
*/
|
||||
package org.hibernate.jpa.internal;
|
||||
|
||||
import static javax.persistence.TemporalType.DATE;
|
||||
import static javax.persistence.TemporalType.TIME;
|
||||
import static javax.persistence.TemporalType.TIMESTAMP;
|
||||
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.NonUniqueResultException;
|
||||
import javax.persistence.ParameterMode;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.TypedQuery;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -36,27 +39,18 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.NonUniqueResultException;
|
||||
import javax.persistence.ParameterMode;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.StoredProcedureQuery;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.TypedQuery;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.CacheMode;
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.SQLQuery;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.TypeMismatchException;
|
||||
import org.hibernate.engine.query.spi.NamedParameterDescriptor;
|
||||
import org.hibernate.engine.query.spi.OrdinalParameterDescriptor;
|
||||
import org.hibernate.engine.query.spi.ParameterMetadata;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.hql.internal.QueryExecutionRequestException;
|
||||
import org.hibernate.internal.SQLQueryImpl;
|
||||
import org.hibernate.jpa.AvailableSettings;
|
||||
|
@ -69,7 +63,10 @@ import org.hibernate.jpa.spi.ParameterBind;
|
|||
import org.hibernate.jpa.spi.ParameterRegistration;
|
||||
import org.hibernate.type.CompositeCustomType;
|
||||
import org.hibernate.type.Type;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static javax.persistence.TemporalType.DATE;
|
||||
import static javax.persistence.TemporalType.TIME;
|
||||
import static javax.persistence.TemporalType.TIMESTAMP;
|
||||
|
||||
/**
|
||||
* Hibernate implementation of both the {@link Query} and {@link TypedQuery} contracts.
|
||||
|
@ -83,7 +80,6 @@ public class QueryImpl<X> extends AbstractQueryImpl<X> implements TypedQuery<X>,
|
|||
public static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(EntityManagerMessageLogger.class, QueryImpl.class.getName());
|
||||
|
||||
private org.hibernate.Query query;
|
||||
private Set<Integer> jpaPositionalIndices;
|
||||
|
||||
public QueryImpl(org.hibernate.Query query, AbstractEntityManagerImpl em) {
|
||||
this( query, em, Collections.<String, Class>emptyMap() );
|
||||
|
@ -104,6 +100,8 @@ public class QueryImpl<X> extends AbstractQueryImpl<X> implements TypedQuery<X>,
|
|||
throw new IllegalStateException( "Unknown query type for parameter extraction" );
|
||||
}
|
||||
|
||||
boolean hadJpaPositionalParameters = false;
|
||||
|
||||
final ParameterMetadata parameterMetadata = org.hibernate.internal.AbstractQueryImpl.class.cast( query ).getParameterMetadata();
|
||||
|
||||
// extract named params
|
||||
|
@ -118,24 +116,30 @@ public class QueryImpl<X> extends AbstractQueryImpl<X> implements TypedQuery<X>,
|
|||
else if ( descriptor.getExpectedType() != null ) {
|
||||
javaType = descriptor.getExpectedType().getReturnedClass();
|
||||
}
|
||||
registerParameter( new ParameterRegistrationImpl( this, query, name, javaType ) );
|
||||
|
||||
if ( descriptor.isJpaStyle() ) {
|
||||
if ( jpaPositionalIndices == null ) {
|
||||
jpaPositionalIndices = new HashSet<Integer>();
|
||||
}
|
||||
jpaPositionalIndices.add( Integer.valueOf( name ) );
|
||||
hadJpaPositionalParameters = true;
|
||||
final Integer position = Integer.valueOf( name );
|
||||
registerParameter( new JpaPositionalParameterRegistrationImpl( this, query, position, javaType ) );
|
||||
}
|
||||
else {
|
||||
registerParameter( new ParameterRegistrationImpl( this, query, name, javaType ) );
|
||||
}
|
||||
}
|
||||
|
||||
// extract positional parameters
|
||||
if ( hadJpaPositionalParameters ) {
|
||||
if ( parameterMetadata.getOrdinalParameterCount() > 0 ) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot mix JPA positional parameters and native Hibernate positional/ordinal parameters"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// extract Hibernate native positional parameters
|
||||
for ( int i = 0, max = parameterMetadata.getOrdinalParameterCount(); i < max; i++ ) {
|
||||
final OrdinalParameterDescriptor descriptor = parameterMetadata.getOrdinalParameterDescriptor( i + 1 );
|
||||
Class javaType = descriptor.getExpectedType() == null ? null : descriptor.getExpectedType().getReturnedClass();
|
||||
registerParameter( new ParameterRegistrationImpl( this, query, i+1, javaType ) );
|
||||
Integer position = descriptor.getOrdinalPosition();
|
||||
if ( jpaPositionalIndices != null && jpaPositionalIndices.contains(position) ) {
|
||||
LOG.parameterPositionOccurredAsBothJpaAndHibernatePositionalParameter(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,7 +166,7 @@ public class QueryImpl<X> extends AbstractQueryImpl<X> implements TypedQuery<X>,
|
|||
|
||||
private ParameterBind<T> bind;
|
||||
|
||||
private ParameterRegistrationImpl(
|
||||
protected ParameterRegistrationImpl(
|
||||
Query jpaQuery,
|
||||
org.hibernate.Query nativeQuery,
|
||||
String name,
|
||||
|
@ -174,7 +178,7 @@ public class QueryImpl<X> extends AbstractQueryImpl<X> implements TypedQuery<X>,
|
|||
this.position = null;
|
||||
}
|
||||
|
||||
private ParameterRegistrationImpl(
|
||||
protected ParameterRegistrationImpl(
|
||||
Query jpaQuery,
|
||||
org.hibernate.Query nativeQuery,
|
||||
Integer position,
|
||||
|
@ -186,6 +190,11 @@ public class QueryImpl<X> extends AbstractQueryImpl<X> implements TypedQuery<X>,
|
|||
this.name = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJpaPositionalParameter() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query getQuery() {
|
||||
return jpaQuery;
|
||||
|
@ -304,6 +313,39 @@ public class QueryImpl<X> extends AbstractQueryImpl<X> implements TypedQuery<X>,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized handling for JPA "positional parameters".
|
||||
*
|
||||
* @param <T> The parameter type type.
|
||||
*/
|
||||
public static class JpaPositionalParameterRegistrationImpl<T> extends ParameterRegistrationImpl<T> {
|
||||
final Integer position;
|
||||
|
||||
protected JpaPositionalParameterRegistrationImpl(
|
||||
Query jpaQuery,
|
||||
org.hibernate.Query nativeQuery,
|
||||
Integer position,
|
||||
Class<T> javaType) {
|
||||
super( jpaQuery, nativeQuery, position.toString(), javaType );
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJpaPositionalParameter() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public org.hibernate.Query getHibernateQuery() {
|
||||
return query;
|
||||
}
|
||||
|
@ -465,11 +507,6 @@ public class QueryImpl<X> extends AbstractQueryImpl<X> implements TypedQuery<X>,
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isJpaPositionalParameter(int position) {
|
||||
return jpaPositionalIndices != null && jpaPositionalIndices.contains( position );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public <T> T unwrap(Class<T> tClass) {
|
||||
|
|
|
@ -450,11 +450,6 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
|
|||
|
||||
// parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@Override
|
||||
protected boolean isJpaPositionalParameter(int position) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public ProcedureCall getHibernateProcedureCall() {
|
||||
return procedureCall;
|
||||
}
|
||||
|
@ -487,6 +482,11 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
|
|||
return nativeParamRegistration.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJpaPositionalParameter() {
|
||||
return getPosition() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query getQuery() {
|
||||
return query;
|
||||
|
|
|
@ -429,21 +429,50 @@ public abstract class BaseQueryImpl implements Query {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <X> ParameterRegistration<X> findParameterRegistration(String parameterName) {
|
||||
return (ParameterRegistration<X>) getParameter( parameterName );
|
||||
final Integer jpaPositionalParameter = toNumberOrNull( parameterName );
|
||||
|
||||
if ( parameterRegistrations != null ) {
|
||||
for ( ParameterRegistration<?> param : parameterRegistrations ) {
|
||||
if ( parameterName.equals( param.getName() ) ) {
|
||||
return (ParameterRegistration<X>) param;
|
||||
}
|
||||
|
||||
// legacy allowance of the application to access the parameter using the position as a String
|
||||
if ( param.isJpaPositionalParameter() && jpaPositionalParameter != null ) {
|
||||
if ( jpaPositionalParameter.equals( param.getPosition() ) ) {
|
||||
LOG.deprecatedJpaPositionalParameterAccess( jpaPositionalParameter );
|
||||
}
|
||||
return (ParameterRegistration<X>) param;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException( "Parameter with that name [" + parameterName + "] did not exist" );
|
||||
}
|
||||
|
||||
private Integer toNumberOrNull(String parameterName) {
|
||||
try {
|
||||
return Integer.valueOf( parameterName );
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <X> ParameterRegistration<X> findParameterRegistration(int parameterPosition) {
|
||||
if ( isJpaPositionalParameter( parameterPosition ) ) {
|
||||
return findParameterRegistration( Integer.toString( parameterPosition ) );
|
||||
}
|
||||
else {
|
||||
return (ParameterRegistration<X>) getParameter( parameterPosition );
|
||||
if ( parameterRegistrations != null ) {
|
||||
for ( ParameterRegistration<?> param : parameterRegistrations ) {
|
||||
if ( param.getPosition() == null ) {
|
||||
continue;
|
||||
}
|
||||
if ( parameterPosition == param.getPosition() ) {
|
||||
return (ParameterRegistration<X>) param;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException( "Parameter with that position [" + parameterPosition + "] did not exist" );
|
||||
}
|
||||
|
||||
protected abstract boolean isJpaPositionalParameter(int position);
|
||||
|
||||
protected static class ParameterBindImpl<T> implements ParameterBind<T> {
|
||||
private final T value;
|
||||
private final TemporalType specifiedTemporalType;
|
||||
|
@ -664,21 +693,14 @@ public abstract class BaseQueryImpl implements Query {
|
|||
@Override
|
||||
public Parameter<?> getParameter(String name) {
|
||||
checkOpen( false );
|
||||
if ( parameterRegistrations != null ) {
|
||||
for ( ParameterRegistration<?> param : parameterRegistrations ) {
|
||||
if ( name.equals( param.getName() ) ) {
|
||||
return param;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException( "Parameter with that name [" + name + "] did not exist" );
|
||||
return findParameterRegistration( name );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Parameter<T> getParameter(String name, Class<T> type) {
|
||||
checkOpen( false );
|
||||
Parameter param = getParameter( name );
|
||||
Parameter param = findParameterRegistration( name );
|
||||
|
||||
if ( param.getParameterType() != null ) {
|
||||
// we were able to determine the expected type during analysis, so validate it here
|
||||
|
@ -698,21 +720,8 @@ public abstract class BaseQueryImpl implements Query {
|
|||
|
||||
@Override
|
||||
public Parameter<?> getParameter(int position) {
|
||||
if ( isJpaPositionalParameter( position ) ) {
|
||||
return getParameter( Integer.toString( position ) );
|
||||
}
|
||||
checkOpen( false );
|
||||
if ( parameterRegistrations != null ) {
|
||||
for ( ParameterRegistration<?> param : parameterRegistrations ) {
|
||||
if ( param.getPosition() == null ) {
|
||||
continue;
|
||||
}
|
||||
if ( position == param.getPosition() ) {
|
||||
return param;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException( "Parameter with that position [" + position + "] did not exist" );
|
||||
return findParameterRegistration( position );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -720,7 +729,7 @@ public abstract class BaseQueryImpl implements Query {
|
|||
public <T> Parameter<T> getParameter(int position, Class<T> type) {
|
||||
checkOpen( false );
|
||||
|
||||
Parameter param = getParameter( position );
|
||||
Parameter param = findParameterRegistration( position );
|
||||
|
||||
if ( param.getParameterType() != null ) {
|
||||
// we were able to determine the expected type during analysis, so validate it here
|
||||
|
|
|
@ -39,6 +39,17 @@ import javax.persistence.TemporalType;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface ParameterRegistration<T> extends Parameter<T> {
|
||||
/**
|
||||
* JPA has a different definition of positional parameters than what legacy Hibernate HQL had. In JPA,
|
||||
* the parameter holders are labelled (named :/). At any rate the semantics are different and we often
|
||||
* need to understand which we are dealing with (and applications might too).
|
||||
*
|
||||
* @return {@code true} if this is a JPA-style positional parameter; {@code false} would indicate
|
||||
* we have either a named parameter ({@link #getName()} would return a non-{@code null} value) or a native
|
||||
* Hibernate positional parameter.
|
||||
*/
|
||||
public boolean isJpaPositionalParameter();
|
||||
|
||||
/**
|
||||
* Access to the query that this parameter belongs to.
|
||||
*
|
||||
|
|
|
@ -187,6 +187,21 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
|
|||
em.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJpaPositionalParameters() {
|
||||
EntityManager em = getOrCreateEntityManager();
|
||||
em.getTransaction().begin();
|
||||
|
||||
Query query = em.createQuery( "from Item item where item.name =?1 or item.descr = ?1" );
|
||||
Parameter p1 = query.getParameter( 1 );
|
||||
Assert.assertNotNull( p1 );
|
||||
Assert.assertNotNull( p1.getPosition() );
|
||||
Assert.assertNull( p1.getName() );
|
||||
|
||||
em.getTransaction().commit();
|
||||
em.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParameterList() throws Exception {
|
||||
final Item item = new Item( "Mouse", "Micro$oft mouse" );
|
||||
|
@ -225,6 +240,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
|
|||
params = new ArrayList();
|
||||
params.add( item.getName() );
|
||||
params.add( item2.getName() );
|
||||
// deprecated usage of positional parameter by String
|
||||
q.setParameter( "1", params );
|
||||
result = q.getResultList();
|
||||
assertNotNull( result );
|
||||
|
@ -277,6 +293,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
|
|||
params = new ArrayList();
|
||||
params.add( item.getName() );
|
||||
params.add( item2.getName() );
|
||||
// deprecated usage of positional parameter by String
|
||||
q.setParameter( "1", params );
|
||||
result = q.getResultList();
|
||||
assertNotNull( result );
|
||||
|
@ -424,6 +441,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
|
|||
|
||||
// next using jpa-style positional parameter, but as a name (which is how Hibernate core treats these
|
||||
query = em.createQuery( "select w from Wallet w where w.brand = ?1" );
|
||||
// deprecated usage of positional parameter by String
|
||||
query.setParameter( "1", "Lacoste" );
|
||||
w = (Wallet) query.getSingleResult();
|
||||
assertNotNull( w );
|
||||
|
|
Loading…
Reference in New Issue