怎样创建网站详细步骤,wordpress html5 模板下载,网络设计有哪些,网站建设华科技公司1.主要的包和目录及其主要功能 2.注册
2.1 顺序图 2.2 参数要求 2.3 接口规范 2.4 实现步骤 1.定义SQL
按用户名查询用户信息 !--1.注意namespace表示命名空间#xff0c;指定要与UserMapper.xml中的namespace相同2.统一用com.example.forum.dao.UserMapper#xff0c…1.主要的包和目录及其主要功能 2.注册
2.1 顺序图 2.2 参数要求 2.3 接口规范 2.4 实现步骤 1.定义SQL
按用户名查询用户信息 !--1.注意namespace表示命名空间指定要与UserMapper.xml中的namespace相同2.统一用com.example.forum.dao.UserMapper也就是UserMapper的完全限定名(包名类名)3.不同的映射文件指定了相同的namespace后定义的所有用id或name标识的结果集映射都可以在不同的文件中共享--select idselectByUserName resultMapBaseResultMap parameterTypejava.lang.Stringselectinclude refidBase_Column_List/from t_userwhere deleteState 0!--1. #{} 占位符允许你在 SQL 查询中动态插入参数值。这些参数值可以在执行查询时从 Java 方法的参数中获取2. 防止 SQL 注入使用 #{} 占位符可以有效防止 SQL 注入攻击。MyBatis 会将参数值作为预处理语句PreparedStatement的参数而不是直接拼接到 SQL 字符串中。这样可以避免恶意输入被当作 SQL 代码执行。3. 为什么需要 jdbcType1)类型安全指定 jdbcType可以确保 MyBatis 在处理参数时不会出现类型不匹配的问题。2)指定 jdbcType 可以帮助数据库优化器更好地理解参数类型从而提高查询性能--AND username #{username, jdbcTypeVARCHAR}
1.1 Mapper中resultMap的作用 1.作用
1.1 字段映射明确指定数据库表中的字段如何映射到实体类的属性上尤其是当字段名和属性名不一致时。
1.2 复杂类型处理支持嵌套结果映射例如关联对象或集合的映射。
1.3 提高灵活性允许更精细地控制数据的处理方式比如处理数据库中的 null 值或默认值。 即使你不定义 resultMapMyBatis 也可以通过简单的自动映射autoMapping来处理字段和属性的映射关系。但是自动映射有一些限制 它要求数据库字段名和实体类属性名完全一致。 对于复杂的数据结构如关联对象或嵌套集合自动映射可能无法正确处理。 因此如果你的项目中字段名和属性名完全一致并且没有复杂的关联关系那么不定义 resultMap 也可以正常使用。 但如果存在字段名不一致或复杂的数据结构定义 resultMap 是更稳妥的选择。 2.复杂的数据结构处理
MyBatis中的复杂类型处理主要通过resultMap实现嵌套结果映射
1.1 关联对象一对一
示例场景商品(Product)与商品详情(ProductDetail)的一对一关系
resultMap idproductWithDetailMap typeProductid propertyid columnproduct_id/result propertyname columnproduct_name/!-- 嵌套关联对象 --association propertydetail javaTypeProductDetail fetchTypeeagerid propertyid columndetail_id/result propertydescription columnproduct_desc//association
/resultMap通过association标签将查询结果中的detail_id等字段映射到Product对象的detail属性
1.2 集合映射一对多
示例场景班级(Class)与学生(Student)的一对多关系
resultMap idclassWithStudentsMap typeClassid propertyid columnclass_id/result propertyclassName columnclass_name/!-- 嵌套集合映射 --collection propertystudents ofTypeStudent fetchTypeeagerid propertyid columnstudent_id/result propertyname columnstudent_name//collection
/resultMap通过collection标签将多个学生记录映射到Class对象的students集合属性
3. 实现方式对比
嵌套结果单次SQL查询通过表连接获取所有数据通过resultMap拆解映射嵌套查询分多次SQL查询通过column属性传递参数
3.1 嵌套结果映射 的应用场景与组件
1.核心实现位置
在resultMap中使用association一对一或collection一对多标签通过单条SQL的JOIN语句关联多表查询
resultMap idorderWithUserMap typeOrderid propertyid columnorder_id/!-- 嵌套结果映射通过JOIN查询直接映射关联对象 --association propertyuser javaTypeUser fetchTypeeagerid propertyid columnuser_id/result propertyname columnuser_name//association
/resultMap1.此配置通过LEFT JOIN user ON order.user_iduser.id一次性获取订单及关联用户数据 2. 加上 fetchTypeeager表示在加载主实体时立即加载其关联的集合数据。 默认情况下MyBatis 使用延迟加载fetchTypelazy即在访问关联数据时才加载。 通过设置 fetchTypeeager可以一次性加载主实体及其关联的集合数据避免“N1 查询问题”。 3.2 嵌套查询 的应用场景与组件
1.核心实现位置
在resultMap中使用association或collection的select属性指定子查询通过column属性传递主查询字段值给子查询
resultMap idorderWithUserQueryMap typeOrderid propertyid columnorder_id/!-- 嵌套查询通过column传递参数执行子查询 --association propertyuser columnuser_id selectcom.example.mapper.UserMapper.selectById/
/resultMap此配置会先执行订单查询再根据user_id逐个调用UserMapper.selectById加载用户数据。
3.3 关键差异对比
组件/行为嵌套结果映射嵌套查询SQL生成方式单条含JOIN的复杂SQL主查询多条子SQL参数传递机制无直接映射结果集通过column传递参数性能影响点 1.可能因多表连接导致结果集膨胀笛卡尔积问题1.多次简单查询避免复杂连接但可能因 N1 问题拖慢性能尤其数据量大时2.多次查询可能导致弱一致性高并发时标签配置重点联合查询结果拆解selectcolumn联动
3.4 配置与灵活性对比
特性嵌套结果映射嵌套查询SQL 复杂度需手动编写多表连接 SQL可读性低主查询和子查询分离SQL 更简洁复用性结果映射可复用通过 extends 继承子查询语句可复用如多个实体引用同一查询参数传递灵活性无参数传递机制支持多参数传递column{prop1col1, prop2col2} 1.支持多参数传递column{prop1col1, prop2col2} !-- 单参数传递示例 --
resultMap iddeptSimpleMap typeDeptid propertyid columnid/collection propertyusers selectcom.mapper.UserMapper.selectByDeptId columnid !-- 只传递部门ID --/
/resultMap!-- 多参数传递示例 --
resultMap iddeptComplexMap typeDeptid propertyid columnid/result propertycompId columncomp_id/collection propertyusers selectcom.mapper.UserMapper.selectByDeptAndComp column{deptIdid, companyIdcomp_id} !-- 传递部门ID和公司ID --/
/resultMap代码说明单参数直接写列名多参数需用{key1col1,key2col2}格式嵌套查询方法需接收Map类型参数。注意列名需与主查询结果集字段对应!-- 单参数接收查询 --
select idselectByDeptId resultTypeUserSELECT * FROM user WHERE dept_id #{deptId}
/select!-- 多参数接收查询 --
select idselectByDeptAndComp resultTypeUserSELECT * FROM user WHERE dept_id #{deptId} AND company_id #{companyId}
/select2.结果映射可复用通过 extends 继承 父类映射定义基础字段子类映射仅需补充新增字段 resultMap idBaseResultMap typeUserid columnid propertyid/result columnname propertyname/
/resultMapresultMap idUserViewMap typeUserView extendsBaseResultMapresult columnrole_name propertyroleName/
/resultMap子类UserView继承父类User的所有映射配置 3.子查询语句可复用如多个实体引用同一查询 1.sql片段复用
定义可重用的SQL片段通过include引用12sql idbaseColumnsid, name, create_time/sqlselect idselectUser resultTypeUserSELECT include refidbaseColumns/ FROM user
/select2.嵌套查询复用
通过association/collection的select属性引用已定义的查询
resultMap iddeptMap typeDeptcollection propertyusers selectselectUserByDept columnid/
/resultMap 3.5 优化建议
嵌套结果映射通过 JOIN 优化 SQL减少返回字段。嵌套查询启用懒加载lazyLoadingEnabledtrue 关联数据延迟加载 避免 N1 问题 1.通过 JOIN 优化 SQL减少返回字段 -- 完整优化版本含索引建议
CREATE INDEX idx_cover ON users(id, name, status);
CREATE INDEX idx_cover_orders ON orders(user_id, id, order_no);EXPLAIN
SELECT o.id, o.order_no, u.name
FROM orders o FORCE INDEX(idx_cover)
JOIN users u FORCE INDEX(idx_cover_orders) ON o.user_idu.id
WHERE u.status1
LIMIT 100;
1.优化前使用SELECT *会返回orders表所有字段
优化后明确指定o.id, o.order_no, u.name三个业务字段2.如果这些字段都在索引中可以利用覆盖索引避免回表2.1 假设我们有以下索引结构users表有主键索引(id)和复合索引(id, name, status)
orders表有主键索引(id)和索引(user_id)2.2 当执行优化后的查询时SELECT o.id, o.order_no, u.name
FROM orders o JOIN users u ON o.user_idu.id
WHERE u.status12.3覆盖索引生效的两种情况
覆盖索引是指查询所需的所有列都包含在索引中无需回表查询原始数据表从而提高查询效率对users表的覆盖
如果存在复合索引(id, name, status)
查询只需访问索引就能获取id、name、status字段
EXPLAIN会显示Using index对orders表的覆盖
如果存在复合索引(user_id, id, order_no)
查询只需访问索引就能获取所需字段
不需要回表查数据页2.4 验证方法
EXPLAIN
SELECT o.id, o.order_no, u.name
FROM orders o JOIN users u ON o.user_idu.id
WHERE u.status1;
在Extra列看到Using index就表示使用了覆盖索引。如果没有则需要创建包含查询字段的复合索引CREATE INDEX idx_cover ON users(id, name, status);
CREATE INDEX idx_cover_orders ON orders(user_id, id, order_no);2.5 FORCE INDEX强制使用指定的索引确保查询优化器按照指定的索引进行查询。JOIN策略优化的实践
3.用INNER JOIN替代了WHERE IN子查询嵌套查询4.users表作为被驱动表其id字段应该建立索引SELECT a.col1, b.col2
FROM tableA a
JOIN tableB b ON a.id b.id;
tableA 是驱动表。
tableB 是被驱动表。如果驱动表较小查询可能更快因为只需要扫描较少的行。
如果驱动表的连接条件过滤性很好即能快速缩小匹配范围查询效率也会更高。假设users表过滤后结果集较小符合小表驱动大表原则
“小表驱动大表” 的核心在于减少驱动表的行数从而降低查询的总体开销包括减少扫描行数、减少内存和缓存的使用、减少 I/O 操作等。这种优化策略特别适用于 JOIN 查询尤其是当其中一个表经过过滤后结果集显著减少时。执行计划优化要点
5.优化后查询应显示Using index如果name在索引中
EXPLAIN会显示使用索引连接而非全表扫描WHERE u.status1在连接之前先过滤出 status1 的用户记录。
6.确保连接算法是BNLJ而非性能更差的Simple Nested Loop JoinBlock Nested Loop Join块嵌套循环连接是一种在数据库系统中实现表连接操作的算法。它属于嵌套循环连接的一种优化版本通过一次性处理多个元组形成一个 块来减少 I/O 次数和提高缓存利用率。块嵌套循环连接算法将外部表通常是较小的表分成多个块每次将一个块加载到内存中。然后它逐行扫描内部表并将每一行与内存中的所有元组进行比较以找出匹配的记录。与简单的嵌套循环连接相比块嵌套循环连接的主要改进在于减少 I/O 次数通过批量处理元组减少了对外部表的读取次数。提高缓存利用率利用内存块的局部性原理减少了缓存失效假设我们有两个表Employees员工和Departments部门需要通过department_id字段进行连接。分块将Employees表外部表分成多个块每个块包含 1000 行。加载每次加载一个块到内存中。扫描逐行扫描Departments表内部表并与内存中的每个员工记录进行比较。匹配输出如果某个员工的department_id与当前部门记录的id匹配则输出连接结果。优化建议选择合适的块大小块大小应根据可用内存和缓存特性进行调整。将较小的表作为外部表减少需要加载的块数。结合索引如果内部表在连接键上有索引可以加速匹配过程。块嵌套循环连接是数据库查询执行中的一种基础算法理解它有助于深入掌握数据库系统的查询优化原理7.结果集处理改进
虽然示例没展示LIMIT但实际可添加LIMIT 100限制行数
这种简单JOIN比嵌套子查询更易维护和优化 2.N1问题 1.1 典型案例
以“博客系统”为例
第 1 次查询获取所有博客帖子主对象
SELECT * FROM posts; -- 返回 N 条记录例如 100 条第 2 到 N1 次查询遍历每条帖子查询其关联的作者信息关联对象
SELECT * FROM authors WHERE id 1; -- 帖子 1 的作者
SELECT * FROM authors WHERE id 2; -- 帖子 2 的作者
...
SELECT * FROM authors WHERE id 100; -- 帖子 100 的作者总查询次数 1主查询 N关联查询 101 次
1.2 性能危害
问题类型具体影响数据库压力大量小查询消耗连接池资源导致数据库 CPU/IO 飙升尤其在并发场景下可能引发雪崩网络延迟累积每次查询需经历网络传输N 较大时总延迟显著增加例如 100 次 10ms 查询 1秒延迟应用响应变慢前端页面等待时间延长用户体验下降如商品列表加载卡顿 本质矛盾ORM 的“对象思维”逐对象加载关联数据与数据库“批量查询”的高效性冲突 1.3 解决方案
1.3.1 预加载Eager Loading—— 嵌套结果映射
一次性通过 JOIN 或子查询获取主对象及关联对象仅需 1 次查询
SELECT posts.*, authors.name
FROM posts
LEFT JOIN authors ON posts.author_id authors.id; -- 1 次查询解决1. SELECT posts.*, authors.name posts.*表示选择 posts 表中的所有列。 authors.name表示选择 authors 表中的 name 列。 这部分定义了查询结果中要返回的字段。 2. FROM posts 指定主表为 posts即查询的起点是 posts 表。 3. LEFT JOIN authors LEFT JOIN 是一种连接操作用于将 posts 表与 authors 表进行连接。 LEFT JOIN 的特点是即使右边的表authors中没有匹配的行左边的表posts中的行仍然会出现在结果中未匹配的列会显示为 NULL。 4. ON posts.author_id authors.id ON 子句定义了连接条件即如何将 posts 表和 authors 表的行关联起来。 这里的条件是posts.author_id文章的作者 ID等于 authors.id作者的 ID ORM 支持
JPAEntityGraph(attributePaths authors)HibernateFetchType.EAGER 或 Fetch(FetchMode.JOIN)MyBatiscollection 嵌套查询优化需配置 fetchTypeeager
1.3.2 批量延迟加载Batch Lazy Loading 同时加上禁用激进懒加载效果更好aggressiveLazyLoadingfalse 激进懒加载可能会自动加载所有相关联的数据即使这些数据在当前查询中并不需要。禁用它意味着更精确地控制加载的数据量避免不必要的加载进一步优化性能。 当访问关联对象时按批次加载非逐条加载
-- 首次访问作者时批量加载所有关联作者例如 10 条/批
SELECT * FROM authors WHERE id IN (1, 2, 3, ..., 10); 1. 配置延迟加载XML映射文件resultMap idPostResultMap typePostassociation propertyauthor columnauthor_id selectcom.example.mapper.AuthorMapper.selectByIdfetchTypelazy/
/resultMap
注selectById 是单条查询方法用于兼容默认延迟加载机制36。2. 实现批量加载Java层// 批量查询接口方法
ListAuthor selectAuthorsByIds(Param(ids) ListLong ids);// 实现类
public class PostService {Autowiredprivate AuthorMapper authorMapper;public void loadAuthorsForPosts(ListPost posts) {SetLong authorIds posts.stream().map(Post::getAuthorId).collect(Collectors.toSet());MapLong, Author authorMap authorMapper.selectAuthorsByIds(new ArrayList(authorIds)).stream().collect(Collectors.toMap(Author::getId, a - a));posts.forEach(post - post.setAuthor(authorMap.get(post.getAuthorId())));}
}3. 批量查询SQLMapper XMLselect idselectAuthorsByIds resultTypeAuthorSELECT * FROM authors WHERE id INforeach itemid collectionids open( separator, close)#{id}/foreach
/select初始查询
仅获取主表数据如 SELECT * FROM posts。批量预加载
主动调用 loadAuthorsForPosts 方法通过 IN 语句批量查询关联数据。属性注入
将查询结果手动注入到主对象中后续访问 post.getAuthor() 直接返回缓存对象 方案对比 方式查询次数适用场景缺点原生延迟加载N1关联数据访问分散性能差手动批量加载2需集中处理关联数据需额外编码JOIN查询1简单关联且数据量小可能返回冗余数据4 关键点通过业务层主动触发批量查询绕过 MyBatis 延迟加载的单条查询限制 总结 通过这种方式MyBatis 实现了批量延迟加载 延迟加载在首次访问关联对象时才加载数据避免不必要的查询。 批量加载通过一次性查询多个关联对象减少了数据库查询次数避免了“N1 查询问题”。 这种策略在处理大量关联数据时非常高效尤其是在分页查询或批量处理场景中
1.3.3 DTO 投影Data Transfer Object
仅查询所需字段避免加载全量关联对象 SELECT p.title, a.name AS author_name
FROM posts p
JOIN authors a ON p.author_id a.id;减少数据传输量提升响应速度 典型应用场景 移动端列表页仅返回ID、名称、缩略图等核心字段68微服务API跨服务调用时过滤敏感字段如密码哈希16大数据导出选择列减少文件体积和传输时间 N1 问题的本质是多次小查询代替单次批量查询导致的性能劣化通过预加载、批量加载或 DTO 投影可有效规避。关键在于理解 ORM 机制根据数据量和访问模式选择最优策略。 3.6 选择建议
1.优先嵌套结果空间换时间
适用当关联数据量小、需要强一致性时如订单详情页
不适用多层级嵌套或关联表过多导致结果集膨胀时
2.选择嵌套查询时间换灵活性
适用需懒加载或主查询结果集小时如用户中心页的订单列表
不适用对延迟敏感或N值过大1000时 简单关联如一对一优先用嵌套结果映射 复杂嵌套如一对多多对一或大数据量场景用嵌套查询懒加载 4.注意事项
关联对象使用javaType指定类型集合映射使用ofType指定元素类型建议对关联字段使用别名避免列名冲突 5.理解 ORMObject-Relational Mapping对象关系映射机制 是使用 MyBatis 或其他 ORM 框架的基础。ORM 机制的核心是将关系型数据库中的表结构映射到面向对象编程语言中的类和对象从而简化数据库操作提高开发效率 2.DAO添加接口 // 1.Param(username) 为SQL 传入的参数名User selectByUserName(Param(username) String username);3.进行Service中业务方法的编写
3.1 创建Service接口
com.example.forum.services包下创建IUserService接口
3.2 实现Service接口
Resourceprivate UserMapper userMapper;/*** 创建一个普通用户** param user 用户信息*/Overridepublic void createNormalUser(User user) {// 1.非空校验if(user null || StringUtils.isEmpty(user.getUsername())|| StringUtils.isEmpty(user.getPassword()) || StringUtils.isEmpty(user.getNickname())|| StringUtils.isEmpty(user.getSalt()) ) {// 打印日志log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());// 抛出异常, 统一抛出 ApplicationExceptionthrow new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));}// 2.按用户名查询用户信息User existUser userMapper.selectByUserName(user.getUsername());// 2.1 判断用户是否存在if (existUser ! null) {log.info(ResultCode.FAILED_USER_EXISTS.toString());throw new ApplicationException(AppResult.failed(ResultCode.FAILED_USER_EXISTS));}// 3.新增用户流程, 设置默认值user.setGender((byte) 2);user.setArticleCount(0);user.setIsAdmin((byte) 0);user.setState((byte) 0);user.setDeleteState((byte) 0);// 当前日期Date date new Date();user.setCreateTime(date);user.setUpdateTime(date);// 写入数据库int row userMapper.insertSelective(user);if(row ! 1) {// 打印日志log.warn(ResultCode.FAILED_CREATE.toString());// 抛出异常, 统一抛出 ApplicationExceptionthrow new ApplicationException(AppResult.failed(ResultCode.FAILED_CREATE));}log.info([UserServiceImpl]:新增用户成功 username user.getUsername() . );}
3.3 进行单元测试 package com.example.forum.services.impl;import com.example.forum.model.User;
import com.example.forum.services.IUserService;
import com.example.forum.utils.MD5Util;
import com.example.forum.utils.UUIDUtil;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;/*** Created with IntelliJ IDEA* Description* User: 王杰* Date: 2025-06-14* Time: 13:06*/
SpringBootTest
class UserServiceImplTest {Resourceprivate IUserService userService;Testvoid createNormalUser() {// 构造User对象User user new User();user.setUsername(girl);user.setNickname(girl);// 定义一个原始的密码String password 123456;// 生成盐String salt UUIDUtil.UUID_32();// 生成密码的密文String ciphertext MD5Util.md5Salt(password, salt);user.setPassword(ciphertext);// 设置盐user.setSalt(salt);// 调用Service 层的方法userService.createNormalUser(user);// 打印结果System.out.println(user);}
}
3.4 编写Controller中的代码 package com.example.forum.controller;import com.example.forum.common.AppResult;
import com.example.forum.common.ResultCode;
import com.example.forum.model.User;
import com.example.forum.services.IUserService;
import com.example.forum.utils.MD5Util;
import com.example.forum.utils.UUIDUtil;
import com.sun.istack.internal.NotNull;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** Created with IntelliJ IDEA* Description* User: 王杰* Date: 2025-06-14* Time: 18:52*/
// 对Controller进行API接口的描述
Api(tags用户接口)
// 日志注解
Slf4j
// 这是一个返回数据的Controller
RestController
// 路由映射 一级路径
RequestMapping(/user)
public class UserController {Resourceprivate IUserService userService;/*** 用户注册* param username 用户名* param nickname 用户昵称* param password 密码* param passwordRepeat 确认密码* return*/ApiOperation(用户注册)PostMapping(/register)public AppResult register(ApiParam(用户名) RequestParam(username) NotNull String username,ApiParam(昵称) RequestParam(nickname) NotNull String nickname,ApiParam(密码) RequestParam(password) NotNull String password,ApiParam(确认密码) RequestParam(passwordRepeat) NotNull String passwordRepeat) {// 1.校验密码与确认密码是否相同if (!password.equals(passwordRepeat)) {log.warn(ResultCode.FAILED_TWO_PWD_NOT_SAME.toString());// 返回错误信息return AppResult.failed(ResultCode.FAILED_TWO_PWD_NOT_SAME);}// 2.准备数据// 构造User对象User user new User();user.setUsername(username);user.setNickname(nickname);// 生成盐String salt UUIDUtil.UUID_32();// 生成密码的密文String encryptPassword MD5Util.md5Salt(password, salt);user.setPassword(encryptPassword);// 设置盐user.setSalt(salt);// 3.调用Service 层的方法userService.createNormalUser(user);// 4.返回成功return AppResult.success();}
}3.5 测试Controller中对外提供的API