使用参数属性的Spring数据JPA查询

135

如何最简单地声明一个使用输入参数属性作为查询参数的Spring data JPA查询?

例如,假设我有一个实体类:

public class Person {
    @Id
    private long id;

    @Column
    private String forename;

    @Column
    private String surname;
}

还有另一个类:

public class Name {
    private String forename;
    private String surname;

    [constructor and getters]
}

如果我想编写一个Spring数据存储库,可以按照以下方式进行:

public interface PersonRepository extends CrudRepository<Person, Long> {
    @Query("select p from Person p where p.forename = ?1.forename and p.surname = ?1.surname")
    findByName(Name name);
}

...但是Spring Data / JPA不喜欢我在?1参数上指定属性名称。

有什么更简洁的替代方案吗?


必须是动态的吗?为什么不能在from语句后面添加“Name”表? - Tolio
1
Name 不一定是实体。 - Kkkev
10个回答

265
这个链接会帮助你:支持使用SpEL表达式的Spring Data JPA M1。类似的示例如下:
@Query("select u from User u where u.firstname = :#{#customer.firstname}")
List<User> findUsersByCustomersFirstname(@Param("customer") Customer customer);

更多详细信息请参见:Spring Data JPA中的SpEL支持@Query定义


5
这是Spring Data JPA 1.7.2+,需要使用Spring 4。 - chrismarx
客户似乎必须在这里成为一个实体,对普通Java对象的评估对我来说行不通。 - Schaka
3
我可以确认我有同样的问题,即使使用Spring Boot 1.4/Spring 4/Spring Data JPA 1.10。 - chrismarx
太棒了!即使在文档中也找不到这个。谢谢兄弟! - de.la.ru
1
客户可以是一个DTO,这将有效,已经在Spring Boot 2.2.0.RELEASE中进行了测试。 - dom
显示剩余2条评论

35

定义以下签名的查询方法。

@Query(select p from Person p where p.forename = :forename and p.surname = :surname)
User findByForenameAndSurname(@Param("surname") String lastname,
                             @Param("forename") String firstname);
}

如需了解更多细节,请查看Spring Data JPA 参考文档


6
那并没有回答问题。关键点是我想使用输入参数的属性作为查询参数,而不是单独的输入参数。 - Kkkev
抱歉,我误读了您的查询。 我认为Spring Data JPA不提供支持(甚至可能连JDBC也不提供;我可能在这里错了)。作为另一种选择(除了自定义存储库方法),您可以编写一个服务包装器方法,将Person对象作为参数传递,并将相关参数传递给存储库方法。这种方法的优点是您所期望的签名(传递Person对象)保持不变,并且您使用Spring Data JPA提供的实现。 - CuriosMind...

12

你也可以使用接口默认方法来解决它:

 @Query(select p from Person p where p.forename = :forename and p.surname = :surname)
User findByForenameAndSurname(@Param("surname") String lastname,
                         @Param("forename") String firstname);

default User findByName(Name name) {
  return findByForenameAndSurname(name.getLastname(), name.getFirstname());
}

当然,您仍然需要公开显示实际的存储库功能...


11

无法做到您想要的。您必须创建两个参数,并分别绑定它们:

select p from Person p where p.forename = :forename and p.surname = :surname
...
query.setParameter("forename", name.getForename());
query.setParameter("surname", name.getSurname());

1
据推测,这需要按照http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/#repositories.single-repository-behaviour中的自定义存储库方法来实现。 - Kkkev
1
我知道JPA的工作原理,但我不知道spring-data为你做了什么以及你必须自己做什么。抱歉。 - JB Nizet

10
您可以尝试类似以下的内容:
public interface PersonRepository extends CrudRepository<Person, Long> {
       @Query("select p from Person AS p"
       + " ,Name AS n"  
       + " where p.forename = n.forename "
       + " and p.surname = n.surname"
       + " and n = :name")
       Set<Person>findByName(@Param("name") Name name);
    }

1
@Kkkev 没错,你需要将 Name 设为实体以解决此问题。 - Casi

3

如果我们使用JpaRepository,它将内部创建查询语句。

示例

findByLastnameAndFirstname(String lastname,String firstname)

findByLastnameOrFirstname(String lastname,String firstname)

findByStartDateBetween(Date date1,Date2)

findById(int id)

注意

如果我们需要复杂的查询,那么我们需要编写手动查询语句。

@Query("SELECT salesOrder FROM SalesOrder salesOrder WHERE salesOrder.clientId=:clientId AND salesOrder.driver_username=:driver_username AND salesOrder.date>=:fdate AND salesOrder.date<=:tdate ")
 @Transactional(readOnly=true)
 List<SalesOrder> findAllSalesByDriver(@Param("clientId")Integer clientId, @Param("driver_username")String driver_username, @Param("fdate") Date fDate, @Param("tdate") Date tdate);

3
Spring Data JPA 的简单之处在于它尝试从存储库中的函数名称进行解释,而不需要指定任何额外的 @Query 或 @Param 注释。 如果您正在提供完整的名称,请尝试将其拆分为名字和姓氏,然后使用类似以下的内容 -
HotelEntity findByName(String name);

我的HotelEntity包含字段name,因此JPA尝试自行解释以推断我正在尝试查询的字段的名称,并在内部创建随后的查询。JPA文档中还有更多证据-enter image description here
更多细节-这里

1
你也在使用 @Service 吗? 如果是的话,那么你可以将你的 PersonRepository@Autowired 注入到 @Service 中,然后在服务中调用 Name 类,并使用 @CuriosMind 提出的形式。
@Query(select p from Person p where p.forename = :forename and p.surname = :surname)
User findByForenameAndSurname(@Param("surname") String lastname,
                         @Param("forename") String firstname);
}

当从服务中的存储库调用该方法时,您可以传递这些参数。


-3
    for using this, you can create a Repository for example this one:
    Member findByEmail(String email);

    List<Member> findByDate(Date date);
    // custom query example and return a member
   @Query("select m from Member m where m.username = :username and m.password=:password")
        Member findByUsernameAndPassword(@Param("username") String username, @Param("password") String password);

-5
@Autowired
private EntityManager entityManager;

@RequestMapping("/authors/{fname}/{lname}")
    public List actionAutherMulti(@PathVariable("fname") String fname, @PathVariable("lname") String lname) {
        return entityManager.createQuery("select A from Auther A WHERE A.firstName = ?1 AND A.lastName=?2")
                .setParameter(1, fname)
                .setParameter(2, lname)
                .getResultList();
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接