使用 Spring Data JPA 在 BLOB 上构建 JPQL

Build a JPQL on a BLOB with Spring Data JPA

我正在使用一个外部服务,该服务发送到我的应用程序中的一个端点,我在其中有一个 @RequestBody CalloutRequest calloutRequest,我将请求正文映射到 Class CalloutRequest.

作为 CalloutRequest 的一部分,有一个名为 userAttributes 的字段,在我的 class 中看起来像这样:

@Lob    
private HashMap<String, List<String>> userAttributes;

我用 @Lob 对它进行了注释,因为我没有 class 可以映射到它,因为它的性质相当复杂。因此这个HashMap。 JSON 格式时 userAttributes 的示例:

  "userAttributes": {
    "lastName": [
      "Guy"
    ],
    "ExternalId": [
      "d9a59407-2bca-46aa-8641-3350fd95bcd1"
    ],
    "distinguishedName": [
      "CN=t2-contractor9,DC=company,DC=com"
    ],
    "employeeID": [],
    "userName": [
      "t2-contractor9@company.com"
    ],
    "groupNames": [
      "ALL USERS",
      "T2-Contractor@company.com"
    ],
    "firstName": [
      "T2-Contractor"
    ],
    "UserStore": [],
    "phone": [],
    "domain": [
      "company.com"
    ],
    "disabled": [
      "false"
    ],
    "email": [
      "t2-contractor9@company.com"
    ]
  }

我的挑战是我想为特定用户名构建查询 - userAttributes.userName - 但考虑到这是作为 BLOB 存储的事实,我无法弄清楚如何 - 或者如果可能的话 -写这样一个查询。
现在我只是在使用 Spring Data JPA 及其存储库实现,但我想知道这是否是我可以通过自定义 @Query 或使用 querydsl 实现的?

我的理解是否正确,如果我能想出一种方法将上面的 HashMap 映射到实际的 class 我可以使用 serialise/de-serialise 并在 CalloutRequest 和这个新的之间建立映射class 我可以构建一个 JPQL 查询,使用连接来实现我想要的吗?

在这里利用 AttributeConverter 实现怎么样:

public class YourEntity {
  // ...
  @ElementCollection
  @Convert(name = "value", converter = StringListToStringConverter.class)
  private Map<String, List<String>> userAttributes;
}

StringListToStringConverter 的简单实现可能是:

public class StringListToStringConverter 
       implements AttributeConverter<List<String>, String> {
    private static final String DELIMITER = "|";

    @Override
    public String converToDatabaseColumn(List<String> attributes) {
      if ( attributes == null || attributes.isEmpty() ) {
        return null;
      }

      StringBuilder sb = new StringBuilder();
      for ( Iterator<String> i = attribtues.iterator(); i.hasNext(); ) {
        sb.append( i.next() );
        if ( i.hasNext() ) {
          sb.append( DELIMITER );
        }
      }
      return sb.toString();
    }

    @Override 
    public List<String> convertToEntityAttribute(String data) {
      if ( data == null ) {
        return new ArrayList<String>();
      }

      List<String> values = Arrays.asList( StringHelper.split( DELIMITER, data ) );         
      return new ArrayList<String>( values );
    }
}

您显然必须处理输入流包含您的 DELIMITER 排序值的情况,但它可能会起作用,具体取决于您的用例。

我相信您可以使用 HQL 编写查询,例如:

SELECT e FROM YourEntity e JOIN e.userAttributes a 
WHERE VALUE(a) = :mapValue

如果您使用 Hibernate 作为 JPA 实现并使用 PostgreSQL 作为数据库,请查看 hibernate-native-json 项目:

例子

您可以序列化 class 或 Map(在任何情况下都需要一个更动态的字段)。

@Entity
public class MyClass {

  @Type(type = "com.marvinformatics.hibernate.json.JsonListUserType")
  @Target(Label.class)
  private List<Label> labels = new ArrayList<Label>();

  @Type(type = "com.marvinformatics.hibernate.json.JsonUserType")
  private Map<String, String> extra;

}

这允许像这样的 HQL 查询:

select
    json_text(i.label, 'value')
from
    Item i
where
    json_text(i.label, 'lang') = :lang

祝你好运!