Mit welcher Spalte arbeitet DISTINCT in JPA und ist es möglich, sie zu ändern?
Hier ist eine Beispiel-JPA-Abfrage mit DISTINCT:
select DISTINCT c from Customer c
Was nicht viel Sinn macht – auf welcher Spalte basiert die Unterscheidung? Ist es auf der Entität als Anmerkung angegeben, weil ich keine finden konnte?
Ich möchte die Spalte angeben, in der die Unterscheidung getroffen werden soll, etwa so:
select DISTINCT(c.name) c from Customer c
Ich verwende MySQL und Hibernate.
Abhängig vom zugrunde liegenden JPQL- oder Kriterien-API-Abfragetyp, DISTINCT
hat in JPA zwei Bedeutungen.
Skalare Abfragen
Für skalare Abfragen, die eine Skalarprojektion zurückgeben, wie die folgende Abfrage:
List<Integer> publicationYears = entityManager
.createQuery(
"select distinct year(p.createdOn) " +
"from Post p " +
"order by year(p.createdOn)", Integer.class)
.getResultList();
LOGGER.info("Publication years: {}", publicationYears);
Das DISTINCT
Das Schlüsselwort sollte an die zugrunde liegende SQL-Anweisung übergeben werden, da wir möchten, dass die DB-Engine Duplikate filtert, bevor sie die Ergebnismenge zurückgibt:
SELECT DISTINCT
extract(YEAR FROM p.created_on) AS col_0_0_
FROM
post p
ORDER BY
extract(YEAR FROM p.created_on)
-- Publication years: [2016, 2018]
Entitätsabfragen
Für Entitätsabfragen DISTINCT
hat eine andere Bedeutung.
Ohne zu benutzen DISTINCT
eine Abfrage wie die folgende:
List<Post> posts = entityManager
.createQuery(
"select p " +
"from Post p " +
"left join fetch p.comments " +
"where p.title = :title", Post.class)
.setParameter(
"title",
"High-Performance Java Persistence eBook has been released!"
)
.getResultList();
LOGGER.info(
"Fetched the following Post entity identifiers: {}",
posts.stream().map(Post::getId).collect(Collectors.toList())
);
wird beitreten post
und die post_comment
Tabellen wie diese:
SELECT p.id AS id1_0_0_,
pc.id AS id1_1_1_,
p.created_on AS created_2_0_0_,
p.title AS title3_0_0_,
pc.post_id AS post_id3_1_1_,
pc.review AS review2_1_1_,
pc.post_id AS post_id3_1_0__
FROM post p
LEFT OUTER JOIN
post_comment pc ON p.id=pc.post_id
WHERE
p.title="High-Performance Java Persistence eBook has been released!"
-- Fetched the following Post entity identifiers: [1, 1]
Aber die Eltern post
Datensätze werden in der Ergebnismenge für jeden zugeordneten Datensatz dupliziert post_comment
die Zeile. Aus diesem Grund ist die List
von Post
Entitäten enthalten Duplikate Post
Entitätsreferenzen.
Zur Beseitigung der Post
Entitätsreferenzen, die wir verwenden müssen DISTINCT
:
List<Post> posts = entityManager
.createQuery(
"select distinct p " +
"from Post p " +
"left join fetch p.comments " +
"where p.title = :title", Post.class)
.setParameter(
"title",
"High-Performance Java Persistence eBook has been released!"
)
.getResultList();
LOGGER.info(
"Fetched the following Post entity identifiers: {}",
posts.stream().map(Post::getId).collect(Collectors.toList())
);
Aber dann DISTINCT
wird auch an die SQL-Abfrage übergeben, und das ist überhaupt nicht erwünscht:
SELECT DISTINCT
p.id AS id1_0_0_,
pc.id AS id1_1_1_,
p.created_on AS created_2_0_0_,
p.title AS title3_0_0_,
pc.post_id AS post_id3_1_1_,
pc.review AS review2_1_1_,
pc.post_id AS post_id3_1_0__
FROM post p
LEFT OUTER JOIN
post_comment pc ON p.id=pc.post_id
WHERE
p.title="High-Performance Java Persistence eBook has been released!"
-- Fetched the following Post entity identifiers: [1]
Durch Vorbeigehen DISTINCT
Zur SQL-Abfrage wird der AUSFÜHRUNGSPLAN eine Extraausführung ausführen Sortieren Phase, die Overhead hinzufügt, ohne einen Wert zu bringen, da die Eltern-Kind-Kombinationen aufgrund der untergeordneten PK-Spalte immer eindeutige Datensätze zurückgeben:
Unique (cost=23.71..23.72 rows=1 width=1068) (actual time=0.131..0.132 rows=2 loops=1)
-> Sort (cost=23.71..23.71 rows=1 width=1068) (actual time=0.131..0.131 rows=2 loops=1)
Sort Key: p.id, pc.id, p.created_on, pc.post_id, pc.review
Sort Method: quicksort Memory: 25kB
-> Hash Right Join (cost=11.76..23.70 rows=1 width=1068) (actual time=0.054..0.058 rows=2 loops=1)
Hash Cond: (pc.post_id = p.id)
-> Seq Scan on post_comment pc (cost=0.00..11.40 rows=140 width=532) (actual time=0.010..0.010 rows=2 loops=1)
-> Hash (cost=11.75..11.75 rows=1 width=528) (actual time=0.027..0.027 rows=1 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
-> Seq Scan on post p (cost=0.00..11.75 rows=1 width=528) (actual time=0.017..0.018 rows=1 loops=1)
Filter: ((title)::text="High-Performance Java Persistence eBook has been released!"::text)
Rows Removed by Filter: 3
Planning time: 0.227 ms
Execution time: 0.179 ms
Entitätsabfragen mit HINT_PASS_DISTINCT_THROUGH
Um die Sort-Phase aus dem Ausführungsplan zu eliminieren, müssen wir die verwenden HINT_PASS_DISTINCT_THROUGH
JPA-Abfragehinweis:
List<Post> posts = entityManager
.createQuery(
"select distinct p " +
"from Post p " +
"left join fetch p.comments " +
"where p.title = :title", Post.class)
.setParameter(
"title",
"High-Performance Java Persistence eBook has been released!"
)
.setHint(QueryHints.HINT_PASS_DISTINCT_THROUGH, false)
.getResultList();
LOGGER.info(
"Fetched the following Post entity identifiers: {}",
posts.stream().map(Post::getId).collect(Collectors.toList())
);
Und jetzt wird die SQL-Abfrage nicht enthalten DISTINCT
aber Post
Duplikate von Entitätsreferenzen werden entfernt:
SELECT
p.id AS id1_0_0_,
pc.id AS id1_1_1_,
p.created_on AS created_2_0_0_,
p.title AS title3_0_0_,
pc.post_id AS post_id3_1_1_,
pc.review AS review2_1_1_,
pc.post_id AS post_id3_1_0__
FROM post p
LEFT OUTER JOIN
post_comment pc ON p.id=pc.post_id
WHERE
p.title="High-Performance Java Persistence eBook has been released!"
-- Fetched the following Post entity identifiers: [1]
Und der Ausführungsplan wird bestätigen, dass wir diesmal keine zusätzliche Sortierphase mehr haben:
Hash Right Join (cost=11.76..23.70 rows=1 width=1068) (actual time=0.066..0.069 rows=2 loops=1)
Hash Cond: (pc.post_id = p.id)
-> Seq Scan on post_comment pc (cost=0.00..11.40 rows=140 width=532) (actual time=0.011..0.011 rows=2 loops=1)
-> Hash (cost=11.75..11.75 rows=1 width=528) (actual time=0.041..0.041 rows=1 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
-> Seq Scan on post p (cost=0.00..11.75 rows=1 width=528) (actual time=0.036..0.037 rows=1 loops=1)
Filter: ((title)::text="High-Performance Java Persistence eBook has been released!"::text)
Rows Removed by Filter: 3
Planning time: 1.184 ms
Execution time: 0.160 ms
Update: Bitte sehen Sie sich die am besten bewertete Antwort an.
Meine eigene ist derzeit veraltet. Nur aus historischen Gründen hier aufbewahrt.
Distinct in HQL wird normalerweise in Joins benötigt und nicht in einfachen Beispielen wie Ihrem eigenen.
Siehe auch So erstellen Sie eine Distinct-Abfrage in HQL
@Entity
@NamedQuery(name = "Customer.listUniqueNames",
query = "SELECT DISTINCT c.name FROM Customer c")
public class Customer {
...
private String name;
public static List<String> listUniqueNames() {
return = getEntityManager().createNamedQuery(
"Customer.listUniqueNames", String.class)
.getResultList();
}
}
Ich bin einverstanden mit Kasanaki‘s Antwort, und es hat mir geholfen. Ich wollte die gesamte Entität auswählen, also habe ich verwendet
select DISTINCT(c) from Customer c
In meinem Fall habe ich eine Viele-zu-Viele-Beziehung und möchte Entitäten mit Sammlungen in einer Abfrage laden.
Ich habe LEFT JOIN FETCH verwendet und am Ende musste ich das Ergebnis eindeutig machen.
Ich würde die Konstruktorausdrucksfunktion von JPA verwenden. Siehe auch folgende Antwort:
JPQL-Konstruktorausdruck – org.hibernate.hql.ast.QuerySyntaxException: Tabelle ist nicht zugeordnet
Nach dem Beispiel in der Frage wäre es so etwas.
SELECT DISTINCT new com.mypackage.MyNameType(c.name) from Customer c
Ich füge eine Antwort hinzu, die etwas spezifisch ist, falls jemand auf das gleiche Problem wie ich stößt und diese Frage findet.
Ich habe JPQL mit Abfrageanmerkungen verwendet (keine Abfrageerstellung). Und ich musste eindeutige Werte für eine Entität erhalten, die es war eingebettet in eine andere Entität wurde die Beziehung über eine Viele-zu-Eins-Anmerkung bestätigt.
Ich habe zwei Datenbanktabellen:
- MainEntitydie ich mit unterschiedlichen Werten möchte
- LinkEntität, die eine Beziehungstabelle zwischen MainEntity und einer anderen Tabelle ist. Es hat einen zusammengesetzten Primärschlüssel, der aus seinen drei Spalten gebildet wird.
Im Java-Spring-Code führt dies zu drei implementierten Klassen:
LinkEntität:
@Entity
@Immutable
@Table(name="link_entity")
public class LinkEntity implements Entity {
@EmbeddedId
private LinkEntityPK pk;
// ... Getter, setter, toString()
}
LinkEntityPK :
@Embeddable
public class LinkEntityPK implements Entity, Serializable {
/** The main entity we want to have distinct values of */
@ManyToOne
@JoinColumn(name = "code_entity")
private MainEntity mainEntity;
/** */
@Column(name = "code_pk2")
private String codeOperation;
/** */
@Column(name = "code_pk3")
private String codeFonction;
MainEntity :
@Entity
@Immutable
@Table(name = "main_entity")
public class MainEntity implements Entity {
/** We use this for LinkEntity*/
@Id
@Column(name="code_entity")
private String codeEntity;
private String name;
// And other attributes, getters and setters
Die letzte Abfrage, um eindeutige Werte für die Hauptentität zu erhalten, lautet also:
@Repository
public interface EntityRepository extends JpaRepository<LinkEntity, String> {
@Query(
"Select " +
"Distinct linkEntity.pk.intervenant " +
"From " +
"LinkEntity as linkEntity " +
"Join MainEntity as mainEntity On " +
"mainEntity = linkEntity.pk.mainEntity ")
List<MainEntity> getMainEntityList();
}
Hoffe das kann jemandem helfen.
Welche spezifische Rolle spielt @Id im Lebenszyklus einer Entität?
– Samuel Owino
12. Januar 2020 um 0:26 Uhr