Spring Data JPA แมปผลลัพธ์เคียวรีเนทีฟกับ Non-Entity POJO


92

ฉันมีวิธีการจัดเก็บ Spring Data พร้อมด้วยแบบสอบถามเนทีฟ

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

และผมอยากจะ map ผลให้ไม่ Entity GroupDetailsPOJO

เป็นไปได้หรือไม่และหากเป็นเช่นนั้นโปรดยกตัวอย่างได้ไหม

คำตอบ:


65

สมมติว่า GroupDetails ในคำตอบของ orid คุณได้ลองใช้ JPA 2.1 @ConstructorResultหรือไม่?

@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")

และใช้สิ่งต่อไปนี้ในอินเทอร์เฟซที่เก็บ:

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

ตามฤดูใบไม้ผลิข้อมูล JPA เอกสารฤดูใบไม้ผลิแรกจะพยายามหาแบบสอบถามที่มีชื่อตรงกับชื่อวิธีของคุณ - ดังนั้นโดยใช้@NamedNativeQuery, @SqlResultSetMappingและ@ConstructorResultคุณควรจะสามารถที่จะบรรลุพฤติกรรมที่


15
เพื่อให้ข้อมูลในฤดูใบไม้ผลิสามารถจับคู่ NamedNativeQuery ได้ชื่อคลาสของเอนทิตีโดเมนตามด้วยจุดต้องนำหน้าชื่อของ NamedNativeQuery ดังนั้นชื่อควรเป็น (สมมติว่าเอนทิตีโดเมนคือ Group) 'Group.getGroupDetails'
Grant Lay

@GrantLay คุณสามารถดูคำถามนี้ได้ไหม: stackoverflow.com/q/44871757/7491770ฉันมีปัญหาแบบนี้
แรม

ฉันจะส่งคืนรายการวัตถุดังกล่าวได้อย่างไร
Nikhil Sahu

1
เพื่อให้ใช้งานได้ควรGroupDetailsมีเครื่องหมาย@Entity? หากเป็นไปได้โปรดบอกว่า@NamedNativeQueryต้องใช้คำอธิบายประกอบในชั้นเรียนใด
มนู

3
@SqlResultSetMappingและ@NamedNativeQueryต้องมีคำอธิบายประกอบในเอนทิตีที่ใช้ในที่เก็บข้อมูล Spring ของคุณ (เช่นpublic interface CustomRepository extends CrudRepository<CustomEntity, Long>เป็นCustomEntityคลาส)
Tomasz W

112

ฉันคิดว่าวิธีที่ง่ายที่สุดคือใช้สิ่งที่เรียกว่าการฉายภาพ สามารถแมปผลลัพธ์แบบสอบถามกับอินเทอร์เฟซ การใช้งานSqlResultSetMappingไม่สะดวกและทำให้โค้ดของคุณน่าเกลียด :)

ตัวอย่างจากแหล่งข้อมูล Spring JPA ซอร์สโค้ด:

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

  }
}

คุณยังสามารถใช้วิธีนี้เพื่อดูรายการประมาณการ

ตรวจสอบรายการเอกสาร JPA ข้อมูลฤดูใบไม้ผลินี้สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการคาดการณ์

หมายเหตุ 1:

อย่าลืมUserกำหนดเอนทิตีของคุณตามปกติ - ฟิลด์จากอินเทอร์เฟซที่คาดการณ์ไว้จะต้องตรงกับฟิลด์ในเอนทิตีนี้ มิฉะนั้นการแมปฟิลด์อาจเสีย ( getFirstname()อาจส่งคืนค่าของนามสกุลและอื่น ๆ )

โน้ต 2:

หากคุณใช้SELECT table.column ...สัญกรณ์ให้กำหนดนามแฝงที่ตรงกับชื่อจากเอนทิตีเสมอ ตัวอย่างเช่นรหัสนี้ทำงานไม่ถูกต้อง (การฉายภาพจะคืนค่า null สำหรับแต่ละ getter):

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

