Belirli sütunları seçerek bahar JPA


146

Tüm veritabanı işlemlerini gerçekleştirmek için Bahar JPA kullanıyorum. Ancak Bahar JPA bir tablodan belirli sütunları seçmek nasıl bilmiyorum?

Örneğin:
SELECT projectId, projectName FROM projects



JPA'nın belirli alanları aramamasının arkasındaki fikir, tablonun bir satırından bir sütun veya tüm sütunları getirmek için maliyetin (verimlilik açısından) aynı olmasıdır.
Desorder

7
@Desorder - maliyet her zaman aynı değildir. Muhtemelen daha basit, ilkel tür veri türleri için büyük bir sorun değil, ancak bu sayfada sonuçlandırmamın nedeni, basit bir "liste belgeleri" sorgusunun yavaş çalıştığını fark etmemdi. Bu varlığın bir BLOB sütunu var (dosya yükleme / depolama için gerekli) ve yavaş olduğundan şüpheleniyorum çünkü dokümanları listelemek için gerekli olmasa da BLOB'ları belleğe yüklüyor.
jm0

@ jm0 Hatırladığınız kadarıyla kaç sütun BLOB sütunu vardı?
Desorder

1
@Desorder sadece bir tablo oldu ama bir "list" işlevi (multirow - verilen bir id tarafından oluşturulan tüm belgeleri listelemek) yapıyordum. Bu sorunu fark etmenin tek nedeni, bu basit liste sorgusunun birkaç saniye sürmesi, diğer tablolardaki daha karmaşık sorguların neredeyse anında gerçekleşmesiydi. Bir kez fark ettim, satırlar eklendikçe daha fazla acı çekeceğini biliyordum çünkü Spring JPA kullanılmadıklarında bile her BLOB'u belleğe yüklüyor. Ben Bahar veriler için iyi bir çözüm (aşağıda yayınlanmıştır) bulundu ama saf JPA açıklama ise çok daha iyi bir tane var eğer çalışırsa, ben tmrw yayınlayacağız
jm0

Yanıtlar:


75

Ayarlayabilirsiniz nativeQuery = trueiçinde @Querybir gelen açıklama Repositoryşöyle sınıfa:

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

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

Ancak haritayı kendiniz yapmanız gerektiğini unutmayın. Sadece bu iki değere gerçekten ihtiyacınız olmadıkça, bu gibi düzenli eşlenmiş aramayı kullanmak daha kolaydır:

public List<Project> findAll()

Muhtemelen Spring veri belgelerine de bakmaya değer .


5
yerel sorgulara gerek yok. Bunları kullanmaktan kaçınmalısınız, çünkü JPQL'in avantajlarını mahvederler. bkz. Atals cevap.
phil294

