특정 열을 선택하는 Spring JPA


146

Spring JPA를 사용하여 모든 데이터베이스 작업을 수행하고 있습니다. 그러나 Spring JPA의 테이블에서 특정 열을 선택하는 방법을 모르겠습니다.

예를 들면 다음과 같습니다.
SELECT projectId, projectName FROM projects



JPA가 특정 필드를 찾지 않는 아이디어는 테이블의 한 행에서 하나의 열 또는 모든 열을 가져 오는 데 비용이 동일하다는 것입니다.
Desorder

7
@Desorder-비용이 항상 같지는 않습니다. 더 단순하고 원시적 인 데이터 유형의 경우 큰 문제는 아니지만이 페이지에서 내가 끝낸 이유는 간단한 "문서 목록"쿼리가 느리게 실행되는 것을 발견했기 때문입니다. 해당 엔티티에는 BLOB 열이 있으며 (파일 업로드 / 저장에 필요) 문서를 나열 할 필요는 없지만 BLOB를 메모리에로드하기 때문에 속도가 느리다고 생각합니다.
jm0

@ jm0 기억하는 한 BLOB 열이있는 테이블은 몇 개입니까?
Desorder

1
@Desorder 그것은 단지 하나의 테이블이지만 "목록"기능을하고있었습니다 (여러 행-주어진 ID로 작성된 모든 문서를 나열하십시오). 내가이 문제를 발견 한 유일한 이유는이 간단한 목록 쿼리가 몇 초가 걸리고 다른 테이블에 대한 더 복잡한 쿼리가 거의 즉시 발생했기 때문입니다. Spring JPA가 사용하지 않는 메모리까지 모든 BLOB를 메모리에로드하기 때문에 행이 추가됨에 따라 점점 더 많은 고통을 겪을 것이라는 것을 알았습니다. Spring 데이터에 대한 적절한 솔루션을 찾았지만 (아래 게시) 순수한 JPA 주석 인 더 나은 솔루션이 있다고 생각합니다 .TMrw가 작동하면 게시 할 것입니다.
jm0

답변:


75

다음 과 같은 클래스 nativeQuery = true에서 @Query주석을 설정할 수 있습니다 Repository.

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

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

그래도 맵핑을 직접 수행해야합니다. 실제로 두 값만 필요하지 않으면 다음과 같이 정규 매핑 조회를 사용하는 것이 더 쉽습니다.

public List<Project> findAll()

Spring 데이터 문서 도 살펴볼 가치가 있습니다.


5
기본 쿼리가 필요 없습니다. JPQL의 장점을 망치므로 사용하지 마십시오. Atals answer를 참조하십시오.
phil294

1
나를 위해 나는 첫 번째 속성 (위 FIND_PROJECTS)을 value속성 이름 으로 한정해야했다. (그래서 이것이 내 코드라면 다음과 같이 작성해야했다 @Query(value = FIND_PROJECTS, nativeQuery = true).)
smeeb

173

Spring Data JPA (doc)의 투영을 사용할 수 있습니다 . 귀하의 경우 인터페이스를 작성하십시오.

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

저장소에 다음 방법을 추가하십시오.

List<ProjectIdAndName> findAll();

11
이것은 깨끗한 해결책입니다. 보일러 템플릿이있을 수 있지만 인터페이스는 엔티티의 내부 클래스 일 수 있습니다. 꽤 깨끗하게.
iceman

1
굉장, 단지 당신의 엔티티에 인터페이스를 구현하지 마십시오 기억하지 않거나 작동하지 않습니다
alizelzele

1
투영 된 인터페이스는 어디로 갑니까? 자체 파일에 있거나 전체 엔티티 특성을 리턴하는 공용 인터페이스에 포함될 수 있습니까?
Micho Rizo

8
이 솔루션은 JpaRepository를 확장 할 때 작동하지 않습니다. 해결 방법을 아는 사람이 있습니까?
독일어

4
findAll ()을 사용할 수 없습니다. JPARepositorys 메소드와 충돌합니다. List <ProjectIdAndName>과 같은 것을 사용해야합니다. findAllBy ();
Code_Mode

137

특히 구문이 마음에 들지 않지만 (약간 해키처럼 보입니다 ...) 이것은 내가 찾을 수있는 가장 우아한 솔루션입니다 (JPA 리포지토리 클래스에서 사용자 정의 JPQL 쿼리 사용).

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

그런 다음 물론 & 생성자 인수 Document를 허용 하는 생성자를 제공하면 됩니다.docIdfilename


9
(그리고 내가 확인한 btw에서는 "문서"를 가져 오는 경우 정규화 된 클래스 이름을 제공 할 필요가 없습니다. 내가 찾은 유일한 샘플에서 그렇게했기 때문에 그렇게했습니다.)
jm0

이것이 정답이어야합니다. 완벽하게 작동하며 실제로 필요한 필드 만 선택합니다.
Yonatan Wilkof

1
불필요한 필드도 포함되지만 값이 'null'인 경우 해당 필드가 메모리를 차지합니까?
gabbler

