Spring Data Jpa相关——JPA基本的查询和修改方法

方法命名查询

  • 概述

根据方法的名字就能执行相对应的查询语句,实现查询。只需要按照Spring Data JPA提供的方法命名规则定义方法的 名称,就可以完成查询工作。Spring Data JPA在程序执行的时候会根据方法名称进行解析,并自动生成查询语句进行查询 按照Spring Data JPA 定义的规则,查询方法以 findBy 开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是: 件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。

  • 关键字

Keyword Sample JPQL
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is,Equals findByFirstname, findByFirstnameIs, findByFirstnameEquals … where x.firstname = ?1
Between findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age <= ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
IsNull findByAgeIsNull … where x.age is null
Like findByFirstnameLike … where x.firstname like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %)
Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection ages) … where x.age not in ?1
True findByActiveTrue() … where x.active = true
False findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)

方法命名查询实际案例

  • 实体类

@Entity
@Table(name = "tb_users")
@Setter
@Getter
@ToString
@NoArgsConstructor
public class User {

    @Id
    @GenericGenerator(name = "idGenerator", strategy = "uuid")
    @GeneratedValue(generator = "idGenerator")
    private String id;

    @Column(name = "username", unique = true, nullable = false, length = 64)
    private String username;

    @Column(name = "password", nullable = false, length = 64)
    private String password;

    @Column(name = "email", length = 64)
    private String email;
}
  • repository接口

@Repository
public interface UserRepository extends JpaRepository<User, String> {

    //注意:方法的名称必须要遵循驼峰式命名规则。xxxBy(关键字)+属性名称(首字母大写)+查询条件(首字母大写)

    /*** 根据用户名精确查询 *
     *  @param username *
     *  @return User
     *  */
    User findByUsername(String username);

    /*** 根据用户名称模糊查询
     * @param username
     * @return User
     */
    List<User> findByUsernameLike(String username);
}
  • 测试类

@SpringBootTest
public class UserTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    public void test1(){
        User user = userRepository.findByUsername("zhangsan");
        System.out.println(user);
    }

    @Test
    public void test2(){
        List<User> userList = userRepository.findByUsernameLike("%z%");
        userList.forEach(System.out::println);
    }

    @Test
    public void test3(){
        List<User> userList = userRepository.findByUsernameLikeAndEmailLike("%z%", "%@qq.com");
        userList.forEach(System.out::println);
    }
}

注解式查询

  • 概述

使用Spring Data JPA提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条 件,这时就可以使用@Query注解,结合JPQL的语句方式完成查询。

  • 使用@query注解

(1)使用JPQL查询

使用声明式 JPQL 查询有个好处,就是启动的时候就知道你的语法正确不正确。JPQL一般只需要关心 @Query 里面的 value 和 nativeQuery

的值

案例:

repository接口

@Repository
public interface UserRepository extends JpaRepository<User, String> {

    //单条件查询
    @Query("from User where username = ?1")
    User gainUserByUsername(String username);

    //多条件查询
    @Query("from User where username like %?1% and email like %?2")
    List<User> gainUserListByUsernameAndEmail(String username, String email);
}

在JPQL中,用数字表示对应的位置参数,数字1对应第一个参数,在数字前要加一个?号表示这个数字为对应位置的参数。

测试方法

@Test
public void test4(){
        User user = userRepository.gainUserByUsername("zhangsan");
        System.out.println(user.getUsername());
        List<User> userList = userRepository.gainUserListByUsernameAndEmail("zhang", ".com");
        userList.forEach(System.out::println);
    }

(2)使用原生的sql语句查询

将@query注解中的nativeQuery设置为true

//使用原生的sql语句查询
    @Query(value = "select password from tb_users where email = ?1", nativeQuery = true)
    String gainPasswdByEmail(String email);

参数位置同样用?加数字来表示

  • @param注解的使用

默认情况下,参数是通过顺序绑定在查询语句上的,这使得查询方法对参数位置的重构容易出错。为了解决这个问题,可以使用 @Param 注解指定方法参数的具体名称,通过绑定的参数名字做查询条件,这样不需要关心参数的顺序,推荐这种做法,比较利于代码重构

查询方法

 /*** 命名参数查询 * 
     * @param username * 
     * @param address * 
     * @return */ 
@Query("from User where userName like :username% and address like %:address%")
    List<User> findUserListByParam(@Param("username") String username,@Param("address") String address);

使用param注解之后,在查询语句中使用':'+参数名的方式,来表示插入语句中的参数

测试方法

	@Test
    public void test6(){
        List<User> userList = userRepository.findUserListByParam("z", "@");
        userList.forEach(System.out::println);
    }
  • @Modifying注解的使用

修改或者删除的方法需要加上@Modifying注解,否则会报错

修改方法

@Modifying
@Query("update User  set username =?1 where id =?2")
int updateUser(String username,String id);

删除方法

@Modifying
@Query("delete from User where id =?1")
int deleteUser(String id);

测试

@Test
@Transactional
@Rollback(false)
public void test7(){
        if(userRepository.findById("40281d817c6d6def017c6d6dfb0a0000").isPresent()){
            System.out.println(userRepository.findById("40281d817c6d6def017c6d6dfb0a0000").get());
            if(userRepository.updateUser("hahaha", "40281d817c6d6def017c6d6dfb0a0000") > 0)
                System.out.println("用户名修改成功");
        }

    }

@Test
@Transactional
@Rollback(false)
public void test8(){
        if(userRepository.deleteUser("40281d817c6deb53017c6deb60b20AEEC") > 0)
            System.out.println("删除成功");
        else
            System.out.println("删除失败,用户可能不存在");
}

测试的时候需要开启事务不然会报错,然后要把回滚关闭,不然的话执行完方法就会自动回滚


  • @query注解查询的优点

  1. 可以灵活快速的使用 JPQL 和 SQL

  2. 对返回的结果和字段可以自定义

  3. 支持连表查询和对象关联查询,可以组合出来复杂的 SQL 或者 JPQL

  4. 可以很好的表达你的查询思路

  5. 灵活性非常强,快捷方便

  • @query注解查询的缺点

  1. 不支持动态查询条件,参数个数如果是不固定的不支持

  2. 若将返回结果用 Map 或者 Object[] 数组接收结果,会导致调用此方法的开发人员不知道返回结果里面到底有些什么数据