当前位置: 首页 > 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…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...