Spring JPA zur Auswahl bestimmter Spalten


146

Ich verwende Spring JPA, um alle Datenbankoperationen auszuführen. Ich weiß jedoch nicht, wie ich in Spring JPA bestimmte Spalten aus einer Tabelle auswählen soll.

Beispielsweise:
SELECT projectId, projectName FROM projects



Die Idee hinter JPA, nicht nach bestimmten Feldern zu suchen, ist, dass die Kosten (in Bezug auf die Effizienz) gleich sind, um eine Spalte oder alle Spalten aus einer Zeile der Tabelle zu holen.
Bestellung

7
@Desorder - die Kosten sind nicht immer gleich. Es ist wahrscheinlich keine große Sache für einfachere, primitive Datentypen, aber der Grund, warum ich auf dieser Seite gelandet bin, ist, dass ich festgestellt habe, dass eine einfache Abfrage "Listendokumente" langsam ausgeführt wurde. Diese Entität hat eine BLOB-Spalte (benötigt sie zum Hochladen / Speichern von Dateien) und ich vermute, dass sie langsam ist, da sie die BLOBs in den Speicher lädt, obwohl sie für die Auflistung der Dokumente nicht erforderlich sind.
jm0

@ jm0 Wie viele Tabellen hatten, soweit Sie sich erinnern, BLOB-Spalten?
Desorder

1
@Desorder Es war nur eine Tabelle, aber ich habe eine "Listen" -Funktion ausgeführt (Mehrfachzeile - Liste aller Dokumente, die mit einer bestimmten ID erstellt wurden). Der einzige Grund, warum ich dieses Problem bemerkte, war, dass diese einfache Listenabfrage einige Sekunden dauerte, während komplexere Abfragen in anderen Tabellen fast sofort erfolgten. Als ich merkte, dass es beim Hinzufügen von Zeilen immer mehr darunter leiden würde, lädt Spring JPA jedes BLOB in den Speicher, auch wenn es nicht verwendet wird. Ich habe eine anständige Lösung für Spring-Daten gefunden (siehe unten), aber ich denke, ich habe eine noch bessere, die reine JPA-Annotation ist. Ich werde tmrw posten, wenn es funktioniert
jm0

Antworten:


75

Sie können nativeQuery = truedie @QueryAnnotation aus einer RepositoryKlasse wie dieser festlegen :

public static final String FIND_PROJECTS = "SELECT projectId, projectName FROM projects";

@Query(value = FIND_PROJECTS, nativeQuery = true)
public List<Object[]> findProjects();

Beachten Sie jedoch, dass Sie das Mapping selbst durchführen müssen. Es ist wahrscheinlich einfacher, nur die reguläre Zuordnung wie diese zu verwenden, es sei denn, Sie benötigen wirklich nur diese beiden Werte:

public List<Project> findAll()

Es lohnt sich wahrscheinlich auch, sich die Spring- Datendokumente anzusehen .


5
Keine Notwendigkeit für native Abfragen. Sie sollten sie vermeiden, da sie die Vorteile von JPQL ruinieren. siehe Atals Antwort.
Phil294

