2017-05-10 2 views
0

Ich habe ein Problem, diese native Abfrage richtig gegen eine Postgres 9.4-Instanz zu bekommen.Wie verwende ich Springdaten jpa, um die jsonb-Spalte abzufragen?

Mein Repository hat eine Methode:

@Query(value = "SELECT t.* " + 
      "FROM my_table t " + 
      "WHERE t.field_1 = ?1 " + 
      "AND t.field_2 = 1 " + 
      "AND t.field_3 IN ?2 " + 
      "AND t.jsonb_field #>> '{key,subkey}' = ?3", 
      nativeQuery = true) 
    List<Entity> getEntities(String field1Value, 
            Collection<Integer> field3Values, 
            String jsonbFieldValue); 

Aber die Protokolle zeigen dies:

SELECT t.* FROM my_table t 
WHERE t.field_1 = ?1 
    AND t.field_2 = 1 
    AND t.field_3 IN ?2 
    AND t.jsonb_field ? '{key,subkey}' = ?3 

Und ich bekomme diese Ausnahme:

Interne Ausnahme: org.postgresql.util .PSQLException: Kein Wert für Parameter 2 angegeben.

Ich protokollierte die Parameter direkt vor Methodenaufruf, und sie werden alle geliefert.

Ich bin mir nicht sicher, warum #>>? im Protokoll zeigt. Muss ich entkommen #>>? Muss ich die Sammlung für IN formatieren? Muss ich dem Json-Pfad entkommen?

Wenn ich die Abfrage direkt gegen die db ausführen, funktioniert es. Beispiel:

SELECT * 
FROM my_table t 
WHERE t.field_1 = 'xxxx' 
    AND t.field_2 = 1 
    AND t.field_3 IN (13) 
    AND t.jsonb_field #>> '{key,subkey}' = 'value' 

Antwort

1

Wenn der Betreiber für einen oder anderen Grund in ein Fragezeichen umgewandelt wird, dann sollten Sie versuchen, die Funktion stattdessen verwenden. Die entsprechende Funktion finden Sie unter \doS+ #>> in der Konsole psql. Es teilt uns mit, dass die Funktion jsonb_extract_path_text aufgerufen wird. Dies würde Ihre Frage stellen:

@Query(value = "SELECT t.* " + 
     "FROM my_table t " + 
     "WHERE t.field_1 = ?1 " + 
     "AND t.field_2 = 1 " + 
     "AND t.field_3 IN ?2 " + 
     "AND jsonb_extract_path_text(t.jsonb_field, '{key,subkey}') = ?3", 
     nativeQuery = true) 
+0

denke ich, ich bin immer näher. Anscheinend sind IN-Klauseln für Listenparameter bei nativen Abfragen nicht zulässig. Ist es möglich, jsonb-Operationen mit jpa/jpql ohne native Abfrage auszuführen? –

+0

Huh? Sie arbeiten mit systemeigenen Abfragen in Hibernate. Verwenden Sie EclipseLink oder eine der weniger populären Implementierungen? Wie auch immer, in Postgres wird der Ausdruck 'Feld IN (val1, val2)' in sein Äquivalent 'field = ANY ('{val1, val1}')' transformiert. Wenn Sie es als ein Array zu "Feld = ANY (?)" Mit den Klammern übergeben, funktioniert es genauso. Das Äquivalent für "NOT IN" ist "Feld <> ALL (?)". Dies zwingt Sie natürlich dazu, die Zeichenfolgendarstellung des Arrays selbst zu erstellen und sicherzustellen, dass Sie alle Strings ordnungsgemäß entkoppelt werden. – coladict

+0

Ja, wir verwenden Eclipselink. Ich bin mir nicht sicher, warum das gegenüber Hibernate vorzuziehen ist. Was würden Sie empfehlen? In jedem Fall, nachdem ich zu 'jsonb_extract_path_text' gewechselt habe, ist die Abfrage mit einer neuen Ausnahme fehlgeschlagen. Als ich nach der Ausnahme gegoogelt habe, habe ich http://stackoverflow.com/a/6279110/918608 und http://stackoverflow.com/a/41564377/918608 gefunden. Es scheint, dass ich eine konstante Anzahl von Werten in meiner Liste haben muss, und ich muss sie in meiner nativen Abfrage aufzählen. Ist das nicht der Fall? –

