Hibernate.orgCommunity Documentation
Once your data is in the datastore, it’s time for some query fun! With Hibernate OGM, you have a few alternatives that should get you covered:
For Hibernate OGM, we developed a brand new JP-QL parser which is already able to convert simple queries into the native underlying datastore query language (e.g. MongoQL for MongoDB, CypherQL for Neo4J, etc). This parser can also generate Hibernate Search queries for datastores that do not support a query language.
For datastores like Infinispan that require Hibernate Search to execute JP-QL queries, the following preconditions must be met:
Here is an example:
@Entity @Indexed
public class Hypothesis {
@Id
public String getId() { return id; }
public void setId(String id) { this.id = id; }
private String id;
@Field(analyze=Analyze.NO)
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
private String description;
}
Query query = session
.createQuery("from Hypothesis h where h.description = :desc")
.setString("desc", "tomorrow it's going to rain");
Note that the description
field is marked as not analysed.
This is necessary to support field equality and comparison as defined by JP-QL.
You can make use of the following JP-QL constructs:
IS NULL
and IS NOT NULL
AND
, OR
, NOT
LIKE
, IN
and BETWEEN
ORDER BY
In particular and of notice, what is not supported is:
That may sound rather limiting for your use cases so bear with us. This is a hot area we want to improve, please tell us what feature you miss by opening a JIRA or via email. Also read the next section, you will see other alternatives to implement your queries.
Let’s look at some of the queries you can express in JP-QL:
Example 7.1. Some JP-QL queries
// query returning an entity based on a simple predicate select h from Hypothesis h where id = 16 // projection of the entity property select id, description from Hypothesis h where id = 16 // projection of the embedded properties select h.author.address.street from Hypothesis h where h.id = 16 // predicate comparing a property value and a literal from Hypothesis h where h.position = '2' // negation from Hypothesis h where not h.id = '13' from Hypothesis h where h.position <> 4 // conjunction from Hypothesis h where h.position = 2 and not h.id = '13' // named parameters from Hypothesis h where h.description = :myParam // range query from Hypothesis h where h.description BETWEEN :start and :end" // comparisons from Hypothesis h where h.position < 3 // in from Hypothesis h where h.position IN (2, 3, 4) // like from Hypothesis h where h.description LIKE '%dimensions%' // comparison with null from Hypothesis h where h.description IS null // order by from Hypothesis h where h.description IS NOT null ORDER BY id from Helicopter h order by h.make desc, h.name
In order to reflect changes performed in the current session, all entities affected by a given query are flushed to the datastore prior to query execution (that’s the case for Hibernate ORM as well as Hibernate OGM).
For not fully transactional stores, this can cause changes to be written as a side-effect of running queries which cannot be reverted by a possible later rollback.
Depending on your specific use cases and requirements you may prefer to disable auto-flushing,
e.g. by invoking query.setFlushMode(FlushMode.MANUAL)
.
Bear in mind though that query results will then not reflect changes applied within the current session.
Often you want the raw power of the underlying NoSQL query engine. Even if that costs you portability.
Hibernate OGM addresses that problem by letting you express native queries (e.g. in MongoQL or CypherQL) and map the result of these queries as mapped entities.
In JPA, use EntityManager.createNativeQuery
.
The first form accepts a result class if your result set maps the mapping definition of the entity.
The second form accepts the name of a resultSetMapping
and lets you customize how properties are mapped to columns by the query.
You can also used a predefined named query which defines its result set mapping.
Let’s take a look at how it is done for Neo4J:
Example 7.2. Various ways to create a native query in JPA
@Entity
@NamedNativeQuery(
name = "AthanasiaPoem",
query = "{ $and: [ { name : 'Athanasia' }, { author : 'Oscar Wilde' } ] }",
resultClass = Poem.class )
public class Poem {
@Id
private Long id;
private String name;
private String author;
// getters, setters ...
}
...
javax.persistence.EntityManager em = ...
// a single result query
String query1 = "MATCH ( n:Poem { name:'Portia', author:'Oscar Wilde' } ) RETURN n";
Poem poem = (Poem) em.createNativeQuery( query1, Poem.class ).getSingleResult();
// query with order by
String query2 = "MATCH ( n:Poem { name:'Portia', author:'Oscar Wilde' } ) " +
"RETURN n ORDER BY n.name";
List<Poem> poems = em.createNativeQuery( query2, Poem.class ).getResultList();
// query with projections
String query3 = MATCH ( n:Poem ) RETURN n.name, n.author ORDER BY n.name";
List<Object[]> poemNames = (List<Object[]>)em.createNativeQuery( query3 )
.getResultList();
// named query
Poem poem = (Poem) em.createNamedQuery( "AthanasiaPoem" ).getSingleResult();
In the native Hibernate API, use OgmSession.createNativeQuery
or Session.getNamedQuery
.
The former form lets you define the result set mapping programmatically.
The latter is receiving the name of a predefined query already describing its result set mapping.
Example 7.3. Hibernate API defining a result set mapping
OgmSession session = ...
String query1 = "{ $and: [ { name : 'Portia' }, { author : 'Oscar Wilde' } ] }";
Poem poem = session.createNativeQuery( query1 )
.addEntity( "Poem", Poem.class )
.uniqueResult();
Check out each individual datastore chapter for more info on the specifics of the native query language mapping. In particular Neo4J and MongoDB.
Hibernate Search offers a way to index Java objects into Lucene indexes and to execute full-text queries on them. The indexes do live outside your datastore. This offers a few interesting properties in terms of feature set and scalability.
Apache Lucene is a full-text indexing and query engine with excellent query performance. Feature wise, full-text means you can do much more than a simple equality match.
Hibernate Search natively integrates with Hibernate ORM. And Hibernate OGM of course!
Example 7.4. Using Hibernate Search for full-text matching
@Entity @Indexed
public class Hypothesis {
@Id
public String getId() { return id; }
public void setId(String id) { this.id = id; }
private String id;
@Field(analyze=Analyze.YES)
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
private String description;
}
EntityManager entityManager = ...
//Add full-text superpowers to any EntityManager:
FullTextEntityManager ftem = Search.getFullTextEntityManager(entityManager);
//Optionally use the QueryBuilder to simplify Query definition:
QueryBuilder b = ftem.getSearchFactory()
.buildQueryBuilder()
.forEntity(Hypothesis.class)
.get();
//Create a Lucene Query:
Query lq = b.keyword().onField("description").matching("tomorrow").createQuery();
//Transform the Lucene Query in a JPA Query:
FullTextQuery ftQuery = ftem.createFullTextQuery(lq, Hypothesis.class);
//List all matching Hypothesis:
List<Hypothesis> resultList = ftQuery.getResultList();
Assuming our database contains an Hypothesis
instance
having description "Sometimes tomorrow we release",
that instance will be returned by our full-text query.
Text similarity can be very powerful as it can be configured for specific languages or domain specific terminology; it can deal with typos and synonyms, and above all it can return results by relevance.
Worth noting the Lucene index is a vectorial space of term occurrence statistics: so extracting tags from text, frequencies of strings and correlate this data makes it very easy to build efficient data analysis applications.
While the potential of Lucene queries is very high, it’s not suited for all use cases Let’s see some of the limitations of Lucene Queries as our main query engine:
to-One
relations can be mapped fine,
and the Lucene community is making progress on other forms,
but restrictions on OneToMany
or ManyToMany
can’t be implemented today.For a complete understanding of what Hibernate Search can do for you and how to use it, go check the Hibernate Search reference documentation.