1
Für mich musste ich das erste Attribut (oben FIND_PROJECTS) mit dem valueAttributnamen qualifizieren (wenn dies also mein Code wäre, hätte ich ihn schreiben @Query(value = FIND_PROJECTS, nativeQuery = true)müssen usw.
smeeb

172

Sie können Projektionen aus Spring Data JPA (doc) verwenden . Erstellen Sie in Ihrem Fall eine Schnittstelle:

interface ProjectIdAndName{
    String getId();
    String getName();
}

und fügen Sie Ihrem Repository die folgende Methode hinzu

List<ProjectIdAndName> findAll();

11
Dies ist eine saubere Lösung. Es kann eine Boiler-Vorlage haben, aber die Schnittstelle kann die innere Klasse der Entität sein. Mach es ganz sauber.
Iceman

1
genial,
denk

1
Wohin geht die projizierte Schnittstelle? in einer eigenen Datei oder kann es in die öffentliche Schnittstelle aufgenommen werden, die die vollständigen Entitätseigenschaften zurückgibt?
Micho Rizo

8
Diese Lösung funktioniert nicht, wenn JpaRepository erweitert wird. Kennt jemand eine Problemumgehung?
Deutsch

4
Sie können findAll () nicht verwenden. da es mit der JPARepositorys-Methode kollidieren wird. Sie müssen so etwas wie List <ProjectIdAndName> findAllBy () verwenden.
Code_Mode

137

Ich mag die Syntax nicht besonders (sie sieht ein bisschen hackig aus ...), aber dies ist die eleganteste Lösung, die ich finden konnte (sie verwendet eine benutzerdefinierte JPQL-Abfrage in der JPA-Repository-Klasse):

@Query("select new com.foo.bar.entity.Document(d.docId, d.filename) from Document d where d.filterCol = ?1")
List<Document> findDocumentsForListing(String filterValue);

Dann müssen Sie natürlich nur einen Konstruktor bereitstellen Document, der docId& filenameals Konstruktorargumente akzeptiert .


9
(und übrigens habe ich überprüft, dass Sie nicht den vollständig qualifizierten Klassennamen angeben müssen, wenn "Dokument" importiert wird - hatten Sie es einfach so, weil es in dem einzigen Beispiel, das ich finden konnte, so gemacht wurde)
jm0

Dies sollte die akzeptierte Antwort sein. Es funktioniert perfekt und wählt wirklich nur die notwendigen Felder aus.
Yonatan Wilkof

1
Die unnötigen Felder sind ebenfalls enthalten, aber würden diese Felder mit dem Wert 'null' Speicher belegen?
Gabbler

Ja, aber so minimal, dass es in den allermeisten Fällen wirklich lächerlich wäre, zu versuchen, dies zu umgehen - stackoverflow.com/questions/2430655/… Sie müssten spezielle leichte Objekte ohne diese Felder erstellen und sie auf dasselbe verweisen lassen Tabelle? Welche IMO unerwünscht ist, wenn ORMs verwendet und für ihre Beziehungen genutzt werden ... Hyperoptimierung besteht möglicherweise eher darin, nur ein leichtes Abfrage-DSL zu verwenden und direkt DTOs zuzuordnen, und selbst dann halte ich Redundanz für entmutigt
jm0

2
jm0 ohne vollqualifizierten Klassennamen hat es bei mir nicht funktioniert, obwohl es importiert wurde. Es wurde jedoch erfolgreich kompiliert.
Heisenberg

20

In meiner Situation brauche ich nur das json-Ergebnis, und das funktioniert bei mir:

public interface SchoolRepository extends JpaRepository<School,Integer> {
    @Query("select s.id, s.name from School s")
    List<Object> getSchoolIdAndName();
}

im Controller:

@Autowired
private SchoolRepository schoolRepository;

@ResponseBody
@RequestMapping("getschoolidandname.do")
public List<Object> getSchool() {
    List<Object> schools = schoolRepository.getSchoolIdAndName();
    return schools;
}

2
Sie sollten Objectdurch eine benutzerdefinierte Schnittstelle ersetzen, wie von mpr beschrieben. funktioniert einwandfrei
phil294

14

In meinem Fall habe ich eine separate Entitätsklasse ohne die Felder erstellt, die nicht benötigt werden (nur mit den Feldern, die benötigt werden).

Ordnen Sie die Entität derselben Tabelle zu. Wenn nun alle Spalten erforderlich sind, verwende ich die alte Entität. Wenn nur einige Spalten erforderlich sind, verwende ich die Lite-Entität.

z.B

@Entity
@Table(name = "user")
Class User{
         @Column(name = "id", unique=true, nullable=false)
         int id;
         @Column(name = "name", nullable=false)
         String name;
         @Column(name = "address", nullable=false)
         Address address;
}

Sie können so etwas erstellen wie:

@Entity
@Table(name = "user")
Class UserLite{
         @Column(name = "id", unique=true, nullable=false)
         int id;
         @Column(name = "name", nullable=false)
         String name;
}

Dies funktioniert, wenn Sie die abzurufenden Spalten kennen (und dies wird sich nicht ändern).

funktioniert nicht, wenn Sie die Spalten dynamisch festlegen müssen.


Hallo Sachin, ich habe einen Zweifel, ob ich die Entität wie oben erwähnt erstellen werde. Wenn JPA ausgeführt wird und versucht wird, eine Tabelle mit dem Namen des Benutzers zu erstellen. welche Entität wird verwenden.
user3364549

3
Erstellen Sie niemals eine Tabelle mit JPA, erstellen Sie Ihre Tabellen manuell in der Datenbank und verwenden Sie JPA, um die relationale Welt der Objektwelt zuzuordnen.
Sachin Sharma

Warum können Sie hier nicht von der Vererbung Gebrauch machen?
Deadbug

8

Ich denke, der einfachste Weg ist die Verwendung von QueryDSL , das mit den Spring-Data geliefert wird.

Mit Ihrer Frage kann die Antwort sein

JPAQuery query = new JPAQuery(entityManager);
List<Tuple> result = query.from(projects).list(project.projectId, project.projectName);
for (Tuple row : result) {
 System.out.println("project ID " + row.get(project.projectId));
 System.out.println("project Name " + row.get(project.projectName)); 
}}

Der Entitätsmanager kann automatisch verdrahtet sein und Sie arbeiten immer mit Objekten und Klassen ohne Verwendung der * QL-Sprache.

