概述

Postgres 和 Oracle 广泛支持存储在数据库中的 JSON。两者都为使用文档路径表达式编写查询提供了大量支持。

Ebean 通过 @DbJson@DbJsonB 提供映射支持,以将 JSON 文档映射到各种数据库类型,包括 Postgres JSON、JSONB 类型以及标准 Varchar、Clob 和 Blob 类型。该映射可与所有受支持的数据库一起使用,而 Postgres 和 Oracle 支持在查询 where 子句中包含 JSON 表达式。

Postgres 和 Oracle 都支持使用 JSON 表达式编写查询,而 Ebean 公开了最常见的查询表达式。

映射

对于所有数据库,JSON 文档都可以映射到实体 Bean,并可以从数据库中保存和加载。

@DbJsonB

在实体 Bean 中,可以使用 @DbJsonB 对属性进行注释,这表示该属性映射到 JSONB 数据库类型。对于非 Postgres 数据库,这映射到数据库 Clob。

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

  @Id
  Long id;
  ...

  @DbJsonB
  Map<String,Object> content;

  // Ordinary bean - use Jackson object mapper
  @DbJsonB
  PlainBean plainBean;

@DbJson

在实体 Bean 中,可以使用 @DbJson 对属性进行注释,这表示该属性映射到 JSON 数据库类型。对于非 Postgres 数据库,这映射到数据库 Clob。

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

  @Id
  Long id;
  ...

  @DbJson
  Map<String,Object> content;

  // Ordinary bean - use Jackson object mapper
  @DbJson
  PlainBean plainBean;

保存和查找

将 JSON 内容放入内容属性并保存,然后像往常一样获取。

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"));

查询表达式

PostgresOracle 提供表达式,以便可以使用 path 表达式来测试给定的路径 EXISTS 或测试路径中的值。

Ebean ExpressionList 具有以下表达式的表达式

/**
 * 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 表达式

Ebean 使用 Postgres ->>#>> 运算符来支持 JSON 表达式。

Exists 表达式

对于 Postgres Ebean 的 jsonExists() jsonNotExists() 表达式,路径值使用 ->>#>>IS NULLIS NOT NULL

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

该函数可用于 Ebean 查询中,如下所示

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

值表达式

对于 Postgres,Ebean 的值表达式(例如 jsonEqualTo()jsonGreaterThan() 等)根据路径使用 ->>#>> 运算符,然后根据要测试的值转换结果。也就是说,如果要测试的值是 Integer 或 Long,则 DB 路径表达式将转换为 ::INTEGER,并且 ::DECIMAL::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 表达式

Ebean 使用 Oracle 的 json_existsjson_value 函数来支持 Ebean 的 JSON 表达式。

Exists 表达式

对于 Oracle,Ebean 的 jsonExists() 表达式使用 Oracle json_exists 函数。

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

该函数可用于 Ebean 查询中,如下所示

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

值表达式

对于 Oracle,Ebean 的值 jsonEqualTo() jsonGreaterThan() 等使用 Oracle json_value 函数。

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() 表达式。

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();