Skip to content

Commit

Permalink
MongoDB Native queries
Browse files Browse the repository at this point in the history
  • Loading branch information
DavideD committed Apr 10, 2013
1 parent 64ff305 commit c042637
Show file tree
Hide file tree
Showing 3 changed files with 321 additions and 1 deletion.
Expand Up @@ -25,22 +25,30 @@
import java.util.Map;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.ogm.datastore.mongodb.AssociationStorage;
import org.hibernate.ogm.datastore.mongodb.Environment;
import org.hibernate.ogm.datastore.spi.DatastoreProvider;
import org.hibernate.ogm.dialect.GridDialect;
import org.hibernate.ogm.dialect.mongodb.MongoDBDialect;
import org.hibernate.ogm.grid.EntityKeyMetadata;
import org.hibernate.ogm.logging.mongodb.impl.Log;
import org.hibernate.ogm.logging.mongodb.impl.LoggerFactory;
import org.hibernate.ogm.persister.OgmEntityPersister;
import org.hibernate.service.spi.Configurable;
import org.hibernate.service.spi.Startable;
import org.hibernate.service.spi.Stoppable;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.WriteConcern;
import com.mongodb.ServerAddress;
import com.mongodb.WriteConcern;

/**
* Provides access to MongoDB system
Expand Down Expand Up @@ -198,4 +206,17 @@ private DB extractDatabase() {
}
}

public <T> MongoDBOgmCursor<T> executeNativeQuery(Session session, String sqlString, Class<T> entityType) {
BasicDBObject mongodbQuery = (BasicDBObject) com.mongodb.util.JSON.parse( sqlString );
EntityKeyMetadata entityKeyMetadata = metadata( session.getSessionFactory(), entityType );
DBCollection collection = getDatabase().getCollection( entityKeyMetadata.getTable() );
DBCursor cursor = collection.find( mongodbQuery );
return new MongoDBOgmCursor<T>( session, entityKeyMetadata, entityType, cursor );
}

private EntityKeyMetadata metadata(SessionFactory sessionFactory, Class<?> indexedType) {
OgmEntityPersister persister = (OgmEntityPersister) ( (SessionFactoryImplementor) sessionFactory ).getEntityPersister( indexedType.getName() );
return new EntityKeyMetadata( persister.getTableName(), persister.getRootTableIdentifierColumnNames() );
}

}
@@ -0,0 +1,112 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* JBoss, Home of Professional Open Source
* Copyright 2011 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.hibernate.ogm.datastore.mongodb.impl;

import java.io.Closeable;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.hibernate.LockOptions;
import org.hibernate.Session;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.ogm.datastore.spi.Tuple;
import org.hibernate.ogm.dialect.mongodb.MongoDBTupleSnapshot;
import org.hibernate.ogm.grid.EntityKeyMetadata;
import org.hibernate.ogm.loader.OgmLoader;
import org.hibernate.ogm.loader.OgmLoadingContext;
import org.hibernate.ogm.persister.OgmEntityPersister;

import com.mongodb.DBCursor;
import com.mongodb.DBObject;

/**
* A wrapper for the {@link DBCursor} used in MongoDB, it converts the list of {@link DBObject} to the corresponding
* entities.
*
* @author Davide D'Alto <davide@hibernate.org>
*/
public class MongoDBOgmCursor<T> implements Iterator<T>, Iterable<T>, Closeable {

private final DBCursor cursor;
private final Class<?> entityClass;
private final EntityKeyMetadata entityKeyMetadata;
private final Session session;

/**
* @param session
* the current open session
* @param entityKeyMetadata
* the metadata information of the entity type that is returned by the iterator
* @param entityClass
* the entity class corresponding to the DBObject returned by the cursor
* @param cursor
* the DBCursor from which DBObject are retrieved
*/
public MongoDBOgmCursor(Session session, EntityKeyMetadata entityKeyMetadata, Class<?> entityClass, DBCursor cursor) {
this.session = session;
this.entityClass = entityClass;
this.cursor = cursor;
this.entityKeyMetadata = entityKeyMetadata;
}

@Override
public boolean hasNext() {
return cursor.hasNext();
}

@Override
public T next() {
DBObject dbObject = cursor.next();
Tuple tuple = new Tuple( new MongoDBTupleSnapshot( dbObject, entityKeyMetadata.getColumnNames() ) );
return entity( session, tuple );
}

@Override
public void remove() {
cursor.remove();
}

/**
* Convert the {@link Tuple} to the corresponding entity.
*/
private T entity(Session session, Tuple tuple) {
OgmEntityPersister persister = (OgmEntityPersister) ( (SessionFactoryImplementor) session.getSessionFactory() ).getEntityPersister( entityClass.getName() );
OgmLoader loader = new OgmLoader( new OgmEntityPersister[] { persister } );
OgmLoadingContext ogmLoadingContext = new OgmLoadingContext();
ogmLoadingContext.setTuples( Arrays.asList( tuple ) );
@SuppressWarnings("unchecked")
List<T> entities = (List<T>) loader.loadEntities( (SessionImplementor) session, LockOptions.NONE, ogmLoadingContext );
return entities.get( 0 );
}

@Override
public void close() throws IOException {
cursor.close();
}

@Override
public Iterator<T> iterator() {
return new MongoDBOgmCursor<T>( session, entityKeyMetadata, entityClass, cursor.copy() );
}
}
@@ -0,0 +1,187 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* JBoss, Home of Professional Open Source
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.hibernate.ogm.test.mongodb.nativequery;

