Spring Data JPA使用JPQL与原生SQL进行查询的操作


Posted in Java/Android onJune 15, 2021

1、使用JPQL语句进行查询

JPQL语言(Java Persistence Query Language)是一种和SQL非常类似的中间性和对象化查询语言,它最终会被编译成针对不同底层数据库的SQL语言,从而屏蔽不同数据库的差异。

JPQL语言通过Query接口封装执行,Query 接口封装了执行数据库查询的相关方法。调用 EntityManager 的 Query、NamedQuery 及 NativeQuery 方法可以获得查询对象,进而可调用Query接口的相关方法来执行查询操作。

JPQL是面向对象进行查询的语言,可以通过自定义的JPQL完成UPDATE和DELETE操作。JPQL不支持使用INSERT。对于UPDATE或DELETE操作,必须使用注解 @Modifying 进行修饰。

【示例】使用JPQL语言进行查询

package com.pjb.jpauserdemo.dao; 
import com.pjb.jpauserdemo.entity.UserInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
 
/**
 * 用户信息数据库访问接口
 * 使用JPQL语言
 * @author pan_junbiao
 **/
@Repository
public interface UserJpqlDao extends JpaRepository<UserInfo,Integer>
{
    /**
     * 根据用户姓名,查询用户信息
     */
    @Query("SELECT u FROM UserInfo u WHERE u.userName = ?1")
    public UserInfo getUserInfoByName(String name);
 
    /**
     * 根据用户姓名,模糊查询用户列表
     */
    @Query("SELECT u FROM UserInfo u WHERE u.userName like %:name%")
    public List<UserInfo> getUserListByName(String name);
 
    /**
     * 修改用户姓名
     */
    @Modifying
    @Query("UPDATE UserInfo u SET u.userName = :name WHERE u.userId = :id")
    public int updateUserName(@Param("id")int userId, @Param("name")String userName);
}

2、使用原生SQL语句进行查询

在使用原生SQL查询时,也使用@Query注解。此时,nativeQuery参数需要设置为true。

【示例】使用原生SQL语句进行查询

package com.pjb.jpauserdemo.dao;
import com.pjb.jpauserdemo.entity.UserInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
 
/**
 * 用户信息数据库访问接口测试类
 * 使用原生SQL语言
 * @author pan_junbiao
 **/
@Repository
public interface UserSqlDao extends JpaRepository<UserInfo,Integer>
{
    /**
     * 根据用户ID,获取用户信息
     */
    @Query(value = "SELECT * FROM tb_user WHERE user_id = :id",nativeQuery = true)
    public UserInfo getUserById(@Param("id")int userId);
 
    /**
     * 根据用户姓名,模糊查询用户列表
     */
    @Query(value = "SELECT * FROM tb_user WHERE user_name LIKE %:userName%",nativeQuery = true)
    public List<UserInfo> getUserListByName(@Param("userName")String userName);
 
    /**
     * 修改用户姓名
     */
    @Modifying
    @Query(value = "UPDATE tb_user SET user_name = :name WHERE user_id = :id",nativeQuery = true)
    public int updateUserName(@Param("id")int userId, @Param("name")String userName);
}

可以看到,@Query与@Modifying这两个注解一起声明,可以定义个性化更新操作。

Spring data jpa@query使用原生SQl,需要注意的坑

根据代码来解说:

@Query(value = "select bill.id_ as id, bill.created_date as date, bill.no, lawyer_case .case_no as caseNo, " +
            "lawyer_case .case_name as caseName, customer.no as customerNo, customer.cn_name as customerName, " +
            "bill.total_expense_after_tax, bill.collected_money, bill.book_ticket_amount, bill.version " +
            "e1.name as creator, bill.status" +
            "from bill " +
            "left join lawyer_case on lawyer_case .case_no=bill.case_no " +
            "left join customer on customer.no=bill.customer_no " +
            "left join employee e1 on e1.id_=bill.creator " +
            "where IF (?1!='', customer_no=?1, 1=1) " +
            "and   IF (?2!='', case_no=?2, 1=1) " +
            "and   IF (?3!='', status=?3, 1=1) " +
            "and   IF (?4!='', creator'%',?4,'%')), 1=1) " +
            "and   create_by=?5 " +
            "ORDER BY ?#{#pageable} ",
            countQuery = "select count(*) " +
                    "from bill " +
                    "left join lawyer_case on lawyer_case .case_no=bill.case_no " +
                    "left join customer on customer.no=bill.customer_no " +
                    "left join employee e1 on e1.id_=bill.creator " +
                    "where IF (?1!='', customer_no=?1, 1=1) " +
                    "and   IF (?2!='', case_no=?2, 1=1) " +
                    "and   IF (?3!='', status=?3, 1=1) " +
                    "and   IF (?4!='', creator'%',?4,'%')), 1=1) " +
                    "and   create_by=?5 "+
                    "ORDER BY ?#{#pageable} ",
            nativeQuery = true)
    Page<Object[]> findAllBill(String customerNo, String caseNo, Integer status, String creator,
                               String createBy, Pageable pageable);

需要注意的方法有以下几点:

1、From 不支持重命名.

2、返回的是一个page<Object[]>,数组中只保存了数据,没有对应的key,只能根据返回数据的顺序,依次注入到DTO中。

3、对于使用分页,需要:“ORDER BY ?#{#pageable}”,可以直接传入一个pageable对象,会自动解析。

4、注意格式问题,很多时候就是换行的时候,没有空格。

