当前位置: 首页 > news >正文

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&#xff0c;真是个好的脚手架。今天继续学习分页/排序/多表查询等复杂功能。按步骤记录如下. 按步骤做的发现不可用&#xff0c;最终还是用的jdbctemplate解决。这也是一次经验。总计在最后。 1.maven依赖 首先从https://start.spring.io/ 选择需要的…...

windows 应用 UI 自动化实战

UI 自动化技术架构选型 UI 自动化是软件测试过程中的重要一环&#xff0c;网络上也有很多 UI 自动化相关的知识或资料&#xff0c;具体到 windows 端的 UI 自动化&#xff0c;我们需要从以下几个方面考虑&#xff1a; 开发语言 毋庸置疑&#xff0c;在 UI 自动化测试领域&am…...

ffmpeg命令详解

原文网址&#xff1a;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&#xff0c;即输入。后面填一个输入地址和一…...

【漏洞复现】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)

为了校验文件在传输中保证完整性和准确性&#xff0c;因此需要发送方先对源文件产生一个校验码&#xff0c;并将该值传输给接收方&#xff0c;将附件通过ftph或http方式传输后&#xff0c;由接收方使用相同的算法对接收文件再获取一个新的校验码&#xff0c;将该值和发送方传的…...

如何借助AI生成PPT,让创作轻松又高效

PPT是现代职场中不可或缺的表达工具&#xff0c;但同时也可能是令人抓狂的时间杀手。几页幻灯片的制作&#xff0c;常常需要花费数小时调整字体、配色与排版。AI的飞速发展为我们带来了革新——AI生成PPT的技术不仅让制作流程大大简化&#xff0c;还重新定义了效率与创意的关系…...

云技术-docker

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团…...

对docker安装的mysql实现主从同步

1:分别安装mysql主,从数据库 将主库容器名称改为mysql_master,将从库容器名称改为mysql_slave 安装教程:docker安装mysql 2:配置主库的my.cnf挂载文件 [mysqld] #log-bin&#xff1a;表示启用binlog功能&#xff0c;并指定二进制日志的存储目录。 log-binmysql-bin #binlog_f…...

【不定长滑动窗口】【灵神题单】【刷题笔记】

采摘水果 fruits[i]表示第i棵树上的水果种类目的是尽可能多收集水果规矩: 只有两个篮子&#xff0c;且每个篮子只能装一种水果&#xff0c;但是每个篮子能装的总量没限制一旦开始采摘&#xff0c;就会连续采摘&#xff0c;把两个篮子都用掉也就是说&#xff0c;采摘到最后一颗…...

AI写论文指令

一、论文选题指令 1、确定研究对象&#xff1a;我是一名xxx&#xff0c;请从以下素材内容中&#xff0c;结合xx相关知识&#xff0c;提炼出可供参考的学术概念 。以下是结合素材内容&#xff0c;提炼出的几个可供参考的学术概念 概念a&#xff1a;概念b&#xff1a;概念C&…...

2625扁平化嵌套数组

请你编写一个函数&#xff0c;它接收一个 多维数组 arr 和它的深度 n &#xff0c;并返回该数组的 扁平化 后的结果。 多维数组 是一种包含整数或其他 多维数组 的递归数据结构。 数组 扁平化 是对数组的一种操作&#xff0c;定义是将原数组部分或全部子数组删除&#xff0c;…...

QT6学习第五天 第一个QT Quick程序

QT6学习第五天 第一个QT Quick程序 概述创建Qt Quick程序使用Qt资源文件 概述 如果将程序的用户界面成为前端&#xff0c;程序的数据存储和逻辑业务成为后端&#xff0c;那么传统QT Widgets程序的前后端都是用C完成的。对于现代软件开发而言&#xff0c;前端演化速度远快于后端…...

【开发商城系统】

在广西开发商城系统&#xff0c;可以按照以下步骤进行&#xff1a; 确定项目需求&#xff1a;与客户沟通&#xff0c;了解商城系统所需的功能和特性&#xff0c;并确定项目的预算和时间限制。 进行市场调研&#xff1a;了解广西地区的电商市场情况&#xff0c;包括竞争对手、消…...

(11)(2.2) BLHeli32 and BLHeli_S ESCs(二)

文章目录 前言 1 传递支持 前言 BLHeli 固件和配置应用程序的开发是为了允许配置 ESC 并提供额外功能。带有此固件的 ESC 允许配置定时、电机方向、LED、电机驱动频率等。在尝试使用 BLHeli 之前&#xff0c;请按照 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算法平台集成机器视觉多种算法组件&#xff0c;适用多种应用场景&#xff0c;可快速组合算法&#xff0c;实现对工件或被测物的查找测量与缺陷检测等。VM算法平台依托海康威视在图像领域多年的技术积淀&#xff0c;自带强大的视觉分析工具库&#xff0c;可…...

基于Python语言的Web爬虫设计源码

基于Python语言的Web爬虫设计源码地址 该项目是一个基于Python语言的Web爬虫设计源码&#xff0c;包含20个文件&#xff0c;其中18个为Python源代码文件&#xff0c;1个Markdown文件用于文档说明&#xff0c;以及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虚拟机&#xff0c;说白了就是为了屏蔽底层操作系统的不一致而设计出来的一个虚拟机&#xff0c;让用户更加专注上层&#xff0c;而不用在乎下层的一个产品。这就是JVM的跨平台&#xff0c;一次编译&#xff0c;到处运行。 而JVM中的核心功能其实就是自动…...

Javascript Insights: Visualizing Var, Let, And Const In 2024

11/2024 出版 MP4 |视频&#xff1a;h264&#xff0c; 19201080 |音频&#xff1a;AAC&#xff0c;44.1 KHz 语言&#xff1a;英语 |大小&#xff1a; 2.96 GB |时长&#xff1a; 5 小时 34 分钟 为所有认真的 JavaScript 开发人员可视化与 VAR、LET、CONST 和 EXECUTON CONTE…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...