springboot学习-分页/排序/多表查询的例子
最近喜欢上了springboot,真是个好的脚手架。今天继续学习分页/排序/多表查询等复杂功能。按步骤记录如下. 按步骤做的发现不可用,最终还是用的jdbctemplate解决。这也是一次经验。总计在最后。
1.maven依赖
首先从https://start.spring.io/ 选择需要的maven依赖
2. 配置好H2数据库
spring.h2.console.enabled=true -- /h2-console 就可以访问数据库了。spring.datasource.name=user-profile -- 定义数据库名,或者说数据源名称。 spring.datasource.generate-unique-name=false --trueh会自动生成随机名称,false用定义的name
3. 数据库表创建并插入数据
约定创建表的脚本在src/main/resroucs/schema.sql
CREATE TABLE users (id BIGINT AUTO_INCREMENT PRIMARY KEY,username VARCHAR(255) NOT NULL,profile_id BIGINT
);CREATE TABLE profiles (id BIGINT AUTO_INCREMENT PRIMARY KEY,address VARCHAR(255),phone_number VARCHAR(255)
);
添加数据是data.sql,要实现分页,数据要多一些,插入8条数据,准备5条分一页。
INSERT INTO profiles (address, phone_number) VALUES ('123 Main St', '555-1234');
INSERT INTO users (username, profile_id) VALUES ('john_doe', 1);
INSERT INTO profiles (address, phone_number) VALUES ('222 qian St', '555-2222');
INSERT INTO users (username, profile_id) VALUES ('qianer', 2);
INSERT INTO profiles (address, phone_number) VALUES ('333 zhang St', '555-3333');
INSERT INTO users (username, profile_id) VALUES ('zhangsan', 3);
INSERT INTO profiles (address, phone_number) VALUES ('444 li St', '555-4444');
INSERT INTO users (username, profile_id) VALUES ('lisi', 4);
INSERT INTO profiles (address, phone_number) VALUES ('555 wang St', '555-5555');
INSERT INTO users (username, profile_id) VALUES ('wangwu', 5);
INSERT INTO profiles (address, phone_number) VALUES ('666 zhao St', '555-6666');
INSERT INTO users (username, profile_id) VALUES ('zhaoliu', 6);
INSERT INTO profiles (address, phone_number) VALUES ('777 tian St', '555-7777');
INSERT INTO users (username, profile_id) VALUES ('tianqi', 7);
INSERT INTO profiles (address, phone_number) VALUES ('888 tian St', '555-8888');
INSERT INTO users (username, profile_id) VALUES ('gongba', 8);
4. 编写JAVA Entities
用户和档案个一个class
// User.java
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;@Table("users")
public class User {@Idprivate Long id;private String username;private Long profileId; // Reference to Profile// getters and setters
}// Profile.java
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;@Table("profiles")
public class Profile {@Idprivate Long id;private String address;private String phoneNumber;// getters and setters
}
5. 创建一个DTO类用于jion result
DTO的意思是Data Transfer Object, 用于存放join的查询结果。
public class UserProfileDTO {private String username;private String address;private String phoneNumber;// constructor, getters, and setters
}
6. 创建一个Repository类,其中查询是定制方法
使用@Query定制查询,并增加pagination and sorting:
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Page;
import java.util.List;public interface UserRepository extends CrudRepository<User, Long> {@Query("SELECT u.username, p.address, p.phoneNumber " +"FROM users u JOIN profiles p ON u.profileId = p.id " +"ORDER BY p.id")Page<UserProfileDTO> findUsersWithProfiles(Pageable pageable);
}
7. 创建Service类
service类中其实可以做一些转换,这里简化,直接返回
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;@Service
public class UserProfileService {@Autowiredprivate UserRepository userRepository;public Page<UserProfileDTO> getUsersWithProfiles(Pageable pageable) {return userRepository.findUsersWithProfiles(pageable);}
}
8. 创建Controller类暴露接口
目的就是expose the service
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class UserProfileController {@Autowiredprivate UserProfileService userProfileService;@GetMapping("/users-with-profiles")public Page<UserProfileDTO> getUsersWithProfiles(@RequestParam int page,@RequestParam int size,@RequestParam String sortBy) {Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy));return userProfileService.getUsersWithProfiles(pageable);}
}
这个返回的是一个json格式的对象。
如果和thymeleaf配合,需要修改一下返回值。
分页的原理有点难,还需要进一步分析研究。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;@Controller
public class UserProfileController {@Autowiredprivate UserProfileService userProfileService;@GetMapping("/users-with-profiles")public String getUsersWithProfiles(@RequestParam(defaultValue = "0") int page,@RequestParam(defaultValue = "10") int size,@RequestParam(defaultValue = "id") String sortBy,Model model) {Page<UserProfileDTO> userProfilesPage = userProfileService.getUsersWithProfiles(PageRequest.of(page, size, Sort.by(sortBy)));model.addAttribute("userProfiles", userProfilesPage.getContent());model.addAttribute("page", page);model.addAttribute("size", size);model.addAttribute("sortBy", sortBy);model.addAttribute("totalPages", userProfilesPage.getTotalPages());return "users";}
}
9. 创建动态HTML
需要放在 src/main/resources/templates目录下,users.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>Users with Profiles</title>
</head>
<body><h1>Users with Profiles</h1><table><thead><tr><th>Username</th><th>Address</th><th>Phone Number</th></tr></thead><tbody><tr th:each="userProfile : ${userProfiles}"><td th:text="${userProfile.username}"></td><td th:text="${userProfile.address}"></td><td th:text="${userProfile.phoneNumber}"></td></tr></tbody></table><div><a th:href="@{|/users-with-profiles?page=${page - 1}&size=${size}&sortBy=${sortBy}|}" th:if="${page > 0}">Previous</a><a th:href="@{|/users-with-profiles?page=${page + 1}&size=${size}&sortBy=${sortBy}|}" th:if="${page < totalPages - 1}">Next</a></div>
</body>
</html>
10. 解决错误,切换JdbcTemplate
上面看似完美的实现,运行起来就报错了。
the program run and report the exception:
Caused by: java.lang.UnsupportedOperationException: Page queries are not supported using string-based queries; Offending method: public abstract org.springframework.data.domain.Page dev.zzz.repository.UserRepository.findUsersWithProfiles(org.springframework.data.domain.Pageable)at org.springframework.data.jdbc.repository.query.StringBasedJdbcQuery.<init>(StringBasedJdbcQuery.java:172) ~[spring-data-jdbc-3.4.0.jar:3.4.0]at org.springframework.data.jdbc.repository.support.JdbcQueryLookupStrategy$DeclaredQueryLookupStrategy.resolveQuery(JdbcQueryLookupStrategy.java:168) ~[spring-data-jdbc-3.4.0.jar:3.4.0]at org.springframework.data.jdbc.repository.support.JdbcQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JdbcQueryLookupStrategy.java:255) ~[spring-data-jdbc-3.4.0.jar:3.4.0]at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lookupQuery(QueryExecutorMethodInterceptor.java:116) ~[spring-data-commons-3.4.0.jar:3.4.0]... 59 common frames omitted
原因是:The exception you’re encountering is due to the fact that Spring Data JDBC does not support pagination with string-based queries directly. To handle pagination and sorting with custom queries, you need to use a different approach. Here’s how you can achieve this:
Custom Repository Interface
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;public interface CustomUserRepository {Page<UserProfileDTO> findUsersWithProfiles(Pageable pageable);
}
Custom Repository Implementation
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;import java.util.List;@Repository
public class CustomUserRepositoryImpl implements CustomUserRepository {@Autowiredprivate JdbcTemplate jdbcTemplate;private final RowMapper<UserProfileDTO> rowMapper = (rs, rowNum) -> new UserProfileDTO(rs.getString("username"),rs.getString("address"),rs.getString("phoneNumber"));@Overridepublic Page<UserProfileDTO> findUsersWithProfiles(Pageable pageable) {String query = "SELECT u.username, p.address, p.phoneNumber " +"FROM users u JOIN profiles p ON u.profileId = p.id " +"ORDER BY " + pageable.getSort().toString().replace(":", " ") +" LIMIT " + pageable.getPageSize() +" OFFSET " + pageable.getOffset();List<UserProfileDTO> userProfiles = jdbcTemplate.query(query, rowMapper);String countQuery = "SELECT COUNT(*) FROM users u JOIN profiles p ON u.profileId = p.id";Long total = jdbcTemplate.queryForObject(countQuery, Long.class);return new PageImpl<>(userProfiles, pageable, total);}
}
这部分代码是关键,里面有几个坑:
首先运行查询,提示:
org.h2.jdbc.JdbcSQLSyntaxErrorException: Ambiguous column name "ID"; SQL statement:
SELECT u.username, p.address, p.phoneNumber FROM users u JOIN profiles p ON u.profileId = p.id ORDER BY id ASC LIMIT 5 OFFSET 0 [90059-232]at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) ~[h2-2.3.232.jar:2.3.232]at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) ~[h2-2.3.232.jar:2.3.232]at org.h2.message.DbException.get(DbException.java:223) ~[h2-2.3.232.jar:2.3.232]at org.h2.message.DbException.get(DbException.java:199) ~[h2-2.3.232.jar:2.3.232]at org.h2.expression.ExpressionColumn.mapColumn(ExpressionColumn.java:197) ~[h2-2.3.232.jar:2.3.232]at org.h2.expression.ExpressionColumn.mapColumns(ExpressionColumn.java:175) ~[h2-2.3.232.jar:2.3.232]
关联表,最好不要都叫ID,无法区分,改为:
"ORDER BY p." + pageable.getSort().toString().replace(":", " ") +
指定了profiles表的id
然后又提示错误:找不到列:phoneNumber,profileid,经检查对象中的名字和数据库不一样,数据库有下划线,jdbcTemplate是不允许隐式转换的,所以必须和表中的字段名一致。修改后就可以了。
Update the UserRepository Interface
import org.springframework.data.repository.CrudRepository;public interface UserRepository extends CrudRepository<User, Long>, CustomUserRepository {
}
11. 界面不美观,bootstrap加上渲染
https://getbootstrap.com/docs/5.3/getting-started/introduction/
下面一段,不知道能否使用。
head中增加:
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<table class="table table-dark">
效果如下:
相关文章:
springboot学习-分页/排序/多表查询的例子
最近喜欢上了springboot,真是个好的脚手架。今天继续学习分页/排序/多表查询等复杂功能。按步骤记录如下. 按步骤做的发现不可用,最终还是用的jdbctemplate解决。这也是一次经验。总计在最后。 1.maven依赖 首先从https://start.spring.io/ 选择需要的…...
windows 应用 UI 自动化实战
UI 自动化技术架构选型 UI 自动化是软件测试过程中的重要一环,网络上也有很多 UI 自动化相关的知识或资料,具体到 windows 端的 UI 自动化,我们需要从以下几个方面考虑: 开发语言 毋庸置疑,在 UI 自动化测试领域&am…...
ffmpeg命令详解
原文网址:ffmpeg命令详解_IT利刃出鞘的博客-CSDN博客 简介 本文介绍ffmpeg命令的用法。 命令示例 1.mp4和avi的基本互转 ffmpeg -i D:\input.mp4 E:\output.avi ffmpeg -i D:\input.avi E:\output.mp4 -i 表示input,即输入。后面填一个输入地址和一…...
【漏洞复现】CVE-2022-43396
漏洞信息 NVD - CVE-2022-43396 In the fix for CVE-2022-24697, a blacklist is used to filter user input commands. But there is a risk of being bypassed. The user can control the command by controlling the kylin.engine.spark-cmd parameter of conf. 背景介绍…...
文件的摘要算法(md5、sm3、sha256、crc)
为了校验文件在传输中保证完整性和准确性,因此需要发送方先对源文件产生一个校验码,并将该值传输给接收方,将附件通过ftph或http方式传输后,由接收方使用相同的算法对接收文件再获取一个新的校验码,将该值和发送方传的…...
如何借助AI生成PPT,让创作轻松又高效
PPT是现代职场中不可或缺的表达工具,但同时也可能是令人抓狂的时间杀手。几页幻灯片的制作,常常需要花费数小时调整字体、配色与排版。AI的飞速发展为我们带来了革新——AI生成PPT的技术不仅让制作流程大大简化,还重新定义了效率与创意的关系…...
云技术-docker
声明! 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及泷羽sec团…...
对docker安装的mysql实现主从同步
1:分别安装mysql主,从数据库 将主库容器名称改为mysql_master,将从库容器名称改为mysql_slave 安装教程:docker安装mysql 2:配置主库的my.cnf挂载文件 [mysqld] #log-bin:表示启用binlog功能,并指定二进制日志的存储目录。 log-binmysql-bin #binlog_f…...
【不定长滑动窗口】【灵神题单】【刷题笔记】
采摘水果 fruits[i]表示第i棵树上的水果种类目的是尽可能多收集水果规矩: 只有两个篮子,且每个篮子只能装一种水果,但是每个篮子能装的总量没限制一旦开始采摘,就会连续采摘,把两个篮子都用掉也就是说,采摘到最后一颗…...
AI写论文指令
一、论文选题指令 1、确定研究对象:我是一名xxx,请从以下素材内容中,结合xx相关知识,提炼出可供参考的学术概念 。以下是结合素材内容,提炼出的几个可供参考的学术概念 概念a:概念b:概念C&…...
2625扁平化嵌套数组
请你编写一个函数,它接收一个 多维数组 arr 和它的深度 n ,并返回该数组的 扁平化 后的结果。 多维数组 是一种包含整数或其他 多维数组 的递归数据结构。 数组 扁平化 是对数组的一种操作,定义是将原数组部分或全部子数组删除,…...
QT6学习第五天 第一个QT Quick程序
QT6学习第五天 第一个QT Quick程序 概述创建Qt Quick程序使用Qt资源文件 概述 如果将程序的用户界面成为前端,程序的数据存储和逻辑业务成为后端,那么传统QT Widgets程序的前后端都是用C完成的。对于现代软件开发而言,前端演化速度远快于后端…...
【开发商城系统】
在广西开发商城系统,可以按照以下步骤进行: 确定项目需求:与客户沟通,了解商城系统所需的功能和特性,并确定项目的预算和时间限制。 进行市场调研:了解广西地区的电商市场情况,包括竞争对手、消…...
(11)(2.2) BLHeli32 and BLHeli_S ESCs(二)
文章目录 前言 1 传递支持 前言 BLHeli 固件和配置应用程序的开发是为了允许配置 ESC 并提供额外功能。带有此固件的 ESC 允许配置定时、电机方向、LED、电机驱动频率等。在尝试使用 BLHeli 之前,请按照 DShot 设置说明进行操作(DShot setup instructions)。 1 传…...
C++ 11重点总结1
智能指针 智能指针: C11引入了四种智能指针: auto_ptr(已弃用)、unique_ptr、shared_ptr和weak_ptr。智能指针可以更有效地管理堆内存,并避免常见的内存泄漏问题。 shared_ptr: 自定义删除器。 shared_ptr使用引用计数来管理它指向的对象的生命周期。多个shared_ptr实例可以指向…...
海康VsionMaster学习笔记(学习工具+思路)
一、前言 VisionMaster算法平台集成机器视觉多种算法组件,适用多种应用场景,可快速组合算法,实现对工件或被测物的查找测量与缺陷检测等。VM算法平台依托海康威视在图像领域多年的技术积淀,自带强大的视觉分析工具库,可…...
基于Python语言的Web爬虫设计源码
基于Python语言的Web爬虫设计源码地址 该项目是一个基于Python语言的Web爬虫设计源码,包含20个文件,其中18个为Python源代码文件,1个Markdown文件用于文档说明,以及1个配置文件。该爬虫专注于网络信息的抓取与处理。 关键词 Py…...
学习日志 --A5rZ
24.11.27 0001:2024 强网杯青少年专项赛 EnterGam 复现已完成 0002:在x86上模拟arm64(搁置,原因:资料过少,可行性过低) 0003:2024 强网杯青少年专项赛 Flip_over 复现终止(无arm真机) 0004: 开始复现 2024 强网杯青少年专项赛 journey_story...
JVM_垃圾收集器详解
1、 前言 JVM就是Java虚拟机,说白了就是为了屏蔽底层操作系统的不一致而设计出来的一个虚拟机,让用户更加专注上层,而不用在乎下层的一个产品。这就是JVM的跨平台,一次编译,到处运行。 而JVM中的核心功能其实就是自动…...
Javascript Insights: Visualizing Var, Let, And Const In 2024
11/2024 出版 MP4 |视频:h264, 19201080 |音频:AAC,44.1 KHz 语言:英语 |大小: 2.96 GB |时长: 5 小时 34 分钟 为所有认真的 JavaScript 开发人员可视化与 VAR、LET、CONST 和 EXECUTON CONTE…...
KL散度改写为一个可用于优化的形式
理解 KL 散度及其公式推导过程 在信息论和概率论中,KL散度(Kullback-Leibler Divergence)是衡量两个概率分布之间差异的重要工具。本文将从 KL 散度的定义入手,详细解析其公式来源以及如何将其改写为一个可用于优化的形式。 1. 什…...
Java代码操作Zookeeper(使用 Apache Curator 库)
1. Zookeeper原生客户端库存在的缺点 复杂性高:原生客户端库提供了底层的 API,需要开发者手动处理很多细节,如连接管理、会话管理、异常处理等。这增加了开发的复杂性,容易出错。连接管理繁琐:使用原生客户端库时&…...
【Linux】Make/Makefile
这个3/4行的语法和1/2行是一样的。也是依赖关系和依赖方法。 make命令扫描makefile文件时,从上向下扫描,默认形成一个目标文件。 指定make clean的时候才回去执行对应的清除。 为什么要给我们的clean.PHONY:clean声明它是伪目标呢? PHONY类…...
C++练级计划->《多态》虚函数表,菱形继承多态
目录 什么是多态? 多态的条件 虚函数: 虚函数的重写: 协变 析构函数的重写 C11 final 和 override final: override: 总结: 三重对比:重载重写重定义对比 抽象类 多态的原理 虚函数…...
OkHttp3 - 2. OkHttp的核心组件与架构
1 OkHttp的工作原理 OkHttp3 的核心设计遵循以下原则: 请求与响应的分离:通过 Request 和 Response 对象解耦请求构建与结果处理。异步与同步支持:使用 Call 对象管理请求,可以同步或异步执行。高效连接复用:通过连接…...
异或操作解决一些问题
前提: 异或操作符合交换律,结合律(因为其根本上来抽象理解,就是查看所有项二进制数相同位是否有奇数个1,对运算结果二进制数而言,没有该位为0,有该位为1,与顺序无关)。 …...
操作系统之输入输出
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...
Centos 安装 Node.js 和 npm
方法2:使用 NVM(Node Version Manager)安装 安装 NVM curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash 重新加载配置 source ~/.bashrc 安装最新的 LTS 版本的 Node.js nvm install --lts 验证安装…...
C语言——指针初阶(一)
目录 一.什么是指针??? 指针是什么? 指针变量: 总结: 总结: 二.指针和指针类型 指针-整数: 总结: 指针的解引用 总结: 三.野指针 如何规避野指针 往期…...
React Native 原生开发指南
写在前面 React Native (RN) 是一个用于构建跨平台移动应用的框架。它允许开发者使用 JavaScript 和 React 来编写应用程序,并将其转换为原生代码。虽然 RN 提供了许多内置的组件和 API,但有时候你可能需要访问原生平台的特定功能或性能优化。为此&…...
重庆大渡口营销型网站建设公司推荐/怎么自己做个网站
PMP(Project Management Professional)是项目管理专业人士资格认证,是由美国国际认证协会(PMI)发起的严格评估项目管理人员知识技能是否具有高品质的资格认证考试。目前已在全球190多个国家和地区得到了认可࿰…...
网站建设评审/免费的网站域名查询565wcc
在贴代码之前,首先需要给QQ邮箱开服务IMAP/SMTP服务,详细开通方法见 "开通方法"(可能需要发送收费短信,所以只要开通这一个服务就好了)。 这边主要就是为了一个服务的授权码,如下图: 服务开好之后࿰…...
高端网站建设域名注册/汕头网站设计公司
p2p思路适应安卓,iOS,web一.SDP配置顺序1.本地PC设置生成offer sdp [localPC offerForConstraints:constraints completionHandler:^(RTCSessionDescription * _Nullable sdp, NSError * _Nullable error) { }];复制代码将生成sdp通过信令发送给远端并设…...
建设门户网站费用/公司营销网站建设
1. 远程连接linux(用Xshell) ssh 用户名服务器的ip ssh s18192.168.235.128 普通用户登录后系统的提示符:$ root用户登录后系统的提示符:# ssh rootip -p port 制定端口号 exit 退出用户登录 2. 查看服务器ip地址 ip addr ifconfig ens33: 网卡名3. 盘符…...
南城网站建设公司案例/镇江网页设计
table在第一次往HashMap中put元素的时候初始化,如果HashMap初始化的时候没有指定容量,那么初始化table的时候会使用默认的DEFAULT_INITIAL_CAPACITY参数,也就是16,作为table初始化时的长度。 如果HashMap初始化的时候指定了容量&…...
网站建设企业蛋糕/软文推广经典案例
git add -A和 git add . git add -ugit add . :监控工作区的状态树,运行会把工作时的所有变化提交到暂存区,包括文件内容修改(modified)以及新文件(new),但不包括被删除的文件。git add -u :仅监控已经被add的文件&…...