Spring Data JPA, yerel sorgu sonucunu Varlık Dışı POJO ile eşler


92

Yerel sorgu içeren bir Spring Data veri havuzu yöntemim var

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

ve sonucu Varlık Dışı POJO ile eşlemek istiyorum GroupDetails.

Mümkün mü ve eğer öyleyse, lütfen bir örnek verebilir misiniz?

Yanıtlar:


65

GroupDetails'i orid'in cevabındaki gibi varsayarsak, JPA 2.1 @ConstructorResult'u denediniz mi?

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

ve depo arayüzünde aşağıdakileri kullanın:

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

Spring Data JPA belgelerine göre , spring önce yöntem adınızla eşleşen adlandırılmış sorguyu bulmaya çalışacaktır - bu yüzden kullanarak @NamedNativeQuery, @SqlResultSetMappingve @ConstructorResultbu davranışı elde edebilmelisiniz.


15
Yay verilerinin NamedNativeQuery ile eşleşebilmesi için, etki alanı varlığının Sınıf adı ve ardından bir nokta, NamedNativeQuery adına önek olarak eklenmesi gerekir. Bu nedenle ad, (Etki alanı varlığının Group olduğu varsayılarak) 'Group.getGroupDetails' olmalıdır.
Grant Lay

@GrantLay şu soruya bir göz atabilir misin: stackoverflow.com/q/44871757/7491770 Tam olarak böyle bir sorunum var.
ram

Bu tür Nesnelerin bir listesini nasıl döndüreceğim?
Nikhil Sahu

1
Çalışması için, GroupDetailsile işaretlenmeli @Entitymi? Mümkünse notun hangi sınıfa @NamedNativeQueryuygulanması gerektiğini söyler misiniz ?
Manu

3
@SqlResultSetMappingve @NamedNativeQuery(örneğin ek açıklamalar Bahar Veri deposu olarak kullanılan bir varlık üzerinde bulunması gerekir public interface CustomRepository extends CrudRepository<CustomEntity, Long>öyle CustomEntitysınıfı)
Tomasz W

112

Bunu yapmanın en kolay yolunun sözde projeksiyon kullanmak olduğunu düşünüyorum. Sorgu sonuçlarını arayüzlerle eşleştirebilir. Kullanmak SqlResultSetMappingsakıncalıdır ve kodunuzu çirkinleştirir :).

Yay verisi JPA kaynak kodundan bir örnek:

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

  }
}

Projeksiyonların bir listesini almak için bu yöntemi de kullanabilirsiniz.

Öngörüler hakkında daha fazla bilgi için bu bahar verileri JPA belgeleri girişine bakın.

Not 1:

Varlığınızın Usernormal olarak tanımlanmasını unutmayın - öngörülen arayüzdeki alanlar bu varlıktaki alanlarla eşleşmelidir. Aksi takdirde alan eşlemesi bozulabilir ( getFirstname()soyadı vb. Değerini döndürebilir).

Not 2:

Eğer kullanırsanız SELECT table.column ...gösterim daima varlıktan eşleşen adları takma adları tanımlar. Örneğin, bu kod düzgün çalışmaz (projeksiyon her alıcı için boş değerler döndürür):

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

Ama bu gayet iyi çalışıyor:

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

Daha karmaşık sorgular söz konusu olduğunda, JdbcTemplatebunun yerine özel depo kullanmayı tercih ederim .


Daha temiz bir çözümdür. Kontrol ettim ama performans SqlResultSetMapping'i kullanmaktan çok daha kötü (yaklaşık% 30-40 daha yavaş :()
kidnan1991

güzel çalışıyor! arayüzü başka bir yerde kullanmak istiyorsanız herkese açık hale getirin
tibi

XML türü (clob) alanını çıkarmak istiyorsanız çalışmaz. Herhangi bir öneri?
Ashish

@Ashish bunun yerine JdbcTemplate( docs.spring.io/spring-framework/docs/current/javadoc-api/org/… ) kullanmayı tercih ederim . Clob'u getirmek için getClobon metodunu kullanabilirsiniz . Örnek olarak: . resultSetInputStreamrs.getClob("xml_column").getCharacterStream()
Michal Stochmal

Sorguda SELECT * kullanırsam ve sorgu yerel bir sorgu ise ne olur?
Salman Kazmi

17

Bence Michal'in yaklaşımı daha iyi. Ancak, sonucu yerel sorgudan almanın bir yolu daha var.

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

Şimdi, bu 2D dizgi dizisini istediğiniz varlığa dönüştürebilirsiniz.


2
sade ve zarif
john

9

Yerel veya yerel olmayan sorgunuzu istediğiniz şekilde yazabilir ve JPQL sorgu sonuçlarını özel sonuç sınıflarının örnekleriyle sarmalayabilirsiniz. Sorguda döndürülen aynı sütun adlarıyla bir DTO oluşturun ve sorgu tarafından döndürülenle aynı sıra ve adlara sahip bir tüm bağımsız değişken oluşturucu oluşturun. Ardından veritabanını sorgulamak için aşağıdaki yolu kullanın.

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

DTO oluşturun:

package example;

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

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

düzeltme: aynı adlar zorunlu değildir ... Yapıcıda aynı parametre dizisi ve döndürülen sonuç kümesi.
Waqas Memon

Bu yalnızca Ülke, java varlık sınıfınız ise çalışır. Ülke java varlık sınıfınız değilse bu olmaz.
Yeshwant KAKAD

1
Bunun yerel sorgularla da çalışması gerektiğini söylüyorsunuz? Buna bir örnek verebilir misiniz?
Richard Tingle

OP yerel sorgu ister, ancak verilen örnek yerel olmayan bir örnektir
CLS

0

Gibi bir şey yapabilirsin

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

Ve bir Oluşturucu olmalı

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
Soru, HQL'de yazılan sorgularla değil, yerel sorgularla ilgilidir.
DBK

-5

Bilgisayarımda bu kodun çalıştığını görüyorum, Daimon'un cevabından biraz farklı.

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.