Wie Sie im Link sehen können, erscheint die letzte Auswahl fast für mich eleganter, dh die Verwendung von DTO zum Speichern des Ergebnisses. Wenden Sie auf Ihr Beispiel Folgendes an:

JPAQuery query = new JPAQuery(entityManager);
QProject project = QProject.project;
List<ProjectDTO> dtos = query.from(project).list(new QProjectDTO(project.projectId, project.projectName));

Definieren von ProjectDTO als:

class ProjectDTO {

 private long id;
 private String name;
 @QueryProjection
 public ProjectDTO(long projectId, String projectName){
   this.id = projectId;
   this.name = projectName;
 }
 public String getProjectId(){ ... }
 public String getProjectName(){....}
}

5

Mit den neueren Spring-Versionen kann man wie folgt vorgehen:

Wenn Sie keine native Abfrage verwenden, können Sie dies wie folgt tun:

public interface ProjectMini {
    String getProjectId();
    String getProjectName();
}

public interface ProjectRepository extends JpaRepository<Project, String> { 
    @Query("SELECT p FROM Project p")
    List<ProjectMini> findAllProjectsMini();
}

Mit der nativen Abfrage können Sie wie folgt vorgehen:

public interface ProjectRepository extends JpaRepository<Project, String> { 
    @Query(value = "SELECT projectId, projectName FROM project", nativeQuery = true)
    List<ProjectMini> findAllProjectsMini();
}

Einzelheiten finden Sie in den Dokumenten


4

Meiner Meinung nach ist dies eine großartige Lösung:

interface PersonRepository extends Repository<Person, UUID> {

    <T> Collection<T> findByLastname(String lastname, Class<T> type);
}

und es so zu benutzen

void someMethod(PersonRepository people) {

  Collection<Person> aggregates =
    people.findByLastname("Matthews", Person.class);

  Collection<NamesOnly> aggregates =
    people.findByLastname("Matthews", NamesOnly.class);
}

Warum nicht List <T> anstelle von Collection zurückgeben?!
Abdullah Khan

@AbdullahKhan, weil das Ergebnis möglicherweise nicht immer eine Bestellung hat.
Ravi Sanwal

4

Bei Verwendung von Spring Data JPA ist die Auswahl bestimmter Spalten aus der Datenbank vorgesehen

---- In DAOImpl ----

@Override
    @Transactional
    public List<Employee> getAllEmployee() throws Exception {
    LOGGER.info("Inside getAllEmployee");
    List<Employee> empList = empRepo.getNameAndCityOnly();
    return empList;
    }

---- In Repo ----

public interface EmployeeRepository extends CrudRepository<Employee,Integer> {
    @Query("select e.name, e.city from Employee e" )
    List<Employee> getNameAndCityOnly();
}

In meinem Fall hat es zu 100% funktioniert. Vielen Dank.


2

Sie können JPQL verwenden:

TypedQuery <Object[]> query = em.createQuery(
  "SELECT p.projectId, p.projectName FROM projects AS p", Object[].class);

List<Object[]> results = query.getResultList();

oder Sie können eine native SQL-Abfrage verwenden.

Query query = em.createNativeQuery("sql statement");
List<Object[]> results = query.getResultList();

2

Es ist möglich, nullals Feldwert in nativem SQL anzugeben.

@Query(value = "select p.id, p.uid, p.title, null as documentation, p.ptype " +
            " from projects p " +
            "where p.uid = (:uid)" +
            "  and p.ptype = 'P'", nativeQuery = true)
Project findInfoByUid(@Param("uid") String uid);

2

Sie können den folgenden Code in Ihrer Repository-Schnittstellenklasse anwenden.

Entitätsname bedeutet, dass Ihr Datenbanktabellenname wie Projekte ist. Und Liste bedeutet, dass Projekt eine Entitätsklasse in Ihren Projekten ist.

@Query(value="select p from #{#entityName} p where p.id=:projectId and p.projectName=:projectName")

List<Project> findAll(@Param("projectId") int projectId, @Param("projectName") String projectName);

0

Native Abfrage verwenden:

Query query = entityManager.createNativeQuery("SELECT projectId, projectName FROM projects");
List result = query.getResultList();

0

Sie können die von @jombie vorgeschlagene Antwort verwenden und:

  • Platzieren Sie die Schnittstelle in einer separaten Datei außerhalb der Entitätsklasse.
  • Verwenden Sie eine native Abfrage oder nicht (die Auswahl hängt von Ihren Anforderungen ab).
  • Überschreiben Sie die findAll()Methode zu diesem Zweck nicht, sondern verwenden Sie einen Namen Ihrer Wahl.
  • Denken Sie daran, eine Listmit Ihrer neuen Schnittstelle parametrisierte Datei zurückzugeben (z List<SmallProject>. B. ).
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.