1
Benim için ilk özellik (yukarıda nitelemek zorunda FIND_PROJECTSolan) valueolarak yazmak için bu benim kod olsaydı dolayısıyla ben olurdu özellik adının ( @Query(value = FIND_PROJECTS, nativeQuery = true)vs
smeeb

173

Spring Data JPA (doc) projeksiyonlarını kullanabilirsiniz . Sizin durumunuzda arayüz oluşturun:

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

ve deponuza aşağıdaki yöntemi ekleyin

List<ProjectIdAndName> findAll();

11
Bu temiz bir çözüm. kazan şablonu olabilir, ancak arayüz varlığın iç sınıfı olabilir. Oldukça temiz yapıyor.
iceman

1
harika, sadece
varlığınıza

1
yansıtılan arayüz nereye gidiyor? kendi dosyasında mı yoksa tüm varlık özelliklerini döndüren genel arayüzde yer alabilir mi?
Micho Rizo

8
Bu çözüm JpaRepository genişletilirken işe yaramıyor, herhangi bir çözüm biliyor mu?
Alman

4
FindAll () yöntemini kullanamazsınız; JPARepositorys yöntemi ile çakışacağı için. List <ProjectIdAndName> findAllBy (); gibi bir şey kullanmanız gerekir;
Code_Mode

137

Özellikle sözdizimini sevmiyorum (biraz hacky görünüyor ...) ama bulabildiğim en zarif çözüm (JPA veri havuzu sınıfında özel bir JPQL sorgusu kullanıyor):

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

O zaman elbette, yapıcı argümanlarını Documentkabul eden docIdve filenameyapıcı olarak bir yapıcı sağlamanız gerekir .


9
(ve btw doğruladı, "Belge" içe aktarılırsa tam nitelikli sınıf adı sağlamanız gerekmez - sadece bu şekilde vardı çünkü bulabildiğim tek örnekte böyle yapıldı)
jm0

bu kabul edilen cevap olmalıdır. Mükemmel çalışıyor ve gerçekten sadece gerekli alanları seçiyor.
Yonatan Wilkof

1
Gereksiz alanlar da dahil edilir, ancak 'null' değeriyle bu alanlar hafızayı kaplar mı?
gabbler

evet ama o kadar azdır ki, vakaların büyük çoğunluğunda bu konuda mühendislik yapmaya çalışmak gerçekten saçma olacaktır - stackoverflow.com/questions/2430655/… bu alanlar olmadan özel hafif nesneler yapmanız ve aynı noktaları göstermeniz gerekir masa mı? hangi IMO ORM'leri kullanırken ve ilişkileri için onları kullanırken istenmeyen ... hiper optimizasyon belki sadece biraz hafif sorgu DSL kullanma ve doğrudan
DTOs

2
jm0 ithal edilmiş olmasına rağmen tam nitelikli sınıf adı olmadan benim için çalışmadı. Ancak başarıyla derlendi.
Heisenberg

20

Benim durumumda, sadece json sonucuna ihtiyacım var ve bu benim için çalışıyor:

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

Denetleyicide:

@Autowired
private SchoolRepository schoolRepository;

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

2
Objectmpr tarafından açıklanan özel bir arayüzle değiştirmelisiniz . kusursuz çalışıyor
phil294

14

Benim durumumda gerekli olmayan alanlar olmadan ayrı bir varlık sınıfı oluşturdum (sadece gerekli olan alanlarla).

Varlığı aynı tabloyla eşleyin. Şimdi tüm sütunlar gerektiğinde eski varlığı kullanıyorum, yalnızca bazı sütunlar gerektiğinde lite varlığını kullanıyorum.

Örneğin

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

Şunun gibi bir şey oluşturabilirsiniz:

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

Bu getirilecek sütunları bildiğinizde işe yarar (ve bu değişmeyecektir).

sütunlara dinamik olarak karar vermeniz gerekiyorsa çalışmaz.


Merhaba sachin, eğer yukarıda bahsettiğin gibi varlığı yaratacaksam bir şüphem var. JPA çalıştığında ve kullanıcı adıyla tablo oluşturmaya çalışacaktır. hangi varlığın kullanacağı.
user3364549

3
Asla JPA ile bir tablo oluşturmak, db içinde tablolarınızı manuel olarak oluşturmak, ilişkisel dünya nesne dünyaya eşlemek için JPA kullanın.
Sachin Sharma

Neden burada mirastan yararlanamıyorsunuz?
deadbug

8

Sanırım en kolay yol , Spring-Data ile birlikte gelen QueryDSL kullanmaktır .

Sorunuzu kullanarak cevap olabilir

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

Varlık yöneticisi Otomatik Kablolu olabilir ve her zaman * QL dili kullanmadan nesne ve sınıflarla çalışacaksınız.

Bağlantıda görebileceğiniz gibi, son seçim neredeyse benim için daha zarif görünüyor, yani sonucu saklamak için DTO kullanıyor. Aşağıdaki örneklere başvurun:

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

ProjectDTO'nun tanımlanması:

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

Yeni Spring versiyonlarıyla One aşağıdakileri yapabilir:

Yerel sorgu kullanılmıyorsa, bu aşağıdaki gibi yapılabilir:

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

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

Yerel sorgu kullanılarak aynı şekilde yapılabilir:

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

Ayrıntılı bilgi için dokümanları kontrol edin


4

Bence bu harika bir çözüm:

interface PersonRepository extends Repository<Person, UUID> {

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

ve böyle kullanmak

void someMethod(PersonRepository people) {

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

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

Neden toplama yerine <T> Listesini döndürmüyorsunuz ?!
Abdullah Khan

@AbdullahKhan çünkü sonucun her zaman bir emri olmayabilir.
Ravi Sanwal

4

Spring Data JPA kullanıldığında, veritabanından belirli sütunları seçmek için bir hüküm vardır

---- DAOImpl'de ----

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

---- Repo'da ----

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

Benim durumumda% 100 çalıştı. Teşekkürler.


2

JPQL kullanabilirsiniz:

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

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

veya yerel sql sorgusu kullanabilirsiniz.

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

2

nullDoğal sql'de alan değeri olarak belirtmek mümkündür .

@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

Aşağıdaki kodu depo arabirim sınıfınıza uygulayabilirsiniz.

varlık adı, projeler gibi veritabanı tablo adınız anlamına gelir. Ve Liste, Projelerinizde Proje'nin Entity sınıfı olduğu anlamına gelir.

@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

Yerel Sorgu Kullanma:

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

0

@Jombie tarafından önerilen yanıtı kullanabilirsiniz ve:

  • arabirimi varlık sınıfı dışında ayrı bir dosyaya yerleştirin;
  • yerel sorgu kullan ya da kullan (seçim ihtiyaçlarınıza bağlı olarak);
  • findAll()bu amaç için yöntemi geçersiz kılmayın, ancak seçtiğiniz adı kullanın;
  • Listyeni arayüzünüzle parametreli bir parametre döndürmeyi unutmayın (örn. List<SmallProject>).
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.