import static org.fest.assertions.Assertions.assertThat;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.ogm.datastore.mongodb.impl.MongoDBDatastoreProvider;
import org.hibernate.ogm.datastore.mongodb.impl.MongoDBOgmCursor;
import org.hibernate.ogm.datastore.spi.DatastoreProvider;
import org.hibernate.ogm.hibernatecore.impl.OgmSessionFactory;
import org.hibernate.ogm.test.simpleentity.OgmTestCase;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
* Test that is possible to obtain a list of entities from MongoDB using a native query.
*
* @author Davide D'Alto <davide@hibernate.org>
*/
public class MongoDBNativeQueryTest extends OgmTestCase {

private final OscarWildePoem portia = new OscarWildePoem( 1L, "Portia", "Oscar Wilde" );
private final OscarWildePoem athanasia = new OscarWildePoem( 2L, "Athanasia", "Oscar Wilde" );

@Override
@Before
public void setUp() throws Exception {
super.setUp();
Session session = openSession();
Transaction transaction = session.beginTransaction();
session.persist( portia );
session.persist( athanasia );
transaction.commit();
session.clear();
session.close();
}

@Override
@After
public void tearDown() throws Exception {
Session session = openSession();
Transaction tx = session.beginTransaction();
session.delete( session.load( OscarWildePoem.class, portia.getId() ) );
session.delete( session.load( OscarWildePoem.class, athanasia.getId() ) );
tx.commit();
session.clear();
session.close();
super.tearDown();
}

@Test
public void testSingleResultQuery() throws Exception {
Session session = openSession();
Transaction transaction = session.beginTransaction();

String nativeQuery = "{ $and: [ { name : 'Portia' }, { author : 'Oscar Wilde' } ] }";
MongoDBOgmCursor<OscarWildePoem> result = executeNativeQuery( session, nativeQuery );
OscarWildePoem poem = result.next();

assertAreEquals( portia, poem );
assertThat( result.hasNext() ).as( "Unexpected result returned" ).isFalse();

result.close();
transaction.commit();
session.clear();
session.close();
}

@Test
public void testMultipleResultQuery() throws Exception {
Session session = openSession();
Transaction transaction = session.beginTransaction();

String nativeQuery = "{ $query : { author : 'Oscar Wilde' }, $orderby : { name : 1 } }";
MongoDBOgmCursor<OscarWildePoem> result = executeNativeQuery( session, nativeQuery );

assertAreEquals( athanasia, result.next() );
assertAreEquals( portia, result.next() );
assertThat( result.hasNext() ).as( "Unexpected result returned" ).isFalse();

result.close();
transaction.commit();
session.clear();
session.close();
}

private MongoDBOgmCursor<OscarWildePoem> executeNativeQuery(Session session, String nativeQuery) {
MongoDBDatastoreProvider provider = (MongoDBDatastoreProvider) registry( session ).getService( DatastoreProvider.class );
return provider.executeNativeQuery( session, nativeQuery, OscarWildePoem.class );
}

private void assertAreEquals(OscarWildePoem expectedPoem, OscarWildePoem poem) {
assertThat( poem ).isNotNull();
assertThat( poem.getId() ).as( "Wrong Id" ).isEqualTo( expectedPoem.getId() );
assertThat( poem.getName() ).as( "Wrong Name" ).isEqualTo( expectedPoem.getName() );
assertThat( poem.getAuthor() ).as( "Wrong Author" ).isEqualTo( expectedPoem.getAuthor() );
}

private ServiceRegistryImplementor registry(Session session) {
return ( (OgmSessionFactory) session.getSessionFactory() ).getServiceRegistry();
}

@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { OscarWildePoem.class };
}

@Entity
@Table(name = OscarWildePoem.TABLE_NAME)
static class OscarWildePoem {

/* The MongoDB driver currently doesn't like the $ char in the default name */
public static final String TABLE_NAME = "WILDE_POEM";

private Long id;

private String name;

private String author;

public OscarWildePoem() {
}

public OscarWildePoem(Long id, String name, String author) {
this.id = id;
this.name = name;
this.author = author;
}

@Id
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getAuthor() {
return author;
}

public void setAuthor(String author) {
this.author = author;
}

@Override
public String toString() {
return "OscarWildePoem [id=" + id + ", name=" + name + ", author=" + author + "]";
}

}
}

0 comments on commit c042637

Please sign in to comment.