/*
* Hibernate OGM, Domain model persistence for NoSQL datastores
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.ogm.hibernatecore.impl;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.Criteria;
import org.hibernate.Filter;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.NaturalIdLoadAccess;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.ScrollableResults;
import org.hibernate.SessionException;
import org.hibernate.SharedSessionBuilder;
import org.hibernate.SimpleNaturalIdLoadAccess;
import org.hibernate.cache.spi.CacheKey;
import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
import org.hibernate.engine.query.spi.sql.NativeSQLQuerySpecification;
import org.hibernate.engine.spi.ActionQueue;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.NamedQueryDefinition;
import org.hibernate.engine.spi.NamedSQLQueryDefinition;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionDelegatorBaseImpl;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.AutoFlushEvent;
import org.hibernate.event.spi.AutoFlushEventListener;
import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.EventType;
import org.hibernate.internal.SessionImpl;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import org.hibernate.loader.custom.CustomQuery;
import org.hibernate.ogm.OgmSession;
import org.hibernate.ogm.OgmSessionFactory;
import org.hibernate.ogm.datastore.spi.DatastoreConfiguration;
import org.hibernate.ogm.exception.NotSupportedException;
import org.hibernate.ogm.loader.nativeloader.BackendCustomQuery;
import org.hibernate.ogm.options.navigation.GlobalContext;
import org.hibernate.ogm.query.NoSQLQuery;
import org.hibernate.ogm.query.impl.NoSQLQueryImpl;
import org.hibernate.ogm.util.impl.Log;
import org.hibernate.ogm.util.impl.LoggerFactory;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.procedure.ProcedureCall;
import org.hibernate.type.Type;
/**
* Delegate most of the work to the underlying Hibernate Session
* except that queries are redirected to our own engine
*
* @author Emmanuel Bernard <emmanuel@hibernate.org>
*/
public class OgmSessionImpl extends SessionDelegatorBaseImpl implements OgmSession, EventSource {
private static final Log log = LoggerFactory.make();
private final EventSource delegate;
private final OgmSessionFactoryImpl factory;
public OgmSessionImpl(OgmSessionFactory factory, EventSource delegate) {
super( delegate, delegate );
this.delegate = delegate;
this.factory = (OgmSessionFactoryImpl) factory;
}
//Overridden methods
@Override
public SessionFactoryImplementor getFactory() {
return factory;
}
@Override
public OgmSessionFactory getSessionFactory() {
return factory;
}
@Override
public Criteria createCriteria(Class persistentClass) {
//TODO plug the Lucene engine
throw new NotSupportedException( "OGM-23", "Criteria queries are not supported yet" );
}
@Override
public Criteria createCriteria(Class persistentClass, String alias) {
//TODO plug the Lucene engine
throw new NotSupportedException( "OGM-23", "Criteria queries are not supported yet" );
}
@Override
public Criteria createCriteria(String entityName) {
//TODO plug the Lucene engine
throw new NotSupportedException( "OGM-23", "Criteria queries are not supported yet" );
}
@Override
public Criteria createCriteria(String entityName, String alias) {
//TODO plug the Lucene engine
throw new NotSupportedException( "OGM-23", "Criteria queries are not supported yet" );
}
@Override
public Query createQuery(NamedQueryDefinition namedQueryDefinition) {
String queryString = namedQueryDefinition.getQueryString();
Query query = createQuery( queryString );
query.setComment( "named HQL/JP-QL query " + namedQueryDefinition.getName() );
query.setFlushMode( namedQueryDefinition.getFlushMode() );
return query;
}
@Override
public NoSQLQuery createSQLQuery(String queryString) throws HibernateException {
return createNativeQuery( queryString );
}
@Override
public SQLQuery createSQLQuery(NamedSQLQueryDefinition namedQueryDefinition) {
return createNativeQuery( namedQueryDefinition.getQuery() );
}
@Override
public NoSQLQuery createNativeQuery(String nativeQuery) {
errorIfClosed();
return new NoSQLQueryImpl(
nativeQuery,
this,
factory.getQueryPlanCache().getSQLParameterMetadata( nativeQuery )
);
}
@Override
public Query createFilter(Object collection, String queryString) throws HibernateException {
//TODO plug the Lucene engine
throw new NotSupportedException( "OGM-24", "filters are not supported yet" );
}
@Override
public Filter enableFilter(String filterName) {
throw new NotSupportedException( "OGM-25", "filters are not supported yet" );
}
@Override
public Filter getEnabledFilter(String filterName) {
return delegate.getEnabledFilter( filterName );
}
@Override
public void disableFilter(String filterName) {
throw new NotSupportedException( "OGM-25", "filters are not supported yet" );
}
@Override
public void doWork(Work work) throws HibernateException {
throw new IllegalStateException( "Hibernate OGM does not support SQL Connections hence no Work" );
}
@Override
public <T> T doReturningWork(ReturningWork<T> work) throws HibernateException {
return delegate.doReturningWork( work );
}
@Override
public ProcedureCall getNamedProcedureCall(String name) {
throw new NotSupportedException( "OGM-359", "Stored procedures are not supported yet" );
}
@Override
public ProcedureCall createStoredProcedureCall(String procedureName) {
throw new NotSupportedException( "OGM-359", "Stored procedures are not supported yet" );
}
@Override
public ProcedureCall createStoredProcedureCall(String procedureName, Class... resultClasses) {
throw new NotSupportedException( "OGM-359", "Stored procedures are not supported yet" );
}
@Override
public ProcedureCall createStoredProcedureCall(String procedureName, String... resultSetMappings) {
throw new NotSupportedException( "OGM-359", "Stored procedures are not supported yet" );
}
//Event Source methods
@Override
public ActionQueue getActionQueue() {
return delegate.getActionQueue();
}
@Override
public Object instantiate(EntityPersister persister, Serializable id) throws HibernateException {
return delegate.instantiate( persister, id );
}
@Override
public void forceFlush(EntityEntry e) throws HibernateException {
delegate.forceFlush( e );
}
@Override
public void merge(String entityName, Object object, Map copiedAlready) throws HibernateException {
delegate.merge( entityName, object, copiedAlready );
}
@Override
public void persist(String entityName, Object object, Map createdAlready) throws HibernateException {
delegate.persist( entityName, object, createdAlready );
}
@Override
public void persistOnFlush(String entityName, Object object, Map copiedAlready) {
delegate.persistOnFlush( entityName, object, copiedAlready );
}
@Override
public void refresh(String entityName, Object object, Map refreshedAlready) throws HibernateException {
delegate.refresh( entityName, object, refreshedAlready );
}
@Override
public void delete(String entityName, Object child, boolean isCascadeDeleteEnabled, Set transientEntities) {
delegate.delete( entityName, child, isCascadeDeleteEnabled, transientEntities );
}
@Override
public JdbcConnectionAccess getJdbcConnectionAccess() {
return delegate.getJdbcConnectionAccess();
}
@Override
public EntityKey generateEntityKey(Serializable id, EntityPersister persister) {
return delegate.generateEntityKey( id, persister );
}
@Override
public CacheKey generateCacheKey(Serializable id, Type type, String entityOrRoleName) {
return delegate.generateCacheKey( id, type, entityOrRoleName );
}
@Override
public List<?> listCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) throws HibernateException {
errorIfClosed();
if ( log.isTraceEnabled() ) {
log.tracev( "NoSQL query: {0}", customQuery.getSQL() );
}
BackendCustomLoader loader = new BackendCustomLoader( (BackendCustomQuery<?>) customQuery, getFactory() );
autoFlushIfRequired( loader.getQuerySpaces() );
return loader.list( getDelegate(), queryParameters );
}
/**
* detect in-memory changes, determine if the changes are to tables named in the query and, if so, complete
* execution the flush
* <p>
* NOTE: Copied as-is from {@link SessionImpl}. We need it here as
* {@link #listCustomQuery(CustomQuery, QueryParameters)} needs to be customized (which makes use of auto flushes)
* to work with our custom loaders.
*/
private boolean autoFlushIfRequired(Set<String> querySpaces) throws HibernateException {
errorIfClosed();
if ( ! isTransactionInProgress() ) {
// do not auto-flush while outside a transaction
return false;
}
AutoFlushEvent event = new AutoFlushEvent( querySpaces, this );
for ( AutoFlushEventListener listener : listeners( EventType.AUTO_FLUSH ) ) {
listener.onAutoFlush( event );
}
return event.isFlushRequired();
}
private <T> Iterable<T> listeners(EventType<T> type) {
return eventListenerGroup( type ).listeners();
}
private <T> EventListenerGroup<T> eventListenerGroup(EventType<T> type) {
return factory.getServiceRegistry().getService( EventListenerRegistry.class ).getEventListenerGroup( type );
}
@Override
public ScrollableResults scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
throws HibernateException {
return delegate.scrollCustomQuery( customQuery, queryParameters );
}
@Override
public List<?> list(NativeSQLQuerySpecification spec, QueryParameters queryParameters) throws HibernateException {
return listCustomQuery(
factory.getQueryPlanCache().getNativeSQLQueryPlan( spec ).getCustomQuery(),
queryParameters
);
}
@Override
public ScrollableResults scroll(NativeSQLQuerySpecification spec, QueryParameters queryParameters)
throws HibernateException {
return delegate.scroll( spec, queryParameters );
}
//SessionImplementor methods
@Override
public Query getNamedQuery(String name) {
errorIfClosed();
NamedQueryDefinition namedQuery = factory.getNamedQuery( name );
//ORM looks for native queries when no HQL definition is found, we do the same here.
if (namedQuery == null) {
return getNamedSQLQuery( name );
}
return createQuery( namedQuery );
}
@Override
public Query getNamedSQLQuery(String queryName) {
errorIfClosed();
NamedSQLQueryDefinition nsqlqd = findNamedNativeQuery( queryName );
Query query = new NoSQLQueryImpl(
nsqlqd,
this,
factory.getQueryPlanCache().getSQLParameterMetadata( nsqlqd.getQuery() )
);
query.setComment( "named native query " + queryName );
return query;
}
private NamedSQLQueryDefinition findNamedNativeQuery(String queryName) {
NamedSQLQueryDefinition nsqlqd = factory.getNamedSQLQuery( queryName );
if ( nsqlqd == null ) {
throw new MappingException( "Named native query not found: " + queryName );
}
return nsqlqd;
}
@Override
public SharedSessionBuilder sessionWithOptions() {
return new OgmSharedSessionBuilderDelegator( delegate.sessionWithOptions(), factory );
}
//Copied from org.hibernate.internal.AbstractSessionImpl.errorIfClosed()
//to mimic same behaviour
protected void errorIfClosed() {
if ( delegate.isClosed() ) {
throw new SessionException( "Session is closed!" );
}
}
public <G extends GlobalContext<?, ?>, D extends DatastoreConfiguration<G>> G configureDatastore(Class<D> datastoreType) {
throw new UnsupportedOperationException( "OGM-343 Session specific options are not currently supported" );
}
@Override
public void removeOrphanBeforeUpdates(String entityName, Object child) {
delegate.removeOrphanBeforeUpdates( entityName, child );
}
/**
* Returns the underlying ORM session to which most work is delegated.
*/
public EventSource getDelegate() {
return delegate;
}
@Override
public NaturalIdLoadAccess byNaturalId(Class entityClass) {
throw new UnsupportedOperationException( "OGM-589 - Natural id look-ups are not yet supported" );
}
@Override
public NaturalIdLoadAccess byNaturalId(String entityName) {
throw new UnsupportedOperationException( "OGM-589 - Natural id look-ups are not yet supported" );
}
@Override
public SimpleNaturalIdLoadAccess bySimpleNaturalId(Class entityClass) {
throw new UnsupportedOperationException( "OGM-589 - Natural id look-ups are not yet supported" );
}
@Override
public SimpleNaturalIdLoadAccess bySimpleNaturalId(String entityName) {
throw new UnsupportedOperationException( "OGM-589 - Natural id look-ups are not yet supported" );
}
}