Spring JPA选择特定列


146

我正在使用Spring JPA执行所有数据库操作。但是我不知道如何从Spring JPA的表中选择特定的列?

例如:
SELECT projectId, projectName FROM projects



JPA不查找特定字段的想法是从表的一行获取一列或所有列的成本(从效率角度而言)相同。
Desorder 2014年

7
@Desorder-成本并不总是相同的。对于简单的原始数据类型来说,这可能没什么大不了的,但之所以出现在此页面上,是因为我注意到一个简单的“列表文档”查询运行缓慢。该实体具有一个BLOB列(用于文件上载/存储需要它),我怀疑它很慢,因为即使列出文档并不需要将BLOB加载到内存中。
jm0

@ jm0据您所记得,有多少个表具有BLOB列?
Desorder

1
@Desorder它只是一个表,但我正在执行“列表”功能(multirow-列出由给定id创建的所有文档)。我注意到此问题的唯一原因是因为此简单的列表查询花费了几秒钟的时间,而对其他表的更复杂的查询几乎立即发生了。一旦意识到,我知道添加行会越来越多,因为Spring JPA会将每个BLOB加载到内存中,甚至不使用它们。我为Spring数据找到了一个不错的解决方案(在下面发布),但是我认为我有一个更好的解决方案,那就是纯JPA注释,如果可行,我会发布tmrw
jm0 2015年

Answers:


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答案。
phil294

1
对我来说,我必须FIND_PROJECTSvalue属性名称来限定第一个属性(在上方)(因此,如果这是我的代码,则必须将其写为@Query(value = FIND_PROJECTS, nativeQuery = true),等等。)
smeeb

172

您可以使用Spring Data JPA (doc)中的投影。在您的情况下,请创建接口:

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

并将以下方法添加到您的存储库

List<ProjectIdAndName> findAll();

11
这是一个干净的解决方案。它可能具有锅炉模板,但接口可以是实体的内部类。使它很干净。
冰人

1
棒极了,请记住不要在您的实体上实现该接口,否则它将无法正常工作
alizelzele

1
投影界面会去哪里?在其自己的文件中还是可以将其包含在返回完整实体属性的公共接口中?
米乔·里佐

8
扩展JpaRepository时,此解决方案不起作用,有人知道解决方法吗?
德国

4
您不能使用findAll(); 因为它将与JPARepositorys方法冲突。您需要使用类似List <ProjectIdAndName> findAllBy();的方法。
Code_Mode

137

我不特别喜欢这种语法(看起来有点hacky ...),但这是我能够找到的最优雅的解决方案(它在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作为构造函数args的构造函数即可。


9
(顺便说一句,我验证了,如果导入了“文档”,则不需要提供完全限定的类名-就是那样,因为这是我唯一能找到的示例中所做的事情)
jm0

这应该是公认的答案。它运行完美,实际上只选择必要的字段。
Yonatan Wilkof

1
还包括不必要的字段,但是值为'null'时,这些字段会占用内存吗?
gabbler '16

是的,但是它是如此之小,以至于在大多数情况下,尝试对此进行工程设计是非常荒谬的-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
您应该Object使用mpr描述的自定义界面替换。完美无瑕的作品
phil294 '17

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

当您知道要提取的列(并且不会改变)时,此方法有效。

如果您需要动态确定列,将无法正常工作。


嗨,萨钦,我不确定我是否会像您上面提到的那样创建实体。当JPA运行时,它将尝试使用用户名创建表。哪个实体将使用。
user3364549

3
永远不要使用JPA创建表,在db中手动创建表,使用JPA将关系世界映射到对象世界。
沙钦·夏尔马

您为什么不能在这里利用继承?
死虫

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

使用较新的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>而不是collection ?!
阿卜杜拉·汗

@AbdullahKhan,因为结果可能并不总是有顺序的。
拉维·桑瓦尔

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

您可以在存储库接口类中应用以下代码。

实体名称表示您的数据库表名称,例如项目。并且List表示Project是您Projects中的Entity类。

@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>)。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.