0

Ich schlage vor, auf diese Weise nicht nach, ich preffer generischen CRUD Weg folgen (auch auf fortschrittliche Selbstarbeits erzeugen DAO Methoden in Art und Weise des StrongLoop Loopback tut, für Spring Data Erholung Maven Plugin, aber es ist im Moment nur experimentell). Aber mit diesem JSON, was jetzt zu tun ist ... Ich bin auf der Suche nach etwas ähnlich MongoDB JSON Verarbeitung in Spring Data über @Document Annotation, aber dies ist noch nicht verfügbar.Aber es gibt andere Wege :-)

Im Allgemeinen ist es über Ihren JSON Benutzertyp (Usertype-Schnittstelle) Umsetzung:

public class YourJSONBType implements UserType { 

Schließlich müssen Sie Ihre JPA-Klassen mit Angabe Ihres implementierten Benutzertypen verbessern:

@Entity 
@Data 
@AllArgsConstructor 
@NoArgsConstructor 
@TypeDef(name = "JsonbType", typeClass = YourJSONBType.class) 
public class Person { 
    @Id 
    @GeneratedValue 
    private Long id; 

    @Column(columnDefinition = "jsonb") 
    @Type(type = "JsonbType") 
    private Map<String,Object> info; 
} 

Blick auf weitere ähnliche Artikel hier: Mapping postgreSQL JSON column to Hibernate value type

Der vollständige Realisierun Ionen-Beispiel finden Sie hier:

ähnlich, aber wenig anderes Beispiel ist hier verfügbar: http://www.wisely.top/2017/06/27/spring-data-jpa-postgresql-jsonb/?d=1

1

ich die Specification api aus Federdaten sehr hilfreich.
Nehmen wir an, wir haben eine Entität mit dem Namen Product und eine Eigenschaft mit dem Namen title vom Typ JSON (B).
Ich nehme an, dass diese Eigenschaft den Titel des Produkts in verschiedenen Sprachen enthält. Ein Beispiel könnte sein: {"EN":"Multicolor LED light", "GR":"Πολύχρωμο LED φώς"}.
Der Quellcode unten findet ein (oder mehr, falls es kein eindeutiges Feld ist) Produkt nach Titel und Gebietsschema als Argumente übergeben.

@Repository 
public interface ProductRepository extends JpaRepository<Product, Integer>, JpaSpecificationExecutor<Product> { 
} 


public class ProductSpecification implements Specification<Product> { 

    private String locale; 
    private String titleToSearch; 

    public ProductSpecification(String locale, String titleToSearch) { 
     this.locale = locale; 
     this.titleToSearch = titleToSearch; 
    } 

    @Override 
    public Predicate toPredicate(Root<Product> root, CriteriaQuery<?> query, CriteriaBuilder builder) { 
     return builder.equal(builder.function("jsonb_extract_path_text", String.class, root.<String>get("title"), builder.literal(this.locale)), this.titleToSearch); 
    } 
} 


@Service 
public class ProductService { 

    @Autowired 
    private ProductRepository productRepository; 

    public List<Product> findByTitle(String locale, String titleToSearch) { 
     ProductSpecification cs = new ProductSpecification(locale, titleToSearch); 
     return productRepository.find(cs); 
     // Or using lambda expression - without the need of ProductSpecification class. 
//  return productRepository.find((Root<ProductCategory> root, CriteriaQuery<?> query, CriteriaBuilder builder) -> { 
//   return builder.equal(builder.function("jsonb_extract_path_text", String.class, root.<String>get("title"), builder.literal(locale)), titleToSearch); 
//  }); 
    } 
} 

Hoffe, dass hilft.

0

Sie können JPQL-Schlüssel FUCT auch zum Aufrufen von benutzerdefinierten Funktionen verwenden und keine native Abfrage verwenden.
Etwas Ähnliches,

@Query(value = "SELECT t FROM my_table t " 
     + "WHERE t.field_1=:field_1 AND t.field_2=1 AND t.field_3 IN :field_3 " 
     + "AND FUNC('jsonb_extract_path_text', 'key', 'subkey')=:value") 
List<Entity> getEntities(@Param("field_1") String field_1, @Param("field_3") Collection<Integer> field_3, @Param("value") String value); 
Verwandte Themen