POSJSONHELPER库是一个开源项目,为PostgreSQL JSON功能提供了Hibernate查询的支持。库还支持PostgreSQL文本搜索功能。要了解如何使用文本搜索组件的更多信息,请检查文本模块的说明。该库是用Java编程语言编写的。这一刻的项目支持Hibernate版本5和6。Java的必需版本至少是Hibernate 5 Support和Hibernate 6版本的第8版。
该项目可在中央Maven存储库中找到。您可以通过将其添加为项目描述符文件(pom.xml)中的依赖项来使用它。
对于冬眠5:
< dependency >
< groupId >com.github.starnowski.posjsonhelper</ groupId >
< artifactId >hibernate5</ artifactId >
< version >0.4.2</ version >
</ dependency >对于冬眠6:
< dependency >
< groupId >com.github.starnowski.posjsonhelper</ groupId >
< artifactId >hibernate6</ artifactId >
< version >0.4.2</ version >
</ dependency >POSJSONHELPER库没有瞬态依赖性对Hibernate库。因此,请记住,必须在您的项目中分别添加Hibernate依赖性,如下所示:
< dependency >
< groupId >org.hibernate</ groupId >
< artifactId >hibernate-core</ artifactId >
< version >????</ version >
</ dependency >请检查Hibernate兼容性版本矩阵以检查正确的版本。
与JSON操作相关的某些功能的默认实现要求org.json:JSON库。但是,有一些方法可以实现特定的接口,而在库下方可能不需要添加。
< dependency >
< groupId >org.json</ groupId >
< artifactId >json</ artifactId >
< version >20240303</ version >
</ dependency >如果有人想从源头本地构建该项目,请参阅contruting.md文件,以检查如何在本地设置该项目。
重要的!本节仅适用于Hibernate 5。要能够使用项目中的PosJsonHelper库,则必须指定正确的Hibernate方言。图书馆实现了很少有扩展的包装器,该包装器已经存在于PostgreSQL的Hibernate方言:
方言必须在Hibernate配置文件中设置:例如:
<? xml version = " 1.0 " encoding = " UTF-8 " ?>
< hibernate-configuration xmlns = " http://www.hibernate.org/xsd/orm/cfg " >
< session-factory >
< property name = " hibernate.dialect " >com.github.starnowski.posjsonhelper.hibernate5.dialects.PostgreSQL95DialectWrapper</ property >
...或例如在春季框架配置属性文件中:
...
spring.jpa.properties.hibernate.dialect =com.github.starnowski.posjsonhelper.hibernate5.dialects.PostgreSQL95DialectWrapper
...如果您已经具有扩展冬眠方言类型的类型,并且您的项目需要。您可以为您的类型添加调整,以便它使用Postgresqledialectenricher组件。
import com . github . starnowski . posjsonhelper . hibernate5 . PostgreSQLDialectEnricher ;
import org . hibernate . dialect . PostgreSQL95Dialect ;
public class PostgreSQLDialectWithDifferentSchema extends PostgreSQL95Dialect {
public PostgreSQLDialectWithDifferentSchema () {
PostgreSQLDialectEnricher enricher = new PostgreSQLDialectEnricher ();
enricher . enrich ( this );
}
}重要的!本节仅适用于Hibernate 6。要使用使用Hibernate 6的项目中的posjsonhelper库,必须有一个指定的org.hibernate.boot.model.model.functioncontributor实现。库具有该界面的实现,即com.github.starnowski.posjsonhelper.hibernate6.posjsonhelperfunctioncontributor。
要使用此实现,需要使用名称“ org.hibernate.boot.model.functionContributor”创建文件。
替代解决方案是使用com.github.starnowski.posjsonhelper.hibernate6.sqmfunctionregistrymistrymenricher组件在应用程序启动期间。就像在下面的示例中使用弹簧框架的使用一样。
import com . github . starnowski . posjsonhelper . hibernate6 . SqmFunctionRegistryEnricher ;
import jakarta . persistence . EntityManager ;
import org . hibernate . query . sqm . NodeBuilder ;
import org . springframework . beans . factory . annotation . Autowired ;
import org . springframework . context . ApplicationListener ;
import org . springframework . context . annotation . Configuration ;
import org . springframework . context . event . ContextRefreshedEvent ;
@ Configuration
public class FunctionDescriptorConfiguration implements
ApplicationListener < ContextRefreshedEvent > {
@ Autowired
private EntityManager entityManager ;
@ Override
public void onApplicationEvent ( ContextRefreshedEvent event ) {
NodeBuilder nodeBuilder = ( NodeBuilder ) entityManager . getCriteriaBuilder ();
SqmFunctionRegistryEnricher sqmFunctionRegistryEnricher = new SqmFunctionRegistryEnricher ();
sqmFunctionRegistryEnricher . enrich ( nodeBuilder . getQueryEngine (). getSqmFunctionRegistry ());
}
}要使用POSJSONHELPER库,需要创建一些执行JSON运算符的SQL函数。一些JSON操作员不能被冬眠执行,因为必须逃脱它们。对于默认配置,库需要以下功能创建以下功能。
CREATE OR REPLACE FUNCTION jsonb_all_array_strings_exist (jsonb, text []) RETURNS boolean AS $$
SELECT $ 1 ?& $ 2 ;
$$ LANGUAGE SQL;
CREATE OR REPLACE FUNCTION jsonb_any_array_strings_exist (jsonb, text []) RETURNS boolean AS $$
SELECT $ 1 ?| $ 2 ;
$$ LANGUAGE SQL;可以在集成测试期间执行生成的DDL语句,也可以由对数据库(例如Liquibase或Flyway)应用更改的工具使用。重要的!如果需要使用相似的功能,但具有不同的名称,则必须在应用程序属性中指定。之所以需要,是因为类型扩展了冬眠方言类型,即“如何附加postgresql方言”](#操作对象 - 触及 - 触及postgresql方言)部分可能无法访问应用程序上下文(IOC)。但是,如果应以不同的方式传递此类属性,则PostgreSqledialectenricher类型也具有传递上下文对象的方法(请检查核心上下文和Hibernate上下文)
上下文类保存图书馆使用的功能的名称。方言类使用CoreContextPropertiessupplier组件,该组件基于系统属性生成上下文对象。
也可以使用DatabaseOperationExecutorFacade类型来编程添加DDL。下面有有关如何使用弹簧框架上下文应用DDL更改的示例。
import com . github . starnowski . posjsonhelper . core . Context ;
import com . github . starnowski . posjsonhelper . core . DatabaseOperationExecutorFacade ;
import com . github . starnowski . posjsonhelper . core . DatabaseOperationType ;
import org . springframework . beans . factory . annotation . Autowired ;
import org . springframework . context . ApplicationListener ;
import org . springframework . context . annotation . Configuration ;
import org . springframework . context . event . ContextRefreshedEvent ;
import javax . sql . DataSource ;
@ Configuration
public class SQLFunctionsConfiguration implements
ApplicationListener < ContextRefreshedEvent > {
@ Autowired
private Context context ;
@ Autowired
private DataSource dataSource ;
@ Override
public void onApplicationEvent ( ContextRefreshedEvent contextRefreshedEvent ) {
DatabaseOperationExecutorFacade facade = new DatabaseOperationExecutorFacade ();
try {
facade . execute ( dataSource , context , DatabaseOperationType . LOG_ALL );
facade . execute ( dataSource , context , DatabaseOperationType . CREATE );
facade . execute ( dataSource , context , DatabaseOperationType . VALIDATE );
} catch ( Exception e ) {
throw new RuntimeException ( "Error during initialization of sql functions for jsonb type operations" , e );
}
}
}有一些操作可以由数据库executorfacade对象执行
| 属性名称 | 描述 |
|---|---|
| 创造 | 将DDL更改应用于数据库 |
| 证实 | 验证是否将DDL更改应用于数据库 |
| 降低 | 丢弃数据库中的DDL更改 |
| log_all | 显示用于创建,验证和删除操作的DDL脚本 |
为了简化说明,让我们假设我们有一个数据库表,其中一列存储JSONB类型。
create table item (
id int8 not null ,
jsonb_content jsonb,
primary key (id)
)对于此表,我们可以使用任何JSON插入行,如以下示例:
INSERT INTO item (id, jsonb_content) VALUES ( 1 , ' {"top_element_with_set_of_values":["TAG1","TAG2","TAG11","TAG12","TAG21","TAG22"]} ' );
INSERT INTO item (id, jsonb_content) VALUES ( 2 , ' {"top_element_with_set_of_values":["TAG3"]} ' );
-- item without any properties, just an empty json
INSERT INTO item (id, jsonb_content) VALUES ( 6 , ' {} ' );
-- int values
INSERT INTO item (id, jsonb_content) VALUES ( 7 , ' {"integer_value": 132} ' );
-- double values
INSERT INTO item (id, jsonb_content) VALUES ( 10 , ' {"double_value": 353.01} ' );
INSERT INTO item (id, jsonb_content) VALUES ( 11 , ' {"double_value": -1137.98} ' );
-- enum values
INSERT INTO item (id, jsonb_content) VALUES ( 13 , ' {"enum_value": "SUPER"} ' );
-- string values
INSERT INTO item (id, jsonb_content) VALUES ( 18 , ' {"string_value": "the end of records"} ' );大多数谓词组件都使用Hibernate上下文对象。它主要保存项目中使用的Hibernate功能名称的名称。方言类和函数contributor类型使用Hibernatecontextpropertiessupplier组件,该组件基于系统属性生成HibernateContext对象。如果不需要更改PSOJSONHELPER运算符的默认HQL函数名称,则甚至使用Builder组件创建的HibernateContext,如下:
HibernateContext hibernateContext = HibernateContext . builder (). build ();“ JSONB_EXTRACT_PATH”是PostgreSQL函数,它返回JSONB值指向路径元素以“ text []”(等效于#> operator)指向的路径元素。这很有用,因为许多功能都使用“ JSONB”类型进行执行。请查看PostgreSQL文档以获取更多信息。 Hibernate 5示例:下面有一个方法的示例,该方法返回一个项目对象的列表,JSON Content属性“ TOP_ELEMENT_WITH_SET_SET_OF_VALUES”包含一个精确的值集。该示例使用JSONBALLARRARSTRINGSEXISTPREDICATE。
@ Autowired
private HibernateContext hibernateContext ;
@ Autowired
private EntityManager entityManager ;
public List < Item > findAllByAllMatchingTags ( Set < String > tags ) {
CriteriaBuilder cb = entityManager . getCriteriaBuilder ();
CriteriaQuery < Item > query = cb . createQuery ( Item . class );
Root < Item > root = query . from ( Item . class );
query . select ( root );
query . where ( new JsonbAllArrayStringsExistPredicate ( hibernateContext , ( CriteriaBuilderImpl ) cb , new JsonBExtractPath (( CriteriaBuilderImpl ) cb , singletonList ( "top_element_with_set_of_values" ), root . get ( "jsonbContent" )), tags . toArray ( new String [ 0 ])));
return entityManager . createQuery ( query ). getResultList ();
}对于上述方法,Hibernate将执行HQL查询:
select
generatedAlias0
from
Item as generatedAlias0
where
jsonb_all_array_strings_exist( jsonb_extract_path( generatedAlias0 . jsonbContent , :param0 ) , json_function_json_array(:param1)) = TRUE本机SQL将具有以下形式:
select
item0_ . id as id1_0_,
item0_ . jsonb_content as jsonb_co2_0_
from
item item0_
where
jsonb_all_array_strings_exist(jsonb_extract_path( item0_ . jsonb_content ,?), array[?]) = true有关更多详细信息,请检查测试中使用的DAO。
Hibernate 6示例:
下面有与上面相同的示例,但对于休眠6。
import org . hibernate . query . sqm . NodeBuilder ;
....
@ Autowired
private HibernateContext hibernateContext ;
@ Autowired
private EntityManager entityManager ;
public List < Item > findAllByAllMatchingTags ( Set < String > tags ) {
CriteriaBuilder cb = entityManager . getCriteriaBuilder ();
CriteriaQuery < Item > query = cb . createQuery ( Item . class );
Root < Item > root = query . from ( Item . class );
query . select ( root );
query . where ( new JsonbAllArrayStringsExistPredicate ( hibernateContext , ( NodeBuilder ) cb , new JsonBExtractPath ( root . get ( "jsonbContent" ), ( NodeBuilder ) cb , singletonList ( "top_element_with_set_of_values" )), tags . toArray ( new String [ 0 ])));
return entityManager . createQuery ( query ). getResultList ();
}有关更多详细信息,请检查测试中使用的DAO。
“ jsonb_extract_path_text”是postgresql函数,返回JSON值,如文本所指向的路径元素所指向的路径元素所传递的“ text []”(等效于#>>> >>操作员)。请查看PostgreSQL文档以获取更多信息。下面有一个方法的Hibernate 5示例,该方法查找包含与“类似”运算符匹配的特定字符串值的项目。
public List < Item > findAllByStringValueAndLikeOperator ( String expression ) {
CriteriaBuilder cb = entityManager . getCriteriaBuilder ();
CriteriaQuery < Item > query = cb . createQuery ( Item . class );
Root < Item > root = query . from ( Item . class );
query . select ( root );
query . where ( cb . like ( new JsonBExtractPathText (( CriteriaBuilderImpl ) cb , singletonList ( "string_value" ), root . get ( "jsonbContent" )), expression ));
return entityManager . createQuery ( query ). getResultList ();
}对于上述方法,Hibernate将执行HQL查询:
select
generatedAlias0
from
Item as generatedAlias0
where
jsonb_extract_path_text( generatedAlias0 . jsonbContent , :param0 ) like :param1本机SQL将具有以下形式:
select
item0_ . id as id1_0_,
item0_ . jsonb_content as jsonb_co2_0_
from
item item0_
where
jsonb_extract_path_text( item0_ . jsonb_content ,?) like ?有关In Operator的更多详细信息和示例,或者如何使用数字值,请检查测试中使用的DAO。
Hibernate 6示例:
下面有与上面相同的示例,但对于休眠6。
....
public List < Item > findAllByStringValueAndLikeOperator ( String expression ) {
CriteriaBuilder cb = entityManager . getCriteriaBuilder ();
CriteriaQuery < Item > query = cb . createQuery ( Item . class );
Root < Item > root = query . from ( Item . class );
query . select ( root );
query . where ( cb . like ( new JsonBExtractPathText ( root . get ( "jsonbContent" ), singletonList ( "string_value" ), ( NodeBuilder ) cb ), expression ));
return entityManager . createQuery ( query ). getResultList ();
}有关更多详细信息,请检查测试中使用的DAO。
JSONBALLARRARSTRINGSISTPREDICATE类型表示谓词,该谓词检查是否在JSON Array属性中存在传递的字符串数组。此谓词的第一个示例是在“ JSONBExtractPath -jsonb_extract_path”部分中引入的。这些谓词假定存在具有默认名称JSONB_ALL_ARRAY_STRINGS_EXIST的SQL函数,在“ Apply DDL更改”部分中提到。以下示例与操作员的组合不提出并非所有搜索字符串的项目。示例仅适用于Hibernate 5!
public List < Item > findAllThatDoNotMatchByAllMatchingTags ( Set < String > tags ) {
CriteriaBuilder cb = entityManager . getCriteriaBuilder ();
CriteriaQuery < Item > query = cb . createQuery ( Item . class );
Root < Item > root = query . from ( Item . class );
query . select ( root );
Predicate notAllMatchingTags = cb . not ( new JsonbAllArrayStringsExistPredicate ( hibernateContext , ( CriteriaBuilderImpl ) cb , new JsonBExtractPath (( CriteriaBuilderImpl ) cb , singletonList ( "top_element_with_set_of_values" ), root . get ( "jsonbContent" )), tags . toArray ( new String [ 0 ])));
Predicate withoutSetOfValuesProperty = cb . isNull ( new JsonBExtractPath (( CriteriaBuilderImpl ) cb , singletonList ( "top_element_with_set_of_values" ), root . get ( "jsonbContent" )));
query . where ( cb . or ( withoutSetOfValuesProperty , notAllMatchingTags ));
return entityManager . createQuery ( query ). getResultList ();
}对于上述方法,Hibernate将执行HQL查询:
select
generatedAlias0
from
Item as generatedAlias0
where
(
jsonb_extract_path( generatedAlias0 . jsonbContent , :param0 ) is null
)
or (
jsonb_all_array_strings_exist( jsonb_extract_path( generatedAlias0 . jsonbContent , :param1 ) , json_function_json_array(:param2, :param3)) = FALSE
)本机SQL将具有以下形式:
select
item0_ . id as id1_0_,
item0_ . jsonb_content as jsonb_co2_0_
from
item item0_
where
jsonb_extract_path( item0_ . jsonb_content ,?) is null
or jsonb_all_array_strings_exist(jsonb_extract_path( item0_ . jsonb_content ,?), array[?,?]) = falseHibernate 6示例:
下面有与上面相同的示例,但对于休眠6。
public List < Item > findAllThatDoNotMatchByAllMatchingTags ( Set < String > tags ) {
CriteriaBuilder cb = entityManager . getCriteriaBuilder ();
CriteriaQuery < Item > query = cb . createQuery ( Item . class );
Root < Item > root = query . from ( Item . class );
query . select ( root );
Predicate notAllMatchingTags = cb . not ( new JsonbAllArrayStringsExistPredicate ( hibernateContext , ( NodeBuilder ) cb , new JsonBExtractPath ( root . get ( "jsonbContent" ), ( NodeBuilder ) cb , singletonList ( "top_element_with_set_of_values" )), tags . toArray ( new String [ 0 ])));
Predicate withoutSetOfValuesProperty = cb . isNull ( new JsonBExtractPath ( root . get ( "jsonbContent" ), ( NodeBuilder ) cb , singletonList ( "top_element_with_set_of_values" )));
query . where ( cb . or ( withoutSetOfValuesProperty , notAllMatchingTags ));
return entityManager . createQuery ( query ). getResultList ();
}有关更多详细信息,请检查测试中使用的DAO。
JSONBANYARRAYSTRINGSTRINGSISTPREDICATE类型表示一个谓词,该谓词检查是否在JSON Array属性中存在传递的字符串数组。这些谓词假定存在具有默认名称JSONB_ARY_ARRAY_STRINGS_EXIST的SQL函数,在“ Apply DDL更改”部分中提到。下面有一个方法的示例,该方法查找所有属性数组的项目包含至少一个从传递的数组作为方法参数传递的字符串。示例仅适用于Hibernate 5!
public List < Item > findAllByAnyMatchingTags ( HashSet < String > tags ) {
CriteriaBuilder cb = entityManager . getCriteriaBuilder ();
CriteriaQuery < Item > query = cb . createQuery ( Item . class );
Root < Item > root = query . from ( Item . class );
query . select ( root );
query . where ( new JsonbAnyArrayStringsExistPredicate ( hibernateContext , ( CriteriaBuilderImpl ) cb , new JsonBExtractPath (( CriteriaBuilderImpl ) cb , singletonList ( "top_element_with_set_of_values" ), root . get ( "jsonbContent" )), tags . toArray ( new String [ 0 ])));
return entityManager . createQuery ( query ). getResultList ();
}对于上述方法,Hibernate将执行HQL查询:
select
generatedAlias0
from
Item as generatedAlias0
where
jsonb_any_array_strings_exist( jsonb_extract_path( generatedAlias0 . jsonbContent , :param0 ) , json_function_json_array(:param1, :param2)) = TRUE本机SQL将具有以下形式:
select
item0_ . id as id1_0_,
item0_ . jsonb_content as jsonb_co2_0_
from
item item0_
where
jsonb_any_array_strings_exist(jsonb_extract_path( item0_ . jsonb_content ,?), array[?,?]) = trueHibernate 6示例:
下面有与上面相同的示例,但对于休眠6。
public List < Item > findAllByAnyMatchingTags ( HashSet < String > tags ) {
CriteriaBuilder cb = entityManager . getCriteriaBuilder ();
CriteriaQuery < Item > query = cb . createQuery ( Item . class );
Root < Item > root = query . from ( Item . class );
query . select ( root );
query . where ( new JsonbAnyArrayStringsExistPredicate ( hibernateContext , ( NodeBuilder ) cb , new JsonBExtractPath ( root . get ( "jsonbContent" ), ( NodeBuilder ) cb , singletonList ( "top_element_with_set_of_values" )), tags . toArray ( new String [ 0 ])));
return entityManager . createQuery ( query ). getResultList ();
}有关In Operator的更多详细信息和示例,或者如何使用数字值,请检查测试中使用的DAO。
该库也可以用于JSON修改操作。默认情况下,在Hibernate中,我们可以通过设置其整个值来更新带有JSON内容的列。 POSJSONHELPER库还允许您通过设置,替换或删除单个JSON属性来修改JSON内容,而无需更换其完整内容。该库包含几个允许此类操作的JSON功能和操作员。
JSONB_SET功能的包装器。该函数基于JSON路径设置或替换JSON属性的值。查看以下示例,有关如何与Criteriaupdate组件一起使用:
// GIVEN
Long itemId = 19L ;
String property = "birthday" ;
String value = "1970-01-01" ;
String expectedJson = "{ " child " : { " pets " : [ " dog " ], " birthday " : " 1970-01-01 " }}" ;
// when
CriteriaUpdate < Item > criteriaUpdate = entityManager . getCriteriaBuilder (). createCriteriaUpdate ( Item . class );
Root < Item > root = criteriaUpdate . from ( Item . class );
// Set the property you want to update and the new value
criteriaUpdate . set ( "jsonbContent" , new JsonbSetFunction (( NodeBuilder ) entityManager . getCriteriaBuilder (), root . get ( "jsonbContent" ), new JsonTextArrayBuilder (). append ( "child" ). append ( property ). build (). toString (), JSONObject . quote ( value ), hibernateContext ));
// Add any conditions to restrict which entities will be updated
criteriaUpdate . where ( entityManager . getCriteriaBuilder (). equal ( root . get ( "id" ), itemId ));
// Execute the update
entityManager . createQuery ( criteriaUpdate ). executeUpdate ();
// then
Item item = tested . findById ( itemId );
assertThat (( String ) JsonPath . read ( item . getJsonbContent (), "$.child." + property )). isEqualTo ( value );
JSONObject jsonObject = new JSONObject ( expectedJson );
DocumentContext document = JsonPath . parse (( Object ) JsonPath . read ( item . getJsonbContent (), "$" ));
assertThat ( document . jsonString ()). isEqualTo ( jsonObject . toString ());这将生成以下SQL Update语句:
update
item
set
jsonb_content = jsonb_set(jsonb_content, ?:: text [], ?::jsonb)
where
id = ?
Hibernate:
select
i1_0 . id ,
i1_0 . jsonb_content
from
item i1_0
where
i1_0 . id = ?该函数也可以在HQL语句中使用,如以下示例:
@ Transactional
public void updateJsonBySettingPropertyForItemByHQL ( Long itemId , String property , String value ) {
// Execute the update
String hqlUpdate = "UPDATE Item SET jsonbContent = jsonb_set(jsonbContent, %s(:path, 'text[]'), %s(:json, 'jsonb' ) ) WHERE id = :id" . formatted ( hibernateContext . getCastFunctionOperator (), hibernateContext . getCastFunctionOperator ());
int updatedEntities = entityManager . createQuery ( hqlUpdate )
. setParameter ( "id" , itemId )
. setParameter ( "path" , new JsonTextArrayBuilder (). append ( "child" ). append ( property ). build (). toString ())
. setParameter ( "json" , JSONObject . quote ( value ))
. executeUpdate ();
}串联操作员的包装器。包装器将两个JSONB值串联成一个新的JSONB值。查看以下示例,有关如何与Criteriaupdate组件一起使用:
// GIVEN
Long itemId = 19l ;
String property = "birthday" ;
String value = "1970-01-01" ;
// WHEN
CriteriaUpdate < Item > criteriaUpdate = entityManager . getCriteriaBuilder (). createCriteriaUpdate ( Item . class );
Root < Item > root = criteriaUpdate . from ( Item . class );
JSONObject jsonObject = new JSONObject ();
jsonObject . put ( "child" , new JSONObject ());
jsonObject . getJSONObject ( "child" ). put ( property , value );
criteriaUpdate . set ( "jsonbContent" , new ConcatenateJsonbOperator (( NodeBuilder ) entityManager . getCriteriaBuilder (), root . get ( "jsonbContent" ), jsonObject . toString (), hibernateContext ));
criteriaUpdate . where ( entityManager . getCriteriaBuilder (). equal ( root . get ( "id" ), itemId ));
entityManager . createQuery ( criteriaUpdate ). executeUpdate ();
// THEN
Item item = tested . findById ( itemId );
assertThat (( String ) JsonPath . read ( item . getJsonbContent (), "$.child." + property )). isEqualTo ( value );
JSONObject expectedJsonObject = new JSONObject (). put ( property , value );
DocumentContext document = JsonPath . parse (( Object ) JsonPath . read ( item . getJsonbContent (), "$.child" ));
assertThat ( document . jsonString ()). isEqualTo ( expectedJsonObject . toString ());这将生成以下SQL Update语句:
update
item
set
jsonb_content = jsonb_content || ?::jsonb
where
id = ?
Hibernate:
select
i1_0 . id ,
i1_0 . jsonb_content
from
item i1_0
where
i1_0 . id = ?该函数也可以在HQL语句中使用,如以下示例:
@ Transactional
public void updateJsonPropertyForItemByHQL ( Long itemId , String property , String value ) throws JSONException {
JSONObject jsonObject = new JSONObject ();
jsonObject . put ( "child" , new JSONObject ());
jsonObject . getJSONObject ( "child" ). put ( property , value );
String hqlUpdate = "UPDATE Item SET jsonbContent = %s(jsonbContent, %s(:json, 'jsonb' ) ) WHERE id = :id" . formatted ( hibernateContext . getConcatenateJsonbOperator (), hibernateContext . getCastFunctionOperator ());
int updatedEntities = entityManager . createQuery ( hqlUpdate )
. setParameter ( "id" , itemId )
. setParameter ( "json" , jsonObject . toString ())
. executeUpdate ();
}删除操作员的包装器'# - '。包装器基于指定路径的索引删除字段或数组元素,其中路径元素可以是字段键或数组索引。查看以下示例,有关如何与Criteriaupdate组件一起使用:
// GIVEN
Item item = tested . findById ( 19L );
JSONObject jsonObject = new JSONObject ( "{ " child " : { " pets " : [ " dog " ]}}" );
DocumentContext document = JsonPath . parse (( Object ) JsonPath . read ( item . getJsonbContent (), "$" ));
assertThat ( document . jsonString ()). isEqualTo ( jsonObject . toString ());
// WHEN
CriteriaUpdate < Item > criteriaUpdate = entityManager . getCriteriaBuilder (). createCriteriaUpdate ( Item . class );
Root < Item > root = criteriaUpdate . from ( Item . class );
// Set the property you want to update and the new value
criteriaUpdate . set ( "jsonbContent" , new DeleteJsonbBySpecifiedPathOperator (( NodeBuilder ) entityManager . getCriteriaBuilder (), root . get ( "jsonbContent" ), new JsonTextArrayBuilder (). append ( "child" ). append ( "pets" ). build (). toString (), hibernateContext ));
// Add any conditions to restrict which entities will be updated
criteriaUpdate . where ( entityManager . getCriteriaBuilder (). equal ( root . get ( "id" ), 19L ));
// Execute the update
entityManager . createQuery ( criteriaUpdate ). executeUpdate ();
// THEN
entityManager . refresh ( item );
jsonObject = new JSONObject ( "{ " child " : {}}" );
document = JsonPath . parse (( Object ) JsonPath . read ( item . getJsonbContent (), "$" ));
assertThat ( document . jsonString ()). isEqualTo ( jsonObject . toString ());这将生成以下SQL Update语句:
update
item
set
jsonb_content = (jsonb_content # - ?::text[])
where
id = ?该函数也可以在HQL语句中使用,如以下示例:
@ Transactional
public void updateJsonByDeletingSpecificPropertyForItemByHql ( Long itemId , String property ) {
// Execute the update
String hqlUpdate = "UPDATE Item SET jsonbContent = %s(jsonbContent, %s(:path, 'text[]') ) WHERE id = :id" . formatted ( hibernateContext . getDeleteJsonBySpecificPathOperator (), hibernateContext . getCastFunctionOperator ());
int updatedEntities = entityManager . createQuery ( hqlUpdate )
. setParameter ( "id" , itemId )
. setParameter ( "path" , new JsonTextArrayBuilder (). append ( "child" ). append ( property ). build (). toString ())
. executeUpdate ();
}RemovejsonValuesFromjSonArrayFunction类型是一个冬眠操作员,可调用PosjsonHelper库生成的SQL函数。默认情况下,生成的函数看起来像以下示例:
CREATE OR REPLACE FUNCTION {{schema}}.remove_values_from_json_array(input_json jsonb, values_to_remove jsonb) RETURNS jsonb AS $$
DECLARE
result jsonb;
BEGIN
IF jsonb_typeof(values_to_remove) <> ' array ' THEN
RAISE EXCEPTION ' values_to_remove must be a JSON array ' ;
END IF;
result : = (
SELECT jsonb_agg(element)
FROM jsonb_array_elements(input_json) AS element
WHERE NOT (element IN ( SELECT jsonb_array_elements(values_to_remove)))
);
RETURN COALESCE(result, ' [] ' ::jsonb);
END;
$$ LANGUAGE plpgsql;该函数具有两个输入参数。首先是JSON数组,它是该功能将返回的结果的基本数组。第二个参数也是一个JSON数组,它表示应从结果数组中删除的元素。以下是如何与其他操作员一起使用SQL Update语句更新JSON列的代码示例。
// GIVEN
Item item = tested . findById ( 24L );
DocumentContext document = JsonPath . parse (( Object ) JsonPath . read ( item . getJsonbContent (), "$" ));
assertThat ( document . jsonString ()). isEqualTo ( "{ " child " :{ " pets " :[ " crab " , " chameleon " ]}, " inventory " :[ " mask " , " fins " , " compass " ]}" );
CriteriaUpdate < Item > criteriaUpdate = entityManager . getCriteriaBuilder (). createCriteriaUpdate ( Item . class );
Root < Item > root = criteriaUpdate . from ( Item . class );
NodeBuilder nodeBuilder = ( NodeBuilder ) entityManager . getCriteriaBuilder ();
JSONArray toRemoveJSONArray = new JSONArray ( Arrays . asList ( "mask" , "compass" ));
RemoveJsonValuesFromJsonArrayFunction deleteOperator = new RemoveJsonValuesFromJsonArrayFunction ( nodeBuilder , new JsonBExtractPath ( root . get ( "jsonbContent" ), nodeBuilder , Arrays . asList ( "inventory" )), toRemoveJSONArray . toString (), hibernateContext );
JsonbSetFunction jsonbSetFunction = new JsonbSetFunction ( nodeBuilder , ( SqmTypedNode ) root . get ( "jsonbContent" ), new JsonTextArrayBuilder (). append ( "inventory" ). build (). toString (), deleteOperator , hibernateContext );
// Set the property you want to update and the new value
criteriaUpdate . set ( "jsonbContent" , jsonbSetFunction );
// Add any conditions to restrict which entities will be updated
criteriaUpdate . where ( entityManager . getCriteriaBuilder (). equal ( root . get ( "id" ), 24L ));
// WHEN
entityManager . createQuery ( criteriaUpdate ). executeUpdate ();
// THEN
entityManager . refresh ( item );
document = JsonPath . parse (( Object ) JsonPath . read ( item . getJsonbContent (), "$" ));
assertThat ( document . jsonString ()). isEqualTo ( "{ " child " :{ " pets " :[ " crab " , " chameleon " ]}, " inventory " :[ " fins " ]}" );同一示例,但与HQL查询示例:
// GIVEN
Item item = tested . findById ( 24L );
DocumentContext document = JsonPath . parse (( Object ) JsonPath . read ( item . getJsonbContent (), "$" ));
assertThat ( document . jsonString ()). isEqualTo ( "{ " child " :{ " pets " :[ " crab " , " chameleon " ]}, " inventory " :[ " mask " , " fins " , " compass " ]}" );
CriteriaUpdate < Item > criteriaUpdate = entityManager . getCriteriaBuilder (). createCriteriaUpdate ( Item . class );
Root < Item > root = criteriaUpdate . from ( Item . class );
JSONArray toRemoveJSONArray = new JSONArray ( Arrays . asList ( "mask" , "compass" ));
String hqlUpdate = "UPDATE Item SET jsonbContent = %s(jsonbContent, %s(:path, 'text[]'), %s(jsonb_extract_path( jsonbContent , 'inventory' ), %s(:to_remove, 'jsonb')) ) WHERE id = :id" . formatted ( JSONB_SET_FUNCTION_NAME , hibernateContext . getCastFunctionOperator (), hibernateContext . getRemoveJsonValuesFromJsonArrayFunction (), hibernateContext . getCastFunctionOperator ());
// WHEN
entityManager . createQuery ( hqlUpdate )
. setParameter ( "id" , 24L )
. setParameter ( "path" , new JsonTextArrayBuilder (). append ( "inventory" ). build (). toString ())
. setParameter ( "to_remove" , toRemoveJSONArray . toString ())
. executeUpdate ();
// THEN
entityManager . refresh ( item );
document = JsonPath . parse (( Object ) JsonPath . read ( item . getJsonbContent (), "$" ));
assertThat ( document . jsonString ()). isEqualTo ( "{ " child " :{ " pets " :[ " crab " , " chameleon " ]}, " inventory " :[ " fins " ]}" );这两个示例将在SQL语句以下生成:
update
item
set
jsonb_content = jsonb_set(jsonb_content, ?:: text [], remove_values_from_json_array(jsonb_extract_path(jsonb_content, ?), ?::jsonb))
where
id = ?使用单个JSONB_SET函数使用单个Update语句为JSON设置单个属性可能很有用,但是,能够使用单个更新语句设置在JSON树的不同级别上设置多个属性可能更有用。
避免在下面的检查代码示例:
// GIVEN
Item item = tested . findById ( 23L );
DocumentContext document = JsonPath . parse (( Object ) JsonPath . read ( item . getJsonbContent (), "$" ));
assertThat ( document . jsonString ()). isEqualTo ( "{ " child " :{ " pets " :[ " dog " ]}, " inventory " :[ " mask " , " fins " ], " nicknames " :{ " school " : " bambo " , " childhood " : " bob " }}" );
CriteriaUpdate < Item > criteriaUpdate = entityManager . getCriteriaBuilder (). createCriteriaUpdate ( Item . class );
Root < Item > root = criteriaUpdate . from ( Item . class );
Hibernate6JsonUpdateStatementBuilder hibernate6JsonUpdateStatementBuilder = new Hibernate6JsonUpdateStatementBuilder ( root . get ( "jsonbContent" ), ( NodeBuilder ) entityManager . getCriteriaBuilder (), hibernateContext );
hibernate6JsonUpdateStatementBuilder . appendJsonbSet ( new JsonTextArrayBuilder (). append ( "child" ). append ( "birthday" ). build (), quote ( "2021-11-23" ));
hibernate6JsonUpdateStatementBuilder . appendJsonbSet ( new JsonTextArrayBuilder (). append ( "child" ). append ( "pets" ). build (), "[ " cat " ]" );
hibernate6JsonUpdateStatementBuilder . appendDeleteBySpecificPath ( new JsonTextArrayBuilder (). append ( "inventory" ). append ( "0" ). build ());
hibernate6JsonUpdateStatementBuilder . appendJsonbSet ( new JsonTextArrayBuilder (). append ( "parents" ). append ( 0 ). build (), "{ " type " : " mom " , " name " : " simone " }" );
hibernate6JsonUpdateStatementBuilder . appendJsonbSet ( new JsonTextArrayBuilder (). append ( "parents" ). build (), "[]" );
hibernate6JsonUpdateStatementBuilder . appendDeleteBySpecificPath ( new JsonTextArrayBuilder (). append ( "nicknames" ). append ( "childhood" ). build ());
// Set the property you want to update and the new value
criteriaUpdate . set ( "jsonbContent" , hibernate6JsonUpdateStatementBuilder . build ());
// Add any conditions to restrict which entities will be updated
criteriaUpdate . where ( entityManager . getCriteriaBuilder (). equal ( root . get ( "id" ), 23L ));
// WHEN
entityManager . createQuery ( criteriaUpdate ). executeUpdate ();
// THEN
entityManager . refresh ( item );
document = JsonPath . parse (( Object ) JsonPath . read ( item . getJsonbContent (), "$" ));
assertThat ( document . jsonString ()). isEqualTo ( "{ " child " :{ " pets " :[ " cat " ], " birthday " : " 2021-11-23 " }, " parents " :[{ " name " : " simone " , " type " : " mom " }], " inventory " :[ " fins " ], " nicknames " :{ " school " : " bambo " }}" );在上面的代码中,我们希望设置三个JSON属性“ child.birthday”,“ child.pets”和“ parters”,并删除另外两个“库存”,“ 0.0”和“ nicknames.childhood”。 “父母”财产是假定是阵列的新财产。尽管设置具有某些值的新数组属性可以通过单个操作来完成,但是出于演示目的,我们使用了两个操作。一种是为了设置名为“父母”的新属性,将空的JSON数组作为值。以及将数组元素设置为特定索引的另一个操作。如果不存在较高的属性,则必须在内部属性之前创建它。幸运的是,Hibernate6jsonupDateTeTatementBuilder类型的默认实例具有适当的排序和过滤组件,可帮助您设置正确的操作顺序。因此,在添加创建阵列操作之前还是之后,我们是否添加附加仪表操作都没关系。默认情况下,将在添加或替换内容的内容之前添加删除内容的操作。当然,可以通过将这些组件设置为null来禁用此行为。有关更多详细信息,请检查Javadoc中的Hibernate6jsonupDateTestatementBuilder类型。
此代码在SQL语句下面生成:
update
item
set
jsonb_content =
jsonb_set(
jsonb_set(
jsonb_set(
jsonb_set(
(
(jsonb_content # - ?::text[]) -- the most nested #- operator
# - ?::text[])
, ?:: text [], ?::jsonb) -- the most nested jsonb_set operation
, ?:: text [], ?::jsonb)
, ?:: text [], ?::jsonb)
, ?:: text [], ?::jsonb)
where
id = ?此准备的语句的最内部JSONB_SET函数执行将为“父母”属性设置一个空数组。
构建器具有设置值的方法:
删除属性:
appenddeletebyspecificpath(jsontextarray jsontextarray)
添加数组元素
删除数组元素
默认情况下,与JSON数组上的修改有关的一些方法需要org.json.json库(检查可选依赖项)。 However, it is possible to pass custom implementation of interface that maps collection object to json array value with "withCollectionToJsonArrayStringMapper(com.github.starnowski.posjsonhelper.hibernate6.Hibernate6JsonUpdateStatementBuilder.CollectionToJsonArrayStringMapper)" builder method
Hibernate6jsonupDateTestatementBuilder类型是通用的。第二个通用类型是一种自定义值,可以添加到Hibernate6jsonupDateTeTatementBuilder上下文中。以下是一个代码示例,其类型代表数组操作(应添加到数组中并删除的元素)。
自定义值类型:
private static class JsonArrayOperations {
private final List < String > toDelete ;
private final List < String > toAdd ;
public JsonArrayOperations ( List < String > toDelete , List < String > toAdd ) {
this . toDelete = toDelete ;
this . toAdd = toAdd ;
}
public List < String > getToDelete () {
return toDelete ;
}
public List < String > getToAdd () {
return toAdd ;
}
}用法示例:
// GIVEN
Item item = tested . findById ( 24L );
DocumentContext document = JsonPath . parse (( Object ) JsonPath . read ( item . getJsonbContent (), "$" ));
assertThat ( document . jsonString ()). isEqualTo ( "{ " child " :{ " pets " :[ " crab " , " chameleon " ]}, " inventory " :[ " mask " , " fins " , " compass " ]}" );
CriteriaUpdate < Item > criteriaUpdate = entityManager . getCriteriaBuilder (). createCriteriaUpdate ( Item . class );
Root < Item > root = criteriaUpdate . from ( Item . class );
Hibernate6JsonUpdateStatementBuilder < Object , JsonArrayOperations > hibernate6JsonUpdateStatementBuilder = new Hibernate6JsonUpdateStatementBuilder ( root . get ( "jsonbContent" ), ( NodeBuilder ) entityManager . getCriteriaBuilder (), hibernateContext );
hibernate6JsonUpdateStatementBuilder . appendJsonbSet ( new JsonTextArrayBuilder (). append ( "child" ). append ( "pets" ). build (), null , new JsonArrayOperations ( Arrays . asList ( "crab" , "ant" ), Arrays . asList ( "lion" , "dolphin" )));
hibernate6JsonUpdateStatementBuilder . appendJsonbSet ( new JsonTextArrayBuilder (). append ( "name" ). build (), JSONObject . quote ( "Simon" ));
hibernate6JsonUpdateStatementBuilder . appendJsonbSet ( new JsonTextArrayBuilder (). append ( "inventory" ). build (), null , new JsonArrayOperations ( Arrays . asList ( "compass" , "mask" ), Arrays . asList ( "knife" )));
hibernate6JsonUpdateStatementBuilder . withJsonbSetFunctionFactory ( new Hibernate6JsonUpdateStatementBuilder . DefaultJsonbSetFunctionFactory < Object , JsonArrayOperations >() {
public JsonbSetFunction build ( NodeBuilder nodeBuilder , Path < Object > rootPath , JsonUpdateStatementConfiguration . JsonUpdateStatementOperation < JsonArrayOperations > operation , HibernateContext hibernateContext ) {
if ( operation . getCustomValue () != null ) {
JSONArray toAddJSONArray = new JSONArray ( operation . getCustomValue (). getToAdd ());
ConcatenateJsonbOperator concatenateOperator = new ConcatenateJsonbOperator ( nodeBuilder , new JsonBExtractPath ( rootPath , nodeBuilder , operation . getJsonTextArray (). getPath (). stream (). map ( ob -> ob . toString ()). collect ( Collectors . toList ())), toAddJSONArray . toString (), hibernateContext );
JSONArray toRemoveJSONArray = new JSONArray ( operation . getCustomValue (). getToDelete ());
RemoveJsonValuesFromJsonArrayFunction deleteOperator = new RemoveJsonValuesFromJsonArrayFunction ( nodeBuilder , concatenateOperator , toRemoveJSONArray . toString (), hibernateContext );
return new JsonbSetFunction ( nodeBuilder , ( SqmTypedNode ) rootPath , operation . getJsonTextArray (). toString (), deleteOperator , hibernateContext );
} else {
return super . build ( nodeBuilder , rootPath , operation , hibernateContext );
}
}
@ Override
public JsonbSetFunction build ( NodeBuilder nodeBuilder , SqmTypedNode sqmTypedNode , JsonUpdateStatementConfiguration . JsonUpdateStatementOperation < JsonArrayOperations > operation , HibernateContext hibernateContext ) {
if ( operation . getCustomValue () != null ) {
JSONArray toAddJSONArray = new JSONArray ( operation . getCustomValue (). getToAdd ());
ConcatenateJsonbOperator concatenateOperator = new ConcatenateJsonbOperator ( nodeBuilder , new JsonBExtractPath ( root . get ( "jsonbContent" ), nodeBuilder , operation . getJsonTextArray (). getPath (). stream (). map ( ob -> ob . toString ()). collect ( Collectors . toList ())), toAddJSONArray . toString (), hibernateContext );
JSONArray toRemoveJSONArray = new JSONArray ( operation . getCustomValue (). getToDelete ());
RemoveJsonValuesFromJsonArrayFunction deleteOperator = new RemoveJsonValuesFromJsonArrayFunction ( nodeBuilder , concatenateOperator , toRemoveJSONArray . toString (), hibernateContext );
return new JsonbSetFunction ( nodeBuilder , sqmTypedNode , operation . getJsonTextArray (). toString (), deleteOperator , hibernateContext );
} else {
return super . build ( nodeBuilder , sqmTypedNode , operation , hibernateContext );
}
}
});
// Set the property you want to update and the new value
criteriaUpdate . set ( "jsonbContent" , hibernate6JsonUpdateStatementBuilder . build ());
// Add any conditions to restrict which entities will be updated
criteriaUpdate . where ( entityManager . getCriteriaBuilder (). equal ( root . get ( "id" ), 24L ));
// WHEN
entityManager . createQuery ( criteriaUpdate ). executeUpdate ();
// THEN
entityManager . refresh ( item );
document = JsonPath . parse (( Object ) JsonPath . read ( item . getJsonbContent (), "$" ));
assertThat ( document . jsonString ()). isEqualTo ( "{ " name " : " Simon " , " child " :{ " pets " :[ " chameleon " , " lion " , " dolphin " ]}, " inventory " :[ " fins " , " knife " ]}" );| 属性名称 | 描述 |
|---|---|
| com.github.starnowski.posjsonhelper.core.functions.jsonb_all_array_array_strings_exist | SQL函数的名称,该函数检查是否全部传递的元素,因为文本[]存在于JSON数组属性中。默认情况下,名称为jsonb_all_array_strings_exist |
| com.github.starnowski.posjsonhelper.core.functions.jsonb_any_array_array_strings_exist | SQL函数的名称,该函数检查是否传入的元素,因为文本[]存在于JSON数组属性中。默认情况下,名称为jsonb_any_array_strings_exist |
| com.github.starnowski.posjsonhelper.core.functions.remove_values_from_json_array | 通过删除JSONB数组中的元素作为函数输入,返回JSONB数组的SQL函数的名称。默认情况下,名称为remove_values_from_json_array |
| com.github.starnowski.posjsonhelper.core.schema | 应该创建SQL功能的数据库架构的名称 |
| com.github.starnowski.posjsonhelper.core.hibernate.functions.jsonb_all_arl_array_strings_exist | 调用由com.github.starnowski.posjsonhelper.core.core.functions.jsonb_all_arl_array_array_strings_exist属性指定的SQL函数的HQL函数的名称。默认情况下,名称为jsonb_all_array_strings_exist |
| com.github.starnowski.posjsonhelper.core.hibernate.functions.jsonb_any_array_array_strings_exist | 调用由com.github.starnowski.posjsonhelper.core.core.functions.jsonb_any_ary_array_array_ertings_exist属性指定的SQL函数的HQL函数的名称。默认情况下,名称为jsonb_any_array_strings_exist |
| com.github.starnowski.posjsonhelper.core.hibernate.functions.json_function_json_array | 将数组运算符在PostgreSQL中包装的HQL函数的名称。默认情况下,名称为json_function_json_array |
| com.github.starnowski.posjsonhelper.core.hibernate.functions.remove_values_from_json_array | 通过删除JSONB数组中的元素作为函数输入,将返回JSONB数组返回JSONB数组的HQL函数的名称。默认情况下,名称为remove_values_from_json_array |
| com.github.starnowski.posjsonhelper.core.hibernate.functions.sqldefinitioncontextfactory.types | 存储com.github.starnowski.posjsonhelper.core.core.sql.isqldefinitioncontextfactfactory类型的系统属性。而不是加载可以在“ com.github.starnowski.posjsonhelper”的class路径上找到的类型。列表上的类型由逗号字符分开。”。 |
| com.github.starnowski.posjsonhelper.hibernate6.functionDescriptorregisterfactory.types | (仅在Hibernate 6中使用)存储com.github.starnowski.posjsonhelper.hibernate6.descriptor.functiondescriptDescriptDescriptDescriptDescriptOrregisterFactoriessupplier类型的系统属性。而不是加载可以在“ com.github.starnowski.posjsonhelper”的class路径上找到的类型。列表上的类型由逗号字符分开。”。 |
| com.github.starnowski.posjsonhelper.hibernate6.functionDescriptorregisterfactory.types.excluded | (仅在Hibernate 6中使用)存储com.github.starnowski.posjsonhelper.hibernate6.descriptor.functiondescriptDescriptDescriptDescriptDescriptOrregisterFactoriessupplier类型的系统属性。如果“ com.github.starnowski.posjsonhelper.hibernate6.functionDescriptDescriptOrregisterFactory.types”属性也指定,然后“ com.github.starnowski.posjsonhelper.hibernate6.functionDescriptDescriptDescriptDescriptDescriptDescriptDescriptDescriptDescriptDescriptorregisterfactory.expespes.exclitity.exclitity”列表上的类型由逗号字符分开。”。 |
兼容性矩阵与休眠6。
| posjsonhelper | 休眠6 |
|---|---|
| 0.3.0 | 6.4.0.最终 |
| 0.2.0-0.2.1 | 6.1.5.最终 |
休眠软件包,版本或类等方面的问题。
如果您面临与缺少方法定义或类型相关的问题,例如:
java.lang.NoSuchMethodError: 'org.hibernate.query.criteria.JpaExpression
然后,请首先检查您的项目是否在ClassPath上有Hibernate-Core库。 Hibernate-Core是该项目的可选依赖性,您必须确保其已在项目中添加。请检查类似的第145期。如果您在ClassPath上冬眠核心,并且问题仍然存在,请报告问题。