特定の列を選択するSpring JPA


146

Spring JPAを使用してすべてのデータベース操作を実行しています。しかし、Spring JPAのテーブルから特定の列を選択する方法がわかりませんか?

例えば:
SELECT projectId, projectName FROM projects



JPAが特定のフィールドを検索しない背景にある考え方は、テーブルの1つの行から1つの列またはすべての列を取得するのと同じコスト(効率)であるということです。
2014

7
@Desorder-コストは常に同じではありません。単純なプリミティブなデータ型の場合は、それほど大きな問題ではないかもしれませんが、このページにたどり着いたのは、単純な「リストドキュメント」クエリの実行が遅いことに気付いたからです。そのエンティティにはBLOB列があり(ファイルのアップロード/ストレージに必要です)、ドキュメントのリストに必要ない場合でも、BLOBをメモリにロードしているため、遅いと思われます。
jm0 2015年

@ jm0あなたが覚えている限り、BLOB列があったテーブルはいくつありますか?
Desorder

1
@Desorderは1つのテーブルだけでしたが、「リスト」関数(複数行-指定されたIDで作成されたすべてのドキュメントをリスト)を実行していました。この問題に気付いた唯一の理由は、この単純なリストクエリが数秒かかっていたのに対し、他のテーブルに対するより複雑なクエリがほぼ瞬時に発生していたためです。気づいたら、Spring JPAがすべてのBLOBを使用していない場合でもメモリにロードしているため、行が追加されるにつれて、その影響がますます大きくなることがわかりました。それが動作するかどうか、私はTMRW掲載します、私は(下に掲載)春データのためのまともな解決策を見つけたが、私は純粋なJPA注釈であっても、より良いものを持っていると思う
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();

ただし、自分でマッピングを行う必要があります。これらの2つの値だけが本当に必要な場合を除いて、次のような通常のマップされたルックアップを使用する方がおそらく簡単です。

public List<Project> findAll()

おそらく、Springデータのドキュメントも一見の価値があります。


5
ネイティブクエリは必要ありません。それらはJPQLの利点を台無しにするので、それらの使用は避けてください。Atalsの回答を参照してください。
phil294

1
私にとっては、最初の属性(上記FIND_PROJECTS)をvalue属性名で修飾する必要がありました(そのため、これが私のコードである場合、次のように記述しなければなりませんでした@Query(value = FIND_PROJECTS, nativeQuery = true)。など
smeeb

172

Spring Data JPA (doc)のプロジェクションを使用できます。あなたの場合、インターフェースを作成してください:

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

次のメソッドをリポジトリに追加します

List<ProjectIdAndName> findAll();

11
これはクリーンなソリューションです。それはボイラーテンプレートを持つかもしれませんが、インターフェイスはエンティティの内部クラスになることができます。それをかなりきれいにします。
アイスマン2017年

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
(そして、私が検証したところ、「ドキュメント」がインポートされている場合、完全修飾クラス名を指定する必要はありません。それは、私が見つけた唯一のサンプルで行われた方法であるため、そのようにしただけです)
jm0

これは受け入れられる答えになるはずです。それは完全に機能し、本当に必要なフィールドのみを選択します。
Yonatan Wilkof

1
不要なフィールドも含まれていますが、値が「null」の場合、それらのフィールドはメモリを占有しますか?
ガブラー2016年

はい、しかし非常に少ないので、ほとんどの場合、これを回避しようとするのは本当にばかげています-stackoverflow.com/questions/2430655/…これらのフィールドなしで特殊な軽量オブジェクトを作成し、それらを同じように指定する必要があります。テーブル?ORMを使用してそれらの関係に活用する場合、どのIMOが望ましくないのでしょう...ハイパー最適化は、軽量のクエリDSLを使用してDTOに直接マッピングするだけの領域にあり、冗長性は推奨されていません
jm0

2
jm0はインポートされましたが、完全修飾クラス名がないと機能しませんでした。ただし、コンパイルは成功しました。
ハイゼンベルク

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

私の場合、必要のないフィールドなしで(必要なフィールドのみで)別個のエンティティークラスを作成しました。

エンティティを同じテーブルにマップします。すべての列が必要な場合は古いエンティティを使用し、一部の列のみが必要な場合はliteエンティティを使用します。

例えば

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

これは、フェッチする列がわかっている場合に機能します(これは変更されません)。

列を動的に決定する必要がある場合は機能しません。


こんにちはサチン、あなたが上記のようにエンティティを作成するかどうか私には疑いがあります。JPAが実行され、ユーザーの名前でテーブルを作成しようとするとき。どのエンティティを使用するか。
user3364549

3
JPAでテーブルを作成することはありません。dbでテーブルを手動で作成し、JPAを使用してリレーショナルワールドをオブジェクトワールドにマップします。
Sachin Sharma

ここで継承を利用できないのはなぜですか?
deadbug

8

QueryDSLを使用するのが簡単な方法かもしれませんSpring-Dataに付属している。

あなたの質問を使って答えは

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

新しいSpringバージョンでは、次のように実行できます。

ネイティブクエリを使用しない場合、これは次のように実行できます。

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>を返さないのはなぜですか?!
Abdullah Khan

@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.