5、仔细对应数据库中表字段,很多时候报某个字段找不到,就是因为字段名写错,和数据库中对应不上。

6、这是解决使用微服务,大量的数据都需要远程调用,会降低程序的性能。

7、使用Pageabel作为参数的时候,去进行分页。刚开始的时候,觉得还是一个可行的办法,但是得注意的时候,当需要排序的时候,是无法加入sort字段的。 会一直报错left*。

8、针对7的解决方案,把原生SQL的数据查询和countQuery分成两个查询方法。得到count,然后进行判断,若是等于0,则直接返回空集合;反之,则取获取数据。 需要自己进行分页计算,传入正确的pageNumber和pageSize。 大部分系统都是按照修改时间进行降序排序。 所以,order by可以写死。然后pageNumber和pageSize动态传入。 pageNumber的算法= (pageNumber - 1) * pageSize, 前提是PageNumber是从1开始,若0,则pageNumber=pageNumber * PageSize; 这样就可以保证数据的正确。

/**
* pageInfos: 转换之后的数据。
* pageable:传入的pageable.
* totalPage: 第一条SQL算好的返回值。
* 这样就可以统一的返回各种pageDTO。
*/
private Page<T> convertForPage(List<T> pageInfos, Pageable pageable, Integer totalPage) {
        return new PageImpl<>(pageInfos, pageable, totalPage);
    }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Java/Android 相关文章推荐
新手入门Jvm-- JVM对象创建与内存分配机制
Jun 18 Java/Android
IDEA使用SpringAssistant插件创建SpringCloud项目
Jun 23 Java/Android
springboot临时文件存储目录配置方式
Jul 01 Java/Android
深入解读Java三大集合之map list set的用法
Nov 11 Java/Android
SpringDataJPA在Entity中常用的注解介绍
Dec 06 Java/Android
SpringBoot2零基础到精通之数据库专项精讲
Mar 22 Java/Android
Spring Boot 底层原理基础深度解析
Apr 03 Java/Android
Elasticsearch Recovery 详细介绍
Apr 19 Java/Android
SpringBoot全局异常处理方案分享
May 25 Java/Android
spring IOC容器的Bean管理XML自动装配过程
May 30 Java/Android
Android开发手册自定义Switch开关按钮控件
Jun 10 Java/Android
Mybatis-plus配置分页插件返回统一结果集
Jun 21 Java/Android
解决Maven项目中 Invalid bound statement 无效的绑定问题
解析Java异步之call future
分析Netty直接内存原理及应用
Jun 14 #Java/Android
详解JAVA中的OPTIONAL
解析Java中的static关键字
Java实现斗地主之洗牌发牌
MybatisPlus代码生成器的使用方法详解
You might like
第4章 数据处理-php数组的处理-郑阿奇
2011/07/04 PHP
php中判断字符串是否全是中文或含有中文的实现代码
2011/09/16 PHP
phpstorm配置Xdebug进行调试PHP教程
2014/12/01 PHP
php入门教程之Zend Studio设置与开发实例
2016/09/09 PHP
[推荐]javascript 面向对象技术基础教程
2009/03/03 Javascript
jquery 图片截取工具jquery.imagecropper.js
2010/04/09 Javascript
《JavaScript DOM 编程艺术》读书笔记之JavaScript 图片库
2015/01/09 Javascript
jQuery判断指定id的对象是否存在的方法
2015/05/22 Javascript
jQuery无刷新分页完整实例代码
2015/10/27 Javascript
常用的几个JQuery代码片段
2017/03/13 Javascript
jQuery实现Select下拉列表进行状态选择功能
2017/03/30 jQuery
从零开始学习Node.js系列教程五:服务器监听方法示例
2017/04/13 Javascript
JavaScript实现各种排序的代码详解
2017/08/28 Javascript
详解小程序输入框闪烁及重影BUG解决方案
2018/08/31 Javascript
ES6 更易于继承的类语法的使用
2019/02/11 Javascript
layui 选择列表,打勾,点击确定返回数据的例子
2019/09/02 Javascript
Vue获取页面元素的相对位置的方法示例
2020/02/05 Javascript
Vue点击切换Class变化,实现Active当前样式操作
2020/07/17 Javascript
如何实现小程序与小程序之间的跳转
2020/11/04 Javascript
[47:26]完美世界DOTA2联赛 LBZS vs Forest 第二场 11.07
2020/11/09 DOTA
详解MySQL数据类型int(M)中M的含义
2016/11/20 Python
Python多线程threading和multiprocessing模块实例解析
2018/01/29 Python
Python实现输出某区间范围内全部素数的方法
2018/05/02 Python
django中静态文件配置static的方法
2018/05/20 Python
PIL对上传到Django的图片进行处理并保存的实例
2019/08/07 Python
python 普通克里金(Kriging)法的实现
2019/12/19 Python
pytorch中的自定义数据处理详解
2020/01/06 Python
html+js 实现markdown编辑器效果
2019/10/23 HTML / CSS
中国综合性网上购物商城:当当(网上卖书起家)
2016/11/16 全球购物
绿色美容,有机护肤品和化妆品:Safe & Chic
2018/10/29 全球购物
哄娃神器4moms商店:美国婴童用品品牌
2019/03/07 全球购物
美国室内和室外装饰花盆购物网站:ePlanters
2019/03/22 全球购物
什么是设计模式
2012/06/17 面试题
股东协议书
2014/04/14 职场文书
《谁的本领大》教后反思
2014/04/25 职场文书
导游词之香港-太平山顶
2019/10/18 职场文书