我的学习系列之Spring Data JPA】三、Spring Data JPA之JPQL的应用

Spring Data JPA之JPQL的应用

     JPQL全称Java Persistence Query Language。
     是一种Java持久化查询语言,一种可移植的查询语言,旨在以面向对象表达式语言的表达式,将SQL语法和简单查询语义绑定在一起·使用这种语言编写的查询是可移植的,可以被编译成所有主流数据库服务器上的SQL。

     其特征与原生SQL语句类似,并且完全面向对象,通过类名和属性访问,而不是表名和表的属性。
     SQL是面向对象关系数据库,他操作的是数据表和数据列。
这就是两者的区别之处。
     JPQL所提供的查询语法主要分为三类:
     1.查询用的 SELECT 语法
     2.更新用的 UPDATE 语法
     3.删除用的 DELETE 语法
     学习过Hibernate的同鞋应该知道,在Hibernate中,有一个HQL,其实JPQL和HQL是极其相似的。


JPQL——查询数据

现在就来演示JPQL的应用。
从官网上可以看到应用的示例,使用的是占位符,在占位符后面加上的是参数的位置,第1个参数位置就是1,通过这种方式,来绑定请求方法中的参数:

 @Query("  from User as u where u.username = ?1")
    List<User> findByUsername(String username);

     先在UserDao中,添加一个以用户名和密码查询的接口方法,使用@Query注解来进行JPQL的应用:
     在上面的示例中,可以看到@Query注解的value值有两种写法,一个是有select,一个是简写,使用哪一种都是可以的。
     需要注意的就是,在JPQL中有两个占位符,分别指定应用了方法上的两个参数,这里是故意把参数的顺序放反的,目的是让大家进一步认识到,JPQL中占位符的取值是如何处理的,所以,这里一定要注意了,这是JQPL的应用规则。(在SpringBoot1.5X版本中,占位符可以不加参数位置索引的,但必须与占位符对应,从2.x开始去除这种应用方式。)
     Dao编写好了,现在就来完成请求的映射。
     定义一个测试类,在类中新建一个测试方法,代码如下:

@SpringBootTest
class SpringbootDataJpaApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    @Test
    void queryUsers(){
        List<User> users = userDao.findByUsername("张三");
        for(User user1 :users){
            System.out.println(user1.getUsername());
        }
    }
}
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.birthday as birthday3_0_, user0_.password as password4_0_, user0_.sex as sex5_0_, user0_.username as username6_0_ from user user0_ where user0_.username=?
张三

要注意了,查询所有属性的数据时,select是可以省略不写的

JPQL——模糊排序查询

    在UserDao中,添加方法与JPQL,要求是:根据用户名进行模糊查询,以主键进行降序排序。

//添加方法与JPQL,要求是:根据用户名进行模糊查询,以主键进行降序排序
@Query("select  u from User as u where u.username like %?1% order by u.id desc ")
List<User> findUserLikeByUsernameSortUid(String username);

@Query("select u from User as u where u.username like  %:name% order by u.id desc ")
List<User> findUserLikeByUsernameSortUid2(@Param("name") String username);

@Query("select  u from User as  u where u.username like %:#{#username}% order by u.id desc ")
List<User> findUserLikeByUsernameSortUid3(@Param("user") User user);

     在实际的开发中,有的时候会接收很多的参数,如果都分别在方法上逐一定义出来,那么方法体看上去会非常的臃肿,这个时候就需要把参数进行封装了,也就是说,参数是一个实体类,那么从实体类中获取指定的属性数据,就需要注意了,在JPQL中,要获取对象的属性值,就要使用这样的方式==> :#{#对象名.属性名}
     在进行操作之前,还需要给参数使用@Param来对参数进行指定别名,这样才可以使用参数读取的方式来实现数据的获取,参数读取的方式【:参数别名】
     这里一定要注意了,语法规则是什么样的。
     同时,在模糊查询中,匹配符可以直接给出,使用的是百分号,当然,也可以在JPQL不使用,但在参数中就必须要给了,否则模糊查询有可能得不到数据。
     需要注意的是,在官网中,有这样的一个应用表达式:#{#entityName}

在这里插入图片描述
     这个表达式的功能是引用@Entity标注的实体类,比如我们的User类。这是动态获取类的方式,也可以理解为一个变量。在使用的过程中有一个注意事项,那么就是#{#entityName}和:#{#对象名.属性名}不能同时出现,要么使用#{#entityName},配合?占位符,要么直接给出类名,使用:#{#对象名.属性名}。
     也就是说,使用#{#entityName}的时候,如果执行的方法中参数是对象的,无法获取对象中的属性值,两者一起使用会报错,因为都是变量,在不确定的情况下,编译无法通过。
    接下来在测试类中,添加查询的方法:

@Test
void queryUsersLike(){
   // List<User> users = userDao.findUserLikeByUsernameSortUid("三");
    List<User> users = userDao.findUserLikeByUsernameSortUid2("张");
    for(User user1 :users){
        System.out.println(user1.getUsername());
    }
}

JPQL——聚合函数应用

     在查询数据库时,还需要进行统计之类的算术操作,比如求和,求平均,统计数量等等。在JPQL中,也是可以完成这些操作的。
     先来在User类中添加一个age属性,使用这个属性来进行测试:

@JsonIgnoreProperties(value = "hibernateLazyInitializer")//忽略属性
@Entity //标注这是一个数据库映射的实体类
public class User {
    @Id //标注这是一个主键属性
    //主键生成策略
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(length = 16)    //普通字段,长度为varchar(16),此时列名就是属性名
    private String username;
    private String password;
    @Column(length = 2)  //指定字段名
    private String sex;
    private Integer age;
    private Date birthday;
    //省略get/set方法
 }

     这个属性是新增的,而我们在配置数据表生成策略的时候使用的是update,所以,当服务器启动的时候,就会帮助我们新增一个age字段到数据表中。
     现在来定义UserDao方法:

@Query("select avg(u.age) as 平均分,sum(u.age) as 总年龄,count(u.id) as 数据量 from User as u")
    Map<String,Object> getMathData();
    

    在使用算术运算时,可以同时查询多个数据,这也是投影查询中的一种,默认的情况下,查询返回的是一个List集合,在集合中,是一个Object[]数组,我们可以使用Map来进行接收,这样就不会这么麻烦。
    定义一个测试方法:

   @Test
    void queryMath(){
        Map<String,Object> data = userDao.getMathData();
        data.forEach((k,v)-> System.out.println(k + "==" + v));
    }

测试结果:

Hibernate: select avg(user0_.age) as col_0_0_, sum(user0_.age) as col_1_0_, count(user0_.id) as col_2_0_ from user user0_
平均分==44.3333
总年龄==133
数据量==4

JPQL——属性投影查询

    好了,有了聚合查询的示例,再来使用属性投影查询就方便的多了,现在来查询所有数据的username和age、sex三个数据,其他的咱不要。
    定义出相应在的方法:

@Query("select u.username as username,u.password as password,u.sex as sex from User as u")
List<Map<String,Object>> findUsers();

@Query("select new User(username,password,sex) from User")
List<User> findUsers2();

    投影查询,可以使用两种形式,一个是使用map封装,一个是使用实体类封装。要使用类封装,就必须要有相应的构造方法。

public User(String username,String password,String sex) {
    this.username = username;
    this.password = password;
    this.sex = sex;
}

    通过构造方法,传递的数据不需要as取别名,只需要把查询到的数据进行传递就OK了,3种写法都是可以实现数据的传递。
    如果是map接收,那么就必须使用as取别名,否则,key都是null。
    Dao接口方法和请求方法都定义完成,就可以进行测试了,定义测试方法

@Test
void queryUsers2() {
    List<Map<String, Object>> users = userDao.findUsers();
    users.forEach(map -> {
        map.forEach((k,v)-> System.out.println(k + "==" + v));
    });
}

@Test
void queryUsers3() {
    List<User> users = userDao.findUsers2();
    users.forEach(System.out::println);
}

JSON的序列化问题

    如果把数据进行JSON化,那么就需要注意了。
    第1种方法,就是使用jackson工具提供的注解@JsonInclude,将它标记放在属性上,如果该属性为NULL则不参与序列化,如果放在类上边,那对这个类的全部属性起作用。
它的值有:
1.Include.Include.ALWAYS 默认
2.Include.NON_DEFAULT 属性为默认值不序列化
3.Include.NON_EMPTY 属性为 空("") 或者为 NULL 都不序列化
4.Include.NON_NULL 属性为NULL 不序列化
我们希望User类中所有的属性,只要是null值就不参与序列化:

这种方法是指定jackson工具JSON序列化的方式,在启动类中,注入一个jackson工具中序列化对象ObjectMapper对象:

@Bean
public ObjectMapper objectMapper(){
    ObjectMapper objectMapper = new ObjectMapper();
    //objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
        @Override
        public void serialize(Object paramT, JsonGenerator paramJsonGenerator,
                              SerializerProvider paramSerializerProvider) throws IOException {
            //设置返回null转为 空字符串""
            paramJsonGenerator.writeString("");
        }
    });
    return objectMapper;
}

    可以看到,除了使用注解来指定外,还可以使用方法来指定,就是通过ObjectMapper对象来完成这个功能的。
    这样是全局的应用方式,现在把User上使用的@JsonInclude注解去掉,重启服务器再测试:

在这里插入图片描述

搞定

JPQL——修改、删除数据

@Modifying //使用JPQL来进行测试或操作
@Transactional //事务管理
@Query("update User set password = ?1 where  id = ?2")
int updateUserByUid(String password,Integer uid);

@Modifying          //使用JPQL来进行删或改操作
@Transactional    //事务管理
@Query("delete from User where id = ?1")
int deleteUserByUid(Integer uid);

    在UserDao中定义的接口方法上,使用了@Modifying注解,这是因为JPQL默认的情况下,是用来做查询操作的,如果要使用到修改和删除的操作上,就必须要添加上@Modifying注解,告诉程序,这是一个修改或者删除的操作,否则就会出现如下所示的异常:

org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML 
operations [UPDATE com.edu118.entity.User SET username = :__$synthetic$__1,password = 
:__$synthetic$__2 WHERE uid = :__$synthetic$__3]

    而要实现数据的修改或者删除操作,就要使用到事务,默认是没有事务的,所以,要使用上@Transactional注解,意思就是当前的方法添加事务的应用,如果没有添加,就会出现如下所示的异常:

javax.persistence.TransactionRequiredException: Executing an update/delete query

    因此,出现这两个异常的情况,就应该知道哪里出现了问题,非常容易解决。
    方法定义完成之后,就可以进行测试了,定义测试方法:

@Test
void deleteData(){
    userDao.deleteUserByUid(2);
}

@Test
void updateData() {
    userDao.updateUserByUid("abaaaa", 3);
}

测试的结果省略!

方法名映射查询

在Spring Data JPA的应用中,还提供了一种非常方便的操作方式,那就是通过方法名来确定查询的功能。
官网参考地址:


https://docs.spring.io/spring-data/jpa/docs/2.3.0.RELEASE/reference/html/#jpa.query-methods


那么,什么是以方法名来确定查询功能呢???
比如现在UserDao中有一个接口方法:

//@Query("select u from User as u where u.username like %?1% order by u.uid desc")
List<User> findUsersByUsernameIsLikeOrderByUidDesc(String username);

//@Query("select u from User as u where u.username = ?1 and u.password = ?2")
List<User> findUsersByUsernameAndPassword(String username,String password);

现在,在JPQLController中,添加一个请求方法:

@Test
void queryUsers4() {
    //List<User> users = userDao.findUsersByUsernameAndPassword("信盈达科技", "123456");
    List<User> users = userDao.findUsersByUsernameIsLikeOrderByUidDesc("%达%");
    users.forEach(System.out::println);
}

测试结果省略。这就是方法名映射查询操作功能。
    在做一些简单的常规的操作时,就可能使用这种方式,通过方法名来指定查询的条件,这是Spring Data JPA提供的一个快捷应用方式,这种应用方式,也是有应用规则的,那就是方法名的定义,可以在官网上得到参考:

https://docs.spring.io/spring-data/jpa/docs/2.3.0.RELEASE/reference/html/#jpa.query-methods.query-creation

    现在就来看看,方法定义的规则:
    方法名必须是以findBy或者是find + 类名 + By开头。
    比如示例中使用的是findByUsernameAndPassword(),也可以使用findUserByUsernameAndPassword()。
    完整的方法名规则:
    findBy | find类名By + 属性名(首字母大写) + 查询方式 + 条件连接符 + 属性名 + 查询方式…
    每一个属性就是一个查询的条件,在实体类中,属性名首字母是小写的,在这里要变成大写,属性与属性之间,使用条件连接符拼接,这些连接符也是关键字。
    条件连接符有AND和OR,还有比较的,还有分组、模糊查询等等查询方式,根据你的需求来使用就可以了。
    方法名的定义方式有了,还需要注意另外一点,那就是参数的定义,使用方法名映射的方式,参数的定义一定要与方法名中的条件完全一致,不参是参数的数据类型,还是参数的顺序,都必须要一样,否则有可能查询数据失败。
这种方式,只做查询。

サクラチル | AO FUJIMORI #Pixiv
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值