Spring JPA chọn các cột cụ thể


146

Tôi đang sử dụng Spring JPA để thực hiện tất cả các hoạt động cơ sở dữ liệu. Tuy nhiên tôi không biết cách chọn các cột cụ thể từ một bảng trong Spring JPA?

Ví dụ:
SELECT projectId, projectName FROM projects



Ý tưởng đằng sau JPA không tìm kiếm các lĩnh vực cụ thể là chi phí (hiệu quả khôn ngoan) giống nhau để mang một cột hoặc tất cả các cột từ một hàng của bảng.
Sắp xếp thứ tự

7
@Desorder - chi phí không phải lúc nào cũng giống nhau. Có lẽ nó không phải là vấn đề lớn đối với các kiểu dữ liệu đơn giản, nguyên thủy hơn nhưng lý do tôi kết thúc trên trang này là vì tôi nhận thấy một truy vấn "danh sách tài liệu" đơn giản đang chạy chậm. Thực thể đó có cột BLOB (cần nó để tải lên / lưu trữ tệp) & tôi nghi ngờ nó chậm vì đang tải BLOB vào bộ nhớ mặc dù chúng không bắt buộc phải liệt kê tài liệu.
jm0

@ jm0 Theo bạn nhớ, có bao nhiêu bảng có cột BLOB?
Sắp xếp thứ tự 14/03/2015

1
@Desorder chỉ là một bảng nhưng tôi đang thực hiện chức năng "liệt kê" (multirow - liệt kê tất cả các tài liệu được tạo bởi một id đã cho). Lý do duy nhất tôi nhận thấy vấn đề này là vì truy vấn danh sách đơn giản này mất vài giây, trong khi các truy vấn phức tạp hơn trên các bảng khác đang diễn ra gần như ngay lập tức. Khi tôi nhận ra, tôi biết rằng nó sẽ bị ảnh hưởng nhiều hơn khi các hàng được thêm vào vì Spring JPA đang tải mọi BLOB vào bộ nhớ ngay cả khi chúng không được sử dụng. Tôi đã tìm thấy một giải pháp tốt cho dữ liệu Spring (được đăng dưới đây) nhưng tôi nghĩ rằng tôi còn có một giải pháp tốt hơn đó là chú thích JPA thuần túy, tôi sẽ đăng tmrw nếu nó hoạt động
jm0

Câu trả lời:


75

Bạn có thể đặt nativeQuery = truetrong @Querychú thích từ một Repositorylớp học như thế này:

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

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

Lưu ý rằng bạn sẽ phải tự làm bản đồ. Có thể dễ dàng hơn khi chỉ sử dụng tra cứu được ánh xạ thông thường như thế này trừ khi bạn thực sự chỉ cần hai giá trị đó:

public List<Project> findAll()

Có lẽ cũng đáng để xem tài liệu dữ liệu mùa xuân .


5
không cần truy vấn riêng. Bạn nên tránh sử dụng chúng, vì chúng phá hỏng những lợi thế của JPQL. xem Atals trả lời.
phil294

1
Đối với tôi, tôi phải đủ điều kiện thuộc tính đầu tiên (ở trên FIND_PROJECTS) với valuetên thuộc tính (do đó nếu đây là mã của tôi, tôi sẽ phải viết nó dưới dạng @Query(value = FIND_PROJECTS, nativeQuery = true), v.v.
smeeb

172

Bạn có thể sử dụng các phép chiếu từ Spring Data JPA (doc) . Trong trường hợp của bạn, tạo giao diện:

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

và thêm phương thức sau vào kho lưu trữ của bạn

List<ProjectIdAndName> findAll();

11
Đây là một giải pháp sạch. nó có thể có mẫu nồi hơi nhưng giao diện có thể là lớp bên trong của thực thể. Làm cho nó khá sạch sẽ.
iceman

1
thật tuyệt vời, chỉ cần nhớ không thực hiện giao diện trên Thực thể của bạn hoặc nó sẽ không hoạt động
alizelzele

1
Giao diện dự kiến ​​đi đâu? trong tập tin riêng của nó hoặc nó có thể được bao gồm trong giao diện công cộng trả về các thuộc tính thực thể đầy đủ không?
Micho Rizo

8
Giải pháp này không hoạt động khi mở rộng JpaRep repository, có ai biết cách giải quyết không?
Đức

4
Bạn không thể sử dụng findAll (); vì nó sẽ xung đột với phương thức JPARep repository. Bạn cần sử dụng một cái gì đó như Danh sách <ProjectIdAndName> findAllBy ();
Code_Mode

137

Tôi không thích cú pháp đặc biệt (có vẻ hơi hack ...) nhưng đây là giải pháp tao nhã nhất mà tôi có thể tìm thấy (nó sử dụng truy vấn JPQL tùy chỉnh trong lớp kho lưu trữ JPA):

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

Sau đó, tất nhiên, bạn chỉ cần cung cấp một hàm tạo cho Documentchấp nhận đó docId& filenamenhư hàm tạo lập luận.


9
(và btw tôi đã xác minh, bạn không cần cung cấp tên lớp đủ điều kiện nếu "Tài liệu" được nhập - cứ như vậy vì đó là cách nó được thực hiện trong mẫu duy nhất tôi có thể tìm thấy)
jm0

đây sẽ là câu trả lời được chấp nhận Nó hoạt động hoàn hảo và thực sự chỉ chọn các lĩnh vực cần thiết.
Yonatan Wilkof

1
Các trường không cần thiết cũng được bao gồm, nhưng với giá trị 'null', các trường đó có chiếm bộ nhớ không?
gabbler

vâng nhưng cho đến tối thiểu mà trong đa số trường hợp nó sẽ thực sự vô lý để cố gắng kỹ sư xung quanh này - stackoverflow.com/questions/2430655/... bạn phải làm cho đối tượng nhẹ chuyên dụng nhưng không các lĩnh vực này & có chúng trỏ đến cùng bàn? IMO không mong muốn khi sử dụng ORM và tận dụng chúng cho các mối quan hệ của họ ... siêu tối ưu hóa có thể nhiều hơn trong lĩnh vực chỉ sử dụng một số DSL truy vấn nhẹ và ánh xạ trực tiếp tới DTOs, và thậm chí sau đó tôi nghĩ rằng sự dư thừa không được khuyến khích
jm0

2
jm0 nó không hoạt động với tôi nếu không có tên lớp đủ điều kiện, mặc dù nó đã được nhập. Nó đã biên dịch thành công mặc dù.
Heisenberg

20

Trong tình huống của tôi, tôi chỉ cần kết quả json và điều này hiệu quả với tôi:

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

trong Bộ điều khiển:

@Autowired
private SchoolRepository schoolRepository;

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

2
bạn nên thay thế Objectbằng một giao diện tùy chỉnh như được mô tả bởi mpr. hoạt động hoàn hảo
phil294

14

Trong trường hợp của tôi, tôi đã tạo một lớp thực thể riêng biệt mà không có các trường không bắt buộc (chỉ với các trường được yêu cầu).

Ánh xạ thực thể vào cùng một bảng. Bây giờ khi tất cả các cột được yêu cầu tôi sử dụng thực thể cũ, khi chỉ một số cột được yêu cầu, tôi sử dụng thực thể lite.

ví dụ

@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;
}

Bạn có thể tạo một cái gì đó như:

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

Điều này hoạt động khi bạn biết các cột để tìm nạp (và điều này sẽ không thay đổi).

sẽ không hoạt động nếu bạn cần tự động quyết định các cột.


Xin chào sachin, tôi có một nghi ngờ nếu tôi sẽ tạo ra thực thể như bạn đề cập ở trên. khi JPA sẽ chạy và nó sẽ cố gắng tạo bảng với tên người dùng. thực thể nào sẽ sử dụng.
dùng3364549

3
không bao giờ tạo bảng với JPA, tạo các bảng của bạn theo cách thủ công trong db, sử dụng JPA để ánh xạ thế giới quan hệ sang thế giới đối tượng.
Sachin Sharma

Tại sao bạn không thể sử dụng quyền thừa kế ở đây?
bọ hung

8

Tôi đoán cách dễ dàng có thể là sử dụng QueryDSL , đi kèm với Spring-Data.

Sử dụng cho câu hỏi của bạn câu trả lời có thể là

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)); 
}}

Trình quản lý thực thể có thể được Tự động và bạn sẽ luôn làm việc với đối tượng và các cụm mà không sử dụng ngôn ngữ * QL.

Như bạn có thể thấy trong liên kết, sự lựa chọn cuối cùng dường như, đối với tôi, thanh lịch hơn, đó là sử dụng DTO để lưu trữ kết quả. Áp dụng cho ví dụ của bạn sẽ là:

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

Xác định ProjectDTO là:

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

Với các phiên bản Spring mới hơn, người ta có thể làm như sau:

Nếu không sử dụng truy vấn gốc, điều này có thể được thực hiện như dưới đây:

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

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

Sử dụng truy vấn gốc tương tự có thể được thực hiện như dưới đây:

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

Để biết chi tiết kiểm tra tài liệu


4

Theo tôi đây là giải pháp tuyệt vời:

interface PersonRepository extends Repository<Person, UUID> {

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

và sử dụng nó như vậy

void someMethod(PersonRepository people) {

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

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

Tại sao không trả về Danh sách <T> thay vì bộ sưu tập?!
Abdullah Khan

@AbdullahKhan vì kết quả có thể không phải lúc nào cũng có đơn hàng.
Ravi Sanwal

4

Sử dụng Spring Data JPA có một điều khoản để chọn các cột cụ thể từ cơ sở dữ liệu

---- Trong DAOImpl ----

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

---- Trong Repo ----

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

Nó hoạt động 100% trong trường hợp của tôi. Cảm ơn.


2

Bạn có thể sử dụng JPQL:

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

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

hoặc bạn có thể sử dụng truy vấn sql bản địa.

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

2

Có thể chỉ định nulllà giá trị trường trong sql gốc.

@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

Bạn có thể áp dụng mã dưới đây trong lớp giao diện kho lưu trữ của bạn.

entityname có nghĩa là tên bảng cơ sở dữ liệu của bạn giống như các dự án. Và Danh sách có nghĩa là Dự án là lớp Thực thể trong Dự án của bạn.

@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

Sử dụng truy vấn gốc:

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

0

Bạn có thể sử dụng câu trả lời được đề xuất bởi @jombie và:

  • đặt giao diện trong một tệp riêng biệt, bên ngoài lớp thực thể;
  • sử dụng truy vấn gốc hay không (sự lựa chọn phụ thuộc vào nhu cầu của bạn);
  • không ghi đè findAll()phương thức cho mục đích này mà sử dụng tên bạn chọn;
  • nhớ trả lại một Listtham số với giao diện mới của bạn (ví dụ List<SmallProject>).
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.