
org.hibernate.jpa.internal.QueryImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-entitymanager
Show all versions of hibernate-entitymanager
A module of the Hibernate O/RM project
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
package org.hibernate.jpa.internal;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
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.TemporalType;
import javax.persistence.TypedQuery;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.SQLQuery;
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.hql.internal.QueryExecutionRequestException;
import org.hibernate.internal.SQLQueryImpl;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.HibernateQuery;
import org.hibernate.jpa.TypedParameterValue;
import org.hibernate.jpa.internal.util.ConfigurationHelper;
import org.hibernate.jpa.internal.util.LockModeTypeHelper;
import org.hibernate.jpa.spi.AbstractEntityManagerImpl;
import org.hibernate.jpa.spi.AbstractQueryImpl;
import org.hibernate.jpa.spi.NullTypeBindableParameterRegistration;
import org.hibernate.jpa.spi.ParameterBind;
import org.hibernate.type.CompositeCustomType;
import org.hibernate.type.Type;
import static javax.persistence.TemporalType.DATE;
import static javax.persistence.TemporalType.TIME;
import static javax.persistence.TemporalType.TIMESTAMP;
import static org.hibernate.jpa.internal.HEMLogging.messageLogger;
/**
* Hibernate implementation of both the {@link Query} and {@link TypedQuery} contracts.
*
* @author Gavin King
* @author Emmanuel Bernard
* @author Steve Ebersole
*/
public class QueryImpl extends AbstractQueryImpl
implements TypedQuery, HibernateQuery, org.hibernate.ejb.HibernateQuery {
public static final EntityManagerMessageLogger LOG = messageLogger( QueryImpl.class );
private org.hibernate.Query query;
public QueryImpl(org.hibernate.Query query, AbstractEntityManagerImpl em) {
this( query, em, Collections.emptyMap() );
}
public QueryImpl(
org.hibernate.Query query,
AbstractEntityManagerImpl em,
Map namedParameterTypeRedefinitions) {
super( em );
this.query = query;
extractParameterInfo( namedParameterTypeRedefinitions );
}
@Override
protected boolean isNativeSqlQuery() {
return SQLQuery.class.isInstance( query );
}
@Override
protected boolean isSelectQuery() {
if ( isNativeSqlQuery() ) {
throw new IllegalStateException( "Cannot tell if native SQL query is SELECT query" );
}
return org.hibernate.internal.QueryImpl.class.cast( query ).isSelect();
}
@SuppressWarnings({"unchecked", "RedundantCast"})
private void extractParameterInfo(Map namedParameterTypeRedefinition) {
if ( !org.hibernate.internal.AbstractQueryImpl.class.isInstance( query ) ) {
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
for ( String name : (Set) parameterMetadata.getNamedParameterNames() ) {
final NamedParameterDescriptor descriptor = parameterMetadata.getNamedParameterDescriptor( name );
Class javaType = namedParameterTypeRedefinition.get( name );
if ( javaType != null && mightNeedRedefinition( javaType, descriptor.getExpectedType() ) ) {
descriptor.resetExpectedType(
sfi().getTypeResolver().heuristicType( javaType.getName() )
);
}
else if ( descriptor.getExpectedType() != null ) {
javaType = descriptor.getExpectedType().getReturnedClass();
}
if ( descriptor.isJpaStyle() ) {
hadJpaPositionalParameters = true;
final Integer position = Integer.valueOf( name );
registerParameter( new JpaPositionalParameterRegistrationImpl( this, query, position, javaType ) );
}
else {
registerParameter( new ParameterRegistrationImpl( this, query, name, javaType ) );
}
}
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 ) );
}
}
private SessionFactoryImplementor sfi() {
return (SessionFactoryImplementor) getEntityManager().getFactory().getSessionFactory();
}
private boolean mightNeedRedefinition(Class javaType, Type expectedType) {
// only redefine dates/times/timestamps that are not wrapped in a CompositeCustomType
if ( expectedType == null ) {
return java.util.Date.class.isAssignableFrom( javaType );
}
else {
return java.util.Date.class.isAssignableFrom( javaType )
&& !CompositeCustomType.class.isAssignableFrom( expectedType.getClass() );
}
}
private static class ParameterRegistrationImpl implements NullTypeBindableParameterRegistration {
private final QueryImpl jpaQuery;
private final org.hibernate.Query nativeQuery;
private final String name;
private final Integer position;
private final Class javaType;
private ParameterBind bind;
protected ParameterRegistrationImpl(
QueryImpl jpaQuery,
org.hibernate.Query nativeQuery,
String name,
Class javaType) {
this.jpaQuery = jpaQuery;
this.nativeQuery = nativeQuery;
this.name = name;
this.javaType = javaType;
this.position = null;
}
protected ParameterRegistrationImpl(
QueryImpl jpaQuery,
org.hibernate.Query nativeQuery,
Integer position,
Class javaType) {
this.jpaQuery = jpaQuery;
this.nativeQuery = nativeQuery;
this.position = position;
this.javaType = javaType;
this.name = null;
}
@Override
public boolean isJpaPositionalParameter() {
return false;
}
@Override
public Query getQuery() {
return jpaQuery;
}
@Override
public String getName() {
return name;
}
@Override
public Integer getPosition() {
return position;
}
@Override
public Class getParameterType() {
return javaType;
}
@Override
public ParameterMode getMode() {
// implicitly
return ParameterMode.IN;
}
@Override
public boolean isBindable() {
// again, implicitly
return true;
}
@Override
@SuppressWarnings("unchecked")
public void bindValue(T value) {
validateBinding( getParameterType(), value, null );
if ( name != null ) {
if ( value instanceof TypedParameterValue ) {
final TypedParameterValue typedValueWrapper = (TypedParameterValue ) value;
nativeQuery.setParameter( name, typedValueWrapper.getValue(), typedValueWrapper.getType() );
value = (T) typedValueWrapper.getValue();
}
else if ( value instanceof Collection ) {
nativeQuery.setParameterList( name, (Collection) value );
}
else {
nativeQuery.setParameter( name, value );
}
}
else {
if ( value instanceof TypedParameterValue ) {
final TypedParameterValue typedValueWrapper = (TypedParameterValue ) value;
nativeQuery.setParameter( position, typedValueWrapper.getValue(), typedValueWrapper.getType() );
value = (T) typedValueWrapper.getValue();
}
else {
nativeQuery.setParameter( position - 1, value );
}
}
bind = new ParameterBindImpl( value, null );
}
@Override
public void bindValue(T value, TemporalType specifiedTemporalType) {
validateBinding( getParameterType(), value, specifiedTemporalType );
if ( value == null || Date.class.isInstance( value ) ) {
if ( name != null ) {
if ( specifiedTemporalType == DATE ) {
nativeQuery.setDate( name, (Date) value );
}
else if ( specifiedTemporalType == TIME ) {
nativeQuery.setTime( name, (Date) value );
}
else if ( specifiedTemporalType == TIMESTAMP ) {
nativeQuery.setTimestamp( name, (Date) value );
}
}
else {
if ( specifiedTemporalType == DATE ) {
nativeQuery.setDate( position - 1, (Date) value );
}
else if ( specifiedTemporalType == TIME ) {
nativeQuery.setTime( position - 1, (Date) value );
}
else if ( specifiedTemporalType == TIMESTAMP ) {
nativeQuery.setTimestamp( position - 1, (Date) value );
}
}
}
else if ( Calendar.class.isInstance( value ) ) {
if ( name != null ) {
if ( specifiedTemporalType == DATE ) {
nativeQuery.setCalendarDate( name, (Calendar) value );
}
else if ( specifiedTemporalType == TIME ) {
throw new IllegalArgumentException( "not yet implemented" );
}
else if ( specifiedTemporalType == TIMESTAMP ) {
nativeQuery.setCalendar( name, (Calendar) value );
}
}
else {
if ( specifiedTemporalType == DATE ) {
nativeQuery.setCalendarDate( position - 1, (Calendar) value );
}
else if ( specifiedTemporalType == TIME ) {
throw new IllegalArgumentException( "not yet implemented" );
}
else if ( specifiedTemporalType == TIMESTAMP ) {
nativeQuery.setCalendar( position - 1, (Calendar) value );
}
}
}
else {
throw new IllegalArgumentException(
"Unexpected type [" + value + "] passed with TemporalType; expecting Date or Calendar"
);
}
bind = new ParameterBindImpl( value, specifiedTemporalType );
}
@Override
public ParameterBind getBind() {
return bind;
}
@Override
public void bindNullValue(Class> nullParameterType) {
if ( nullParameterType == null ) {
throw new IllegalArgumentException( "nullParameterType must be non-null" );
}
if ( getParameterType() != null ) {
throw new IllegalArgumentException(
String.format(
"Cannot bind null value as type [%s]; it is already mapped as type [%s]",
nullParameterType.getName(),
getParameterType().getName()
)
);
}
validateBinding( nullParameterType, null, null );
if ( !org.hibernate.internal.AbstractQueryImpl.class.isInstance( jpaQuery.getHibernateQuery() ) ) {
throw new IllegalStateException(
"Unknown query type for binding null value" + jpaQuery.getHibernateQuery()
.getClass()
.getName()
);
}
org.hibernate.internal.AbstractQueryImpl abstractQuery =
(org.hibernate.internal.AbstractQueryImpl) jpaQuery.getHibernateQuery();
final Type explicitType = abstractQuery.guessType( nullParameterType );
if ( name != null ) {
nativeQuery.setParameter( name, null, explicitType );
}
else {
nativeQuery.setParameter( position - 1, null, explicitType );
}
bind = new ParameterBindImpl( null, null );
}
}
/**
* Specialized handling for JPA "positional parameters".
*
* @param The parameter type type.
*/
public static class JpaPositionalParameterRegistrationImpl extends ParameterRegistrationImpl {
final Integer position;
protected JpaPositionalParameterRegistrationImpl(
QueryImpl jpaQuery,
org.hibernate.Query nativeQuery,
Integer position,
Class 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;
}
@Override
protected int internalExecuteUpdate() {
return query.executeUpdate();
}
@Override
protected void applyMaxResults(int maxResults) {
query.setMaxResults( maxResults );
}
@Override
protected void applyFirstResult(int firstResult) {
query.setFirstResult( firstResult );
}
@Override
protected boolean applyTimeoutHint(int timeout) {
query.setTimeout( timeout );
return true;
}
@Override
protected boolean applyCommentHint(String comment) {
query.setComment( comment );
return true;
}
@Override
protected boolean applyFetchSizeHint(int fetchSize) {
query.setFetchSize( fetchSize );
return true;
}
@Override
protected boolean applyCacheableHint(boolean isCacheable) {
query.setCacheable( isCacheable );
return true;
}
@Override
protected boolean applyCacheRegionHint(String regionName) {
query.setCacheRegion( regionName );
return true;
}
@Override
protected boolean applyReadOnlyHint(boolean isReadOnly) {
query.setReadOnly( isReadOnly );
return true;
}
@Override
protected boolean applyCacheModeHint(CacheMode cacheMode) {
query.setCacheMode( cacheMode );
return true;
}
@Override
protected boolean applyFlushModeHint(FlushMode flushMode) {
query.setFlushMode( flushMode );
return true;
}
@Override
protected boolean canApplyAliasSpecificLockModeHints() {
return org.hibernate.internal.QueryImpl.class.isInstance( query ) || SQLQueryImpl.class.isInstance( query );
}
@Override
protected void applyAliasSpecificLockModeHint(String alias, LockMode lockMode) {
query.getLockOptions().setAliasSpecificLockMode( alias, lockMode );
}
@Override
@SuppressWarnings({"unchecked", "RedundantCast"})
public List getResultList() {
getEntityManager().checkOpen( true );
checkTransaction();
beforeQuery();
try {
return list();
}
catch (QueryExecutionRequestException he) {
throw new IllegalStateException( he );
}
catch (TypeMismatchException e) {
throw new IllegalArgumentException( e );
}
catch (HibernateException he) {
throw getEntityManager().convert( he );
}
}
/**
* For JPA native SQL queries, we may need to perform a flush before executing the query.
*/
private void beforeQuery() {
final org.hibernate.Query query = getHibernateQuery();
if ( !SQLQuery.class.isInstance( query ) ) {
// this need only exists for native SQL queries, not JPQL or Criteria queries (both of which do
// partial auto flushing already).
return;
}
final SQLQuery sqlQuery = (SQLQuery) query;
if ( sqlQuery.getSynchronizedQuerySpaces() != null && !sqlQuery.getSynchronizedQuerySpaces().isEmpty() ) {
// The application defined query spaces on the Hibernate native SQLQuery which means the query will already
// perform a partial flush according to the defined query spaces, no need to do a full flush.
return;
}
// otherwise we need to flush. the query itself is not required to execute in a transaction; if there is
// no transaction, the flush would throw a TransactionRequiredException which would potentially break existing
// apps, so we only do the flush if a transaction is in progress.
if ( getEntityManager().isTransactionInProgress() ) {
getEntityManager().flush();
}
}
@Override
@SuppressWarnings({"unchecked", "RedundantCast"})
public X getSingleResult() {
getEntityManager().checkOpen( true );
checkTransaction();
beforeQuery();
try {
final List result = list();
if ( result.size() == 0 ) {
NoResultException nre = new NoResultException( "No entity found for query" );
getEntityManager().handlePersistenceException( nre );
throw nre;
}
else if ( result.size() > 1 ) {
final Set uniqueResult = new HashSet( result );
if ( uniqueResult.size() > 1 ) {
NonUniqueResultException nure = new NonUniqueResultException(
"result returns more than one elements"
);
getEntityManager().handlePersistenceException( nure );
throw nure;
}
else {
return uniqueResult.iterator().next();
}
}
else {
return result.get( 0 );
}
}
catch (QueryExecutionRequestException he) {
throw new IllegalStateException( he );
}
catch (TypeMismatchException e) {
throw new IllegalArgumentException( e );
}
catch (HibernateException he) {
throw getEntityManager().convert( he );
}
}
@Override
@SuppressWarnings({"unchecked"})
public T unwrap(Class tClass) {
if ( org.hibernate.Query.class.isAssignableFrom( tClass ) ) {
return (T) query;
}
if ( QueryImpl.class.isAssignableFrom( tClass ) ) {
return (T) this;
}
if ( HibernateQuery.class.isAssignableFrom( tClass ) ) {
return (T) this;
}
throw new PersistenceException(
String.format(
"Unsure how to unwrap %s impl [%s] as requested type [%s]",
Query.class.getSimpleName(),
this.getClass().getName(),
tClass.getName()
)
);
}
@Override
protected void internalApplyLockMode(javax.persistence.LockModeType lockModeType) {
query.getLockOptions().setLockMode( LockModeTypeHelper.getLockMode( lockModeType ) );
if ( getHints() != null && getHints().containsKey( AvailableSettings.LOCK_TIMEOUT ) ) {
applyLockTimeoutHint( ConfigurationHelper.getInteger( getHints().get( AvailableSettings.LOCK_TIMEOUT ) ) );
}
}
@Override
protected boolean applyLockTimeoutHint(int timeout) {
query.getLockOptions().setTimeOut( timeout );
return true;
}
private List list() {
if ( getEntityGraphQueryHint() != null ) {
// Safe to assume QueryImpl at this point.
unwrap( org.hibernate.internal.QueryImpl.class ).applyEntityGraphQueryHint( getEntityGraphQueryHint() );
}
return query.list();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy