Spring Data JPA ánh xạ kết quả truy vấn gốc tới POJO không phải thực thể


91

Tôi có phương pháp lưu trữ Dữ liệu mùa xuân với truy vấn gốc

@Query(value = "SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", nativeQuery = true)
    GroupDetails getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);

và tôi muốn ánh xạ kết quả tới POJO không phải thực thể GroupDetails.

Có thể không và nếu có, bạn có thể vui lòng cung cấp một ví dụ?

Câu trả lời:


64

Giả sử GroupDetails như trong câu trả lời của orid, bạn đã thử JPA 2.1 @ConstructorResult chưa?

@SqlResultSetMapping(
    name="groupDetailsMapping",
    classes={
        @ConstructorResult(
            targetClass=GroupDetails.class,
            columns={
                @ColumnResult(name="GROUP_ID"),
                @ColumnResult(name="USER_ID")
            }
        )
    }
)

@NamedNativeQuery(name="getGroupDetails", query="SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", resultSetMapping="groupDetailsMapping")

và sử dụng sau trong giao diện kho lưu trữ:

GroupDetails getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);

Theo mùa xuân dữ liệu JPA tài liệu , đầu tiên mùa xuân sẽ cố gắng tìm tên truy vấn phù hợp với tên phương pháp của bạn - vì vậy bằng cách sử dụng @NamedNativeQuery, @SqlResultSetMapping@ConstructorResultbạn sẽ có thể để đạt được hành vi đó


15
Để dữ liệu mùa xuân có thể khớp với NamedNativeQuery, tên Lớp của thực thể miền theo sau là dấu chấm, cần được đặt trước tên của NamedNativeQuery. Vì vậy, tên phải là (Giả sử thực thể miền là Nhóm) 'Group.getGroupDetails'.
Grant Lay

@GrantLay, bạn có thể xem câu hỏi này không: stackoverflow.com/q/44871757/7491770 Tôi gặp chính xác loại vấn đề này.
ram

Làm cách nào để trả về danh sách các Đối tượng như vậy?
Nikhil Sahu

1
Để nó hoạt động, nên GroupDetailsđánh dấu bằng @Entity? Nếu có thể, bạn có thể vui lòng cho biết lớp nào mà chú thích @NamedNativeQueryphải được áp dụng?
Manu

3
@SqlResultSetMapping@NamedNativeQuerychú thích phải có trên thực thể được sử dụng trong kho lưu trữ Dữ liệu mùa xuân của bạn (ví dụ: đối với public interface CustomRepository extends CrudRepository<CustomEntity, Long>nó là CustomEntitylớp)
Tomasz W

110

Tôi nghĩ cách dễ nhất để làm điều đó là sử dụng cái gọi là phép chiếu. Nó có thể ánh xạ kết quả truy vấn tới các giao diện. Sử dụng SqlResultSetMappinglà bất tiện và làm cho mã của bạn trở nên xấu xí :).

Một ví dụ ngay từ mã nguồn JPA dữ liệu mùa xuân:

public interface UserRepository extends JpaRepository<User, Integer> {

   @Query(value = "SELECT firstname, lastname FROM SD_User WHERE id = ?1", nativeQuery = true)
   NameOnly findByNativeQuery(Integer id);

   public static interface NameOnly {

     String getFirstname();

     String getLastname();

  }
}

Bạn cũng có thể sử dụng phương pháp này để lấy danh sách các phép chiếu.

Kiểm tra mục nhập tài liệu JPA dữ liệu mùa xuân này để biết thêm thông tin về các phép chiếu.

Lưu ý 1:

Hãy nhớ Userxác định thực thể của bạn là bình thường - các trường từ giao diện được chiếu phải khớp với các trường trong thực thể này. Nếu không, ánh xạ trường có thể bị hỏng ( getFirstname()có thể trả về giá trị của họ và vân vân).

Lưu ý 2:

Nếu bạn sử dụng SELECT table.column ...ký hiệu, hãy luôn xác định bí danh khớp với tên từ thực thể. Ví dụ: mã này sẽ không hoạt động bình thường (phép chiếu sẽ trả về giá trị null cho mỗi getter):

@Query(value = "SELECT user.firstname, user.lastname FROM SD_User user WHERE id = ?1", nativeQuery = true)
NameOnly findByNativeQuery(Integer id);

Nhưng điều này hoạt động tốt:

@Query(value = "SELECT user.firstname AS firstname, user.lastname AS lastname FROM SD_User user WHERE id = ?1", nativeQuery = true)
NameOnly findByNativeQuery(Integer id);

Trong trường hợp các truy vấn phức tạp hơn, JdbcTemplatethay vào đó tôi muốn sử dụng với kho lưu trữ tùy chỉnh.


Đó là một giải pháp sạch hơn. Tôi đã kiểm tra nhưng hiệu suất là tồi tệ hơn nhiều so với sử dụng SqlResultSetMapping (nó là chậm hơn khoảng 30-40% :()
kidnan1991

hoạt động tốt! làm cho công chúng giao diện nếu bạn muốn sử dụng nó ở nơi khác
Tibi

Không hoạt động nếu bạn muốn trích xuất trường kiểu XML (clob). Bất kì lời đề nghị nào?
Ashish

@Ashish Tôi muốn sử dụng JdbcTemplate( docs.spring.io/spring-framework/docs/current/javadoc-api/org/… ) thay thế. Bạn có thể sử dụng getClobphương pháp trên resultSetđể lấy cục máu đông InputStream. Đối với một ví dụ: rs.getClob("xml_column").getCharacterStream().
Michał Stochmal

Điều gì sẽ xảy ra nếu tôi sử dụng SELECT * trong truy vấn và truy vấn là truy vấn gốc?
Salman Kazmi

17

Tôi nghĩ cách tiếp cận của Michal tốt hơn. Tuy nhiên, có một cách nữa để lấy kết quả ra khỏi truy vấn gốc.

@Query(value = "SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", nativeQuery = true)
String[][] getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);

Bây giờ, bạn có thể chuyển đổi mảng chuỗi 2D này thành thực thể mong muốn của mình.


2
đơn giản và thanh lịch
John

9

Bạn có thể viết truy vấn gốc hoặc không gốc theo cách bạn muốn và bạn có thể bao bọc kết quả truy vấn JPQL với các phiên bản của lớp kết quả tùy chỉnh. Tạo DTO với cùng tên của các cột được trả về trong truy vấn và tạo một phương thức khởi tạo tất cả đối số có cùng trình tự và tên như được trả về bởi truy vấn. Sau đó, sử dụng cách sau để truy vấn cơ sở dữ liệu.

@Query("SELECT NEW example.CountryAndCapital(c.name, c.capital.name) FROM Country AS c")

Tạo DTO:

package example;

public class CountryAndCapital {
    public String countryName;
    public String capitalName;

    public CountryAndCapital(String countryName, String capitalName) {
        this.countryName = countryName;
        this.capitalName = capitalName;
    }
}

sửa: không bắt buộc phải trùng tên ... chỉ là cùng một chuỗi các tham số trong hàm tạo và tập kết quả trả về.
Waqas Memon

Điều này chỉ hoạt động nếu Quốc gia là lớp thực thể java của bạn. Điều này sẽ không xảy ra nếu Quốc gia không phải là lớp thực thể java của bạn.
Yeshwant KAKAD

1
Bạn nói điều này cũng nên hoạt động với các truy vấn gốc? Bạn có thể cho một ví dụ về điều đó?
Richard Tingle

OP yêu cầu truy vấn gốc, nhưng ví dụ được đưa ra là một truy vấn không gốc
CLS

0

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

@NamedQuery(name="IssueDescriptor.findByIssueDescriptorId" ,

    query=" select new com.test.live.dto.IssuesDto (idc.id, dep.department, iss.issueName, 
               cat.issueCategory, idc.issueDescriptor, idc.description) 
            from Department dep 
            inner join dep.issues iss 
            inner join iss.category cat 
            inner join cat.issueDescriptor idc 
            where idc.id in(?1)")

Và phải có Constructor như

public IssuesDto(long id, String department, String issueName, String issueCategory, String issueDescriptor,
            String description) {
        super();
        this.id = id;
        this.department = department;
        this.issueName = issueName;
        this.issueCategory = issueCategory;
        this.issueDescriptor = issueDescriptor;
        this.description = description;
    }

13
Câu hỏi là về các truy vấn gốc, không phải là các truy vấn được viết bằng HQL.
DBK

-5

Trong máy tính, tôi nhận được đoạn mã này hoạt động, nó hơi khác với câu trả lời của Daimon.

@SqlResultSetMapping(
    name="groupDetailsMapping",
    classes={
        @ConstructorResult(
            targetClass=GroupDetails.class,
            columns={
                @ColumnResult(name="GROUP_ID",type=Integer.class),
                @ColumnResult(name="USER_ID",type=Integer.class)
            }
        )
    }
)

@NamedNativeQuery(name="User.getGroupDetails", query="SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", resultSetMapping="groupDetailsMapping")

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.