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…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)
旅游业是一个城市的重要产业构成。旅游竞争力是一个城市竞争力的重要构成部分。一个城市的旅游竞争力反映了其在旅游市场竞争中的比较优势。 今日我们分享的是2005-2019年我国272个地级市的旅游竞争力多指标数据!该数据集源自2025年4月发表于《地理学报》的论文成果…...
leetcode_69.x的平方根
题目如下 : 看到题 ,我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历,我们是整数的平方根,所以我们分两…...
简单介绍C++中 string与wstring
在C中,string和wstring是两种用于处理不同字符编码的字符串类型,分别基于char和wchar_t字符类型。以下是它们的详细说明和对比: 1. 基础定义 string 类型:std::string 字符类型:char(通常为8位)…...
Spring Boot 与 Kafka 的深度集成实践(二)
3. 生产者实现 3.1 生产者配置 在 Spring Boot 项目中,配置 Kafka 生产者主要是配置生产者工厂(ProducerFactory)和 KafkaTemplate 。生产者工厂负责创建 Kafka 生产者实例,而 KafkaTemplate 则是用于发送消息的核心组件&#x…...
边缘计算设备全解析:边缘盒子在各大行业的落地应用场景
随着工业物联网、AI、5G的发展,数据量呈爆炸式增长。但你有没有想过,我们生成的数据,真的都要发回云端处理吗?其实不一定。特别是在一些对响应时间、网络带宽、数据隐私要求高的行业里,边缘计算开始“火”了起来&#…...