그렇습니다. 그러나 최소한의 경우에는이 문제를 해결하려고 시도하는 것이 정말 어리 석습니다 .- stackoverflow.com/questions/2430655/ ... 이러한 필드없이 특수 경량 객체를 만들어야하고 동일하게 가리켜 야합니다. 표? ORM을 사용하고 관계에 활용할 때 IMO가 바람직하지 않습니다. 과도 최적화는 가벼운 쿼리 DSL을 사용하고 DTO에 직접 매핑하는 영역에있을 수 있습니다. 심지어 중복성이 권장되지 않습니다.
jm0

2
jm0 정규화 된 클래스 이름이 없으면 가져 오지 않았지만 작동하지 않았습니다. 그래도 성공적으로 컴파일되었습니다.
Heisenberg

20

내 상황에서는 json 결과 만 필요하며 이것은 나를 위해 작동합니다.

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

컨트롤러에서 :

@Autowired
private SchoolRepository schoolRepository;

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

2
Objectmpr에서 설명한대로 사용자 정의 인터페이스로 대체해야합니다 . 완벽하게 작동
phil294

14

필자의 경우 필자는 필요하지 않은 필드 (필수 필드 만)없이 별도의 엔티티 클래스를 만들었습니다.

엔티티를 동일한 테이블에 맵핑하십시오. 이제 모든 열이 필요할 때 이전 엔터티를 사용하고 일부 열만 필요한 경우 라이트 엔터티를 사용합니다.

예 :

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

다음과 같은 것을 만들 수 있습니다.

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

가져올 열을 알고있을 때 작동합니다 (변경되지 않음).

열을 동적으로 결정 해야하는 경우 작동하지 않습니다.


안녕하세요 sachin, 위에서 언급 한 것처럼 엔터티를 만들지 의심합니다. JPA가 실행될 때 사용자 이름으로 테이블을 작성하려고 시도합니다. 사용할 엔티티입니다.
user3364549

3
JPA로 테이블을 작성하지 말고 db에서 수동으로 테이블을 작성하고 JPA를 사용하여 관계 세계를 오브젝트 세계에 맵핑하십시오.
Sachin Sharma

여기서 상속을 사용할 수없는 이유는 무엇입니까?
deadbug

8

Spring-Data와 함께 제공되는 QueryDSL을 사용하는 것이 가장 쉬운 방법이라고 생각합니다 .

귀하의 질문에 대한 답은

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

엔티티 관리자는 자동 와이어 링 될 수 있으며 * QL 언어를 사용하지 않고 항상 오브젝트 및 클래스로 작업합니다.

링크에서 볼 수 있듯이 마지막 선택은 거의 나에게 더 우아합니다. 즉, 결과를 저장하기 위해 DTO를 사용하는 것 같습니다. 다음과 같은 예에 적용하십시오.

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

ProjectDTO를 다음과 같이 정의 :

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

최신 스프링 버전을 사용하면 다음과 같이 할 수 있습니다.

기본 쿼리를 사용하지 않는 경우 다음과 같이 수행 할 수 있습니다.

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

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

기본 쿼리를 사용하여 아래와 같이 수행 할 수 있습니다.

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

자세한 내용은 문서를 확인하십시오.


4

내 생각에 이것은 훌륭한 해결책입니다.

interface PersonRepository extends Repository<Person, UUID> {

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

그렇게 사용

void someMethod(PersonRepository people) {

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

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

컬렉션 대신 List <T>를 반환하지 않으시겠습니까?!
압둘라 칸

결과가 항상 순서를 가질 수는 없기 때문에 @AbdullahKhan.
Ravi Sanwal

4

Spring Data JPA를 사용하면 데이터베이스에서 특정 열을 선택하는 조항이 있습니다.

---- DAOImpl에서 ----

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

---- 레포에서 ----

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

내 경우에는 100 % 작동했습니다. 감사.


2

JPQL을 사용할 수 있습니다.

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

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

또는 네이티브 SQL 쿼리를 사용할 수 있습니다.

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

2

null네이티브 SQL에서 필드 값 으로 지정할 수 있습니다 .

@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

리포지토리 인터페이스 클래스에 아래 코드를 적용 할 수 있습니다.

entityname은 프로젝트와 같은 데이터베이스 테이블 이름을 의미합니다. 그리고 목록은 프로젝트가 프로젝트에서 프로젝트가 엔티티 클래스임을 의미합니다.

@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

기본 쿼리 사용 :

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

0

@jombie가 제안한 답변을 사용할 수 있습니다.

  • 인터페이스를 엔티티 클래스 외부의 별도 파일에 배치하십시오.
  • 기본 쿼리 사용 여부 (필요에 따라 선택)
  • findAll()이 목적을 위해 방법을 재정의하지 말고 원하는 이름을 사용하십시오.
  • List새 인터페이스 로 매개 변수화 된 매개 변수 를 반환 해야합니다 (예 :) List<SmallProject>.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.