Overview

Postgres and Oracle have extensive support with JSON stored in the database. Both provide a lot of support for writing queries using document path expressions.

Ebean provides mapping support via @DbJson and @DbJsonB to map JSON documents to various database types including Postgres JSON, JSONB types as well as standard Varchar, Clob and Blob types. The mapping can be used with all supported databases and Postgres and Oracle provide support for including JSON expressions in query where clauses.

Both Postgres and Oracle support writing queries with JSON expressions and Ebean exposes the most common query expressions.:

Mapping

For all databases JSON documents can be mapped on entity beans and saved and loaded from the database.

@DbJsonB

In your entity bean you can annotate a property with @DbJsonB and that indicates that the property maps to a JSONB database type. For non-Postgres databases this maps to a database Clob.

@Entity
@Table(name="p_doc")
public class SimpleDoc extends Model {

  @Id
  Long id;
  ...

  @DbJsonB
  Map<String,Object> content;
  

@DbJson

In your entity bean you can annotate a property with @DbJson and that indicates that the property maps to a JSON database type. For non-Postgres databases this maps to a database Clob.

@Entity
@Table(name="p_doc")
public class SimpleDoc extends Model {

  @Id
  Long id;
  ...

  @DbJson
  Map<String,Object> content;
  

Save and Find

You put JSON content into the content property and save it and fetch is as normal.

String rawJson = "{\"docName\":\"My document\", \"docScore\":234, \"title\":\"Some title\"}";

// get the JSON into a map using Jackson or similar tool.
// Ebean has EJson which using Jackson core which can be used
// to parse JSON content
Map<String, Object> content = EJson.parseObject(rawJson);

SimpleDoc doc = new SimpleDoc();
doc.setName("doc1");
doc.setContent(content);

// save to db
doc.save();

// fetch from db
SimpleDoc doc1 = SimpleDoc.find.byId(doc.getId());

assertEquals("My document", doc1.getContent().get("docName"));

Query expressions

Postgres and Oracle provide expressions such that you can use a path expression to test is a given path EXISTS or test the value at a path.

Ebean ExpressionList has expressions for:

/**
 * Path exists - for the given path in a JSON document.
 */
ExpressionList<T> jsonExists(String propertyName, String path);

/**
 * Path does not exist - for the given path in a JSON document.
 */
ExpressionList<T> jsonNotExists(String propertyName, String path);

/**
 * Equal to expression for the value at the given path in the JSON document.
 */
ExpressionList<T> jsonEqualTo(String propertyName, String path, Object value);

/**
 * Not Equal to - for the given path in a JSON document.
 */
ExpressionList<T> jsonNotEqualTo(String propertyName, String path, Object val);

/**
 * Greater than - for the given path in a JSON document.
 */
ExpressionList<T> jsonGreaterThan(String propertyName, String path, Object val);

/**
 * Greater than or equal to - for the given path in a JSON document.
 */
ExpressionList<T> jsonGreaterOrEqual(String propertyName, String path, Object val);

/**
 * Less than - for the given path in a JSON document.
 */
ExpressionList<T> jsonLessThan(String propertyName, String path, Object val);

/**
 * Less than or equal to - for the given path in a JSON document.
 */
ExpressionList<T> jsonLessOrEqualTo(String propertyName, String path, Object val);

Postgres expressions

Ebean uses Postgres ->> and #>> operators to support the JSON expressions.

Exists expression

For Postgres Ebean's jsonExists() jsonNotExists() expression result in the path value using ->> or #>> and IS NULL and IS NOT NULL.

List<SimpleDoc> list = new QSimpleDoc().query()
.where()
.jsonExists("content", "path.other")
.findList();

The function can then be used in an Ebean query like:

select t0.id c0, ...
where (t0.content #>> '{path,other}') is not null

Value expressions

For Postgres Ebean's value expressions such as jsonEqualTo(), jsonGreaterThan() etc use the ->> or #>> operator depending on the path and then cast the result depending on the value testing against. That is, if the value tested is a Integer or Long the DB path expression is cast to ::INTEGER and there is similar casting for ::DECIMAL and ::BOOLEAN.

List<SimpleDoc> list = new QSimpleDoc().query()
  .where()
  .jsonEqualTo("content", "path.other", 34)
  .findList();
  
select t0.id c0, ...
where (t0.content #>> '{path,other}')::INTEGER = ?

Oracle expressions

Ebean uses Oracles json_exists and json_value functions to support Ebean's JSON expressions.

Exists expression

For Oracle Ebean's jsonExists() expression uses the Oracle json_exists function.

List<SimpleDoc> list = new QSimpleDoc().query()
  .where()
  .jsonExists("content", "path.other")
  .findList();

The function can then be used in an Ebean query like:

select t0.id c0, ...
where json_exists(t0.content, '$.path.other')

Value expressions

For Oracle Ebean's value's jsonEqualTo() jsonGreaterThan() etc use the Oracle json_value function.

List<SimpleDoc> list = new QSimpleDoc().query()
  .where()
  .jsonEqualTo("content", "path.other", 34)
  .findList();
  
select t0.id c0, ...
where json_value(t0.content, '$.path.other') = ?

Raw expression

If the provided expressions do not match what is required you can use raw() expressions.

List<SimpleDoc> docs = SimpleDoc.find.where()
  // pass a raw expression through - property names are translated to
  // db columns but everything else is passed through to the DB
  .raw("content#>'{docName}' ? 'rob doc'")
  .findList();