แต่ใช้งานได้ดี:

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

ในกรณีของการสืบค้นที่ซับซ้อนมากขึ้นฉันควรใช้JdbcTemplateกับพื้นที่เก็บข้อมูลแบบกำหนดเองแทน


เป็นทางออกที่สะอาดกว่า ฉันตรวจสอบแล้ว แต่ประสิทธิภาพแย่กว่าการใช้ SqlResultSetMapping มาก (ช้าประมาณ 30-40% :()
kidnan1991

ใช้งานได้ดี! ทำให้อินเทอร์เฟซเป็นแบบสาธารณะหากคุณต้องการใช้ที่อื่น
tibi

ไม่ทำงานถ้าคุณต้องการแยกฟิลด์ชนิด XML (clob) ข้อเสนอแนะใด ๆ ?
Ashish

@ Ashish ฉันอยากใช้JdbcTemplate( docs.spring.io/spring-framework/docs/current/javadoc-api/org/… ) แทน คุณสามารถใช้getClobวิธีการในการresultSetในการที่จะดึงข้อมูล InputStreamCLOB ตัวอย่างเช่น: rs.getClob("xml_column").getCharacterStream().
Michał Stochmal

จะเกิดอะไรขึ้นถ้าฉันใช้ SELECT * ในแบบสอบถามและแบบสอบถามเป็นแบบเนทีฟ?
Salman Kazmi

17

ฉันคิดว่าแนวทางของมิชาลดีกว่า แต่มีอีกวิธีหนึ่งที่จะทำให้ผลลัพธ์ออกมาจากข้อความค้นหาดั้งเดิม

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

ตอนนี้คุณสามารถแปลงอาร์เรย์สตริง 2D นี้เป็นเอนทิตีที่คุณต้องการได้


2
เรียบง่ายและสง่างาม
ห์น

9

คุณสามารถเขียนแบบสอบถามแบบเนทีฟหรือไม่ใช่เนทีฟของคุณในแบบที่คุณต้องการและคุณสามารถรวมผลลัพธ์คิวรี JPQL ด้วยอินสแตนซ์ของคลาสผลลัพธ์ที่กำหนดเองได้ สร้าง DTO ด้วยชื่อเดียวกันของคอลัมน์ที่ส่งคืนในคิวรีและสร้างตัวสร้างอาร์กิวเมนต์ทั้งหมดที่มีลำดับและชื่อเดียวกันกับที่ส่งคืนโดยคิวรี จากนั้นใช้วิธีต่อไปนี้เพื่อสอบถามฐานข้อมูล

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

สร้าง DTO:

package example;

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

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

การแก้ไข: ชื่อเดียวกันไม่บังคับ ... ลำดับของพารามิเตอร์เดียวกันในตัวสร้างและชุดผลลัพธ์ที่ส่งคืน
Waqas Memon

สิ่งนี้ใช้ได้เฉพาะในกรณีที่ Country เป็นคลาสเอนทิตี java ของคุณ สิ่งนี้จะไม่เกิดขึ้นหากประเทศไม่ใช่คลาสเอนทิตี java ของคุณ
Yeshwant KAKAD

1
คุณว่าสิ่งนี้ควรใช้กับคำค้นหาเนทีฟด้วยหรือไม่? คุณช่วยยกตัวอย่างได้ไหม
Richard Tingle

OP ขอคำค้นหาแบบเนทีฟ แต่ตัวอย่างที่ให้มานั้นไม่ใช่แบบเนทีฟ
CLS

0

คุณสามารถทำสิ่งที่ชอบ

@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)")

และต้องมีตัวสร้างเช่น

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
คำถามเกี่ยวกับการสืบค้นแบบเนทีฟไม่ใช่คำค้นหาที่เขียนด้วย HQL
DBK

-5

ในคอมพิวเตอร์ของฉันฉันได้รับรหัสนี้ซึ่งแตกต่างจากคำตอบของ 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")

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.