java 有没有一种方法可以使用Hibernate/JPQL查询PostgreSQL hstore?

bfnvny8b  于 2023-08-01  发布在  Java
关注(0)|答案(3)|浏览(186)

假设我有一个Hibernate/JPA实体如下:

  1. @Entity
  2. public class FooEntity {
  3. ...
  4. @Type(type = "hstore")
  5. HashMap<String, String> tags;
  6. }

字符串
...而hstore Type是来自this资源的简单UserType实现。
有没有一种方法可以在JPQL查询中访问hstore,类似于下面的伪代码:

  1. SELECT f FROM FooEntity f WHERE f.tags CONTAINS KEY(:key)

jslywgbw

jslywgbw1#

您也可以简单地创建一个Hibernate org. hibernate. usertype. UserType。你扩展了那个类;我们自己实现的一个例子:

  1. public class HstoreUserType implements UserType {
  2. /**
  3. * PostgreSQL {@code hstore} field separator token.
  4. */
  5. private static final String HSTORE_SEPARATOR_TOKEN = "=>";
  6. /**
  7. * {@link Pattern} used to find and split {@code hstore} entries.
  8. */
  9. private static final Pattern HSTORE_ENTRY_PATTERN = Pattern.compile(String.format("\"(.*)\"%s\"(.*)\"", HSTORE_SEPARATOR_TOKEN));
  10. /**
  11. * The PostgreSQL value for the {@code hstore} data type.
  12. */
  13. public static final int HSTORE_TYPE = 1111;
  14. @Override
  15. public int[] sqlTypes() {
  16. return new int[] { HSTORE_TYPE };
  17. }
  18. @SuppressWarnings("rawtypes")
  19. @Override
  20. public Class returnedClass() {
  21. return Map.class;
  22. }
  23. @Override
  24. public boolean equals(final Object x, final Object y) throws HibernateException {
  25. return x.equals(y);
  26. }
  27. @Override
  28. public int hashCode(final Object x) throws HibernateException {
  29. return x.hashCode();
  30. }
  31. @Override
  32. public Object nullSafeGet(final ResultSet rs, final String[] names,
  33. final SessionImplementor session, final Object owner)
  34. throws HibernateException, SQLException {
  35. return convertToEntityAttribute(rs.getString(names[0]));
  36. }
  37. @SuppressWarnings("unchecked")
  38. @Override
  39. public void nullSafeSet(final PreparedStatement st, final Object value, final int index,
  40. final SessionImplementor session) throws HibernateException, SQLException {
  41. st.setObject(index, convertToDatabaseColumn((Map<String,Object>)value), HSTORE_TYPE);
  42. }
  43. @SuppressWarnings("unchecked")
  44. @Override
  45. public Object deepCopy(final Object value) throws HibernateException {
  46. return new HashMap<String,Object>(((Map<String,Object>)value));
  47. }
  48. @Override
  49. public boolean isMutable() {
  50. return true;
  51. }
  52. @Override
  53. public Serializable disassemble(final Object value) throws HibernateException {
  54. return (Serializable) value;
  55. }
  56. @Override
  57. public Object assemble(final Serializable cached, final Object owner)
  58. throws HibernateException {
  59. return cached;
  60. }
  61. @Override
  62. public Object replace(final Object original, final Object target, final Object owner)
  63. throws HibernateException {
  64. return original;
  65. }
  66. private String convertToDatabaseColumn(final Map<String, Object> attribute) {
  67. final StringBuilder builder = new StringBuilder();
  68. for (final Map.Entry<String, Object> entry : attribute.entrySet()) {
  69. if(builder.length() > 1) {
  70. builder.append(", ");
  71. }
  72. builder.append("\"");
  73. builder.append(entry.getKey());
  74. builder.append("\"");
  75. builder.append(HSTORE_SEPARATOR_TOKEN);
  76. builder.append("\"");
  77. builder.append(entry.getValue().toString());
  78. builder.append("\"");
  79. }
  80. return builder.toString();
  81. }
  82. private Map<String, Object> convertToEntityAttribute(final String dbData) {
  83. final Map<String, Object> data = new HashMap<String, Object>();
  84. if (dbData != null) {
  85. final StringTokenizer tokenizer = new StringTokenizer(dbData, ",");
  86. while(tokenizer.hasMoreTokens()) {
  87. final Matcher matcher = HSTORE_ENTRY_PATTERN.matcher(tokenizer.nextToken().trim());
  88. if(matcher.find()) {
  89. data.put(matcher.group(1), matcher.group(2));
  90. }
  91. }
  92. }
  93. return data;
  94. }

字符串
}
现在你可以在Entity bean中使用它,如下所示:

  1. @Entity
  2. @Table(name="YourEntityBeanTable")
  3. @TypeDefs({
  4. @TypeDef(name = "hstore", typeClass = HstoreUserType.class)
  5. })
  6. public class YourEntityBean {
  7. .....
  8. @Type(type = "hstore")
  9. @Column(name= "an_hstore_column", columnDefinition = "hstore")
  10. private Map<String, String> anHStoreColumn = new HashMap<>();
  11. }

展开查看全部
laawzig2

laawzig22#

Hibernate提供了一个跨许多DB的通用查询抽象,因此很难抽象出非SQL语法。
我会使用一个本地查询来获取id,并使用它们来获取Hibernate实体,如果您确实需要的话。
如果您只对投影感兴趣,那么原生查询是您的最佳选择。

sqougxex

sqougxex3#

下面是一个如何将HStore与Hibernate一起使用的示例。
首先编写一个用户类型:

  1. /**
  2. * Custom Hibernate {@link UserType} used to convert between a {@link Map}
  3. * and PostgreSQL {@code hstore} data type.
  4. */
  5. public class HStoreType implements UserType {
  6. /**
  7. * PostgreSQL {@code hstore} field separator token.
  8. */
  9. private static final String HSTORE_SEPARATOR_TOKEN = "=>";
  10. /**
  11. * {@link Pattern} used to find and split {@code hstore} entries.
  12. */
  13. private static final Pattern HSTORE_ENTRY_PATTERN = Pattern.compile(
  14. String.format("\"(.*)\"%s\"(.*)\"", HSTORE_SEPARATOR_TOKEN)
  15. );
  16. public static final int SQL_TYPE = Types.OTHER;
  17. @Override
  18. public int[] sqlTypes() {
  19. return new int[] { SQL_TYPE };
  20. }
  21. @SuppressWarnings("rawtypes")
  22. @Override
  23. public Class returnedClass() {
  24. return Map.class;
  25. }
  26. @Override
  27. public boolean equals(final Object x, final Object y) throws HibernateException {
  28. return x.equals(y);
  29. }
  30. @Override
  31. public int hashCode(final Object x) throws HibernateException {
  32. return x.hashCode();
  33. }
  34. @Override
  35. public Object nullSafeGet(final ResultSet rs, final String[] names,
  36. final SharedSessionContractImplementor session, final Object owner)
  37. throws HibernateException, SQLException {
  38. return convertToEntityAttribute(rs.getString(names[0]));
  39. }
  40. @SuppressWarnings("unchecked")
  41. @Override
  42. public void nullSafeSet(final PreparedStatement st, final Object value, final int index,
  43. final SharedSessionContractImplementor session) throws HibernateException, SQLException {
  44. st.setObject(index, convertToDatabaseColumn((Map<String,Object>)value), SQL_TYPE);
  45. }
  46. @SuppressWarnings("unchecked")
  47. @Override
  48. public Object deepCopy(final Object value) throws HibernateException {
  49. return new HashMap<String,Object>(((Map<String,Object>)value));
  50. }
  51. @Override
  52. public boolean isMutable() {
  53. return true;
  54. }
  55. @Override
  56. public Serializable disassemble(final Object value) throws HibernateException {
  57. return (Serializable) value;
  58. }
  59. @Override
  60. public Object assemble(final Serializable cached, final Object owner)
  61. throws HibernateException {
  62. return cached;
  63. }
  64. @Override
  65. public Object replace(final Object original, final Object target, final Object owner)
  66. throws HibernateException {
  67. return original;
  68. }
  69. private String convertToDatabaseColumn(final Map<String, Object> attribute) {
  70. final StringBuilder builder = new StringBuilder();
  71. for (final Map.Entry<String, Object> entry : attribute.entrySet()) {
  72. if(builder.length() > 1) {
  73. builder.append(", ");
  74. }
  75. builder.append("\"");
  76. builder.append(entry.getKey());
  77. builder.append("\"");
  78. builder.append(HSTORE_SEPARATOR_TOKEN);
  79. builder.append("\"");
  80. builder.append(entry.getValue().toString());
  81. builder.append("\"");
  82. }
  83. return builder.toString();
  84. }
  85. private Map<String, Object> convertToEntityAttribute(final String dbData) {
  86. final Map<String, Object> data = new HashMap<String, Object>();
  87. final StringTokenizer tokenizer = new StringTokenizer(dbData, ",");
  88. while(tokenizer.hasMoreTokens()) {
  89. final Matcher matcher = HSTORE_ENTRY_PATTERN.matcher(tokenizer.nextToken().trim());
  90. if(matcher.find()) {
  91. data.put(matcher.group(1), matcher.group(2));
  92. }
  93. }
  94. return data;
  95. }
  96. }

字符串
其次编写一个SQL函数适配器:

  1. public class HStoreValueFunction implements SQLFunction {
  2. @Override
  3. public boolean hasArguments() {
  4. return true;
  5. }
  6. @Override
  7. public boolean hasParenthesesIfNoArguments() {
  8. return false;
  9. }
  10. @Override
  11. public Type getReturnType(Type type, Mapping mpng) throws QueryException {
  12. return new StringType();
  13. }
  14. @Override
  15. public String render(Type type, List args, SessionFactoryImplementor sfi) throws QueryException {
  16. if (args.size() < 2) {
  17. throw new IllegalArgumentException("2 arguments required");
  18. }
  19. String field = (String) args.get(0);
  20. String key = (String) args.get(1);
  21. return field + " -> " + key;
  22. }
  23. }


并以自定义方言进行注册:

  1. public class ExpandedPostgresDialect extends PostgreSQL95Dialect {
  2. public ExpandedPostgresDialect() {
  3. super();
  4. registerFunction("hstoreValue", new HStoreValueFunction());
  5. }
  6. }


因此,您将能够在以下实体中使用它:

  1. @Embeddable
  2. public class TranslatableField {
  3. private String value;
  4. @Type(type = "it.walczak.examples.in18jpa.concepts.b.hibernate.HStoreType")
  5. @Column(columnDefinition = "hstore")
  6. private Map<String, String> translationByLanguage = new HashMap<>();


以及在JPQL查询中,例如:

  1. select p from Product p
  2. where lower(p.details.name.value) like concat('%', lower(?1), '%')
  3. or lower(hstoreValue(p.details.name.translationsByLanguage, ?2))
  4. like concat('%', lower(?1), '%')


有关更深入的文章以及完整的代码示例,请参阅:https://walczak.it/blog/performant-internationalization-in-postgresql-hibernate-jpa-hstore-column-with-translations

展开查看全部

相关问题