JavaWeb Day02:MyBatis XML 开发与 tlias 部门管理
1922 字
10 分钟
JavaWeb Day02:MyBatis XML 开发与 tlias 部门管理
MyBatis XML 方式开发
从注解式到 XML 式(UserMapper.java + UserMapper.xml)
Day01 的 SQL 写在 @Select/@Insert/@Update/@Delete 注解里,现在把它们全部注释掉,SQL 迁移到 UserMapper.xml,接口只留方法签名:
@Mapperpublic interface UserMapper { List<User> findAll(); // 接口只留方法签名 Integer deleteById(int id); Integer add(User user); Integer update(User user); User findByUsernameAndPassword(@Param("username") String username, @Param("password") String password);}<mapper namespace="com.ryuki.mapper.UserMapper"> <!-- namespace = 接口全限定名 --> <select id="findAll" resultType="com.ryuki.pojo.User"> select id, username, password, name, age from user </select> <delete id="deleteById" parameterType="int"> delete from user where id = #{id} </delete> <insert id="add" parameterType="com.ryuki.pojo.User"> insert into user(username, password, name, age) values(#{username}, #{password}, #{name}, #{age}) </insert> ...</mapper>- XML ↔ 接口绑定三要素:
namespace= 接口全限定名、id= 接口方法名、#{参数}= 方法入参 → 调用userMapper.findAll()时 MyBatis 用「全限定名 + 方法名」作 key 唯一找到对应MappedStatement(每个标签编译成一个 MappedStatement) - 接口依然没有实现类——底层仍是 Day01 的 JDK 动态代理,只是 SQL 来源从注解变成 XML
parameterType= 入参类型(单个 POJO/基本类型);resultType= 返回 POJO 类型
#{} vs ${}
#{} | ${} | |
|---|---|---|
| 本质 | 预编译参数占位符 | 原样文本替换 |
| 底层 | 替换成 ?,PreparedStatement.setXxx 按序设参 | 直接拼进 SQL 字符串 |
| 安全性 | ✅ 输入被当「值」,改不了 SQL 结构 | ❌ 输入能改 SQL 结构 → SQL 注入 |
| 用途 | 值(where 条件/插入值) | 动态列名/排序 order by ${orderCols}(占位符不能换列名表名) |
- 铁律:用户输入绝不能直接进
${},必须过服务端白名单/枚举(排序字段映射成固定片段) #{}就是 MyBatis 版的?占位符,底层是同一个 PreparedStatement 预编译机制
@Param 多参数绑定(findByUsernameAndPassword)
User findByUsernameAndPassword(@Param("username") String username, @Param("password") String password);<select id="findByUsernameAndPassword" resultType="com.ryuki.pojo.User"> select ... where username = #{username} and password = #{password}</select>- 多参数时 XML 里
#{参数名}必须与@Param("名字")对应——否则 MyBatis 不知道#{}里写什么名字(会报绑定错误) - 单对象参数(
add(User user))直接用#{属性名},不需要 @Param
数据源 + 映射配置(application.yml)
spring: datasource: url: jdbc:mysql://localhost:3306/web01?serverTimezone=UTC driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印 SQL mapper-locations: classpath:com/ryuki/mapper/*.xml # 指定 XML 位置mapper-locations:告诉 MyBatis 去哪找 XML 映射文件(默认在接口同包同目录可省)log-impl: StdOutImpl:把每条 SQL + 参数打印到控制台,调试神器
XML 映射文件的常用标签
除增删改查四个标签外,XML 映射文件还有这些常用标签:
<resultMap>:显式定义结果映射<sql>+<include>:抽取并复用 SQL 片段<selectKey>- 动态 SQL 标签:if / where / set / trim / foreach / choose / when / otherwise / bind
注解式 vs XML 式 对比
| 注解式 | XML 式 | |
|---|---|---|
| SQL 位置 | 写在 @Select 等注解里 | 写在 *.xml(namespace + id 绑定) |
| 优点 | 简单,适合简单 SQL | 复杂 SQL/动态 SQL 可读性好;集中管理 |
| 缺点 | 复杂 SQL 难维护、字符串里拼 SQL 易错 | 需要配 mapper-locations |
| 项目 | tlias | 本工程 |
tlias 部门管理完整 CRUD
三层架构完整落地
DeptController(@RestController) → DeptService/DeptServiceImpl → DeptMapper(@Mapper 注解式) 接收请求/返回Result 业务:补 createTime/updateTime SQL 数据访问RESTful 方法映射 + 参数绑定(DeptController)
@RestController@RequestMapping("/depts")public class DeptController { @GetMapping // GET /depts → 查询全部 public Result list() { ... } @DeleteMapping // DELETE /depts?id=1 → 按 id 删除 public Result deleteById(Integer id) { ... } @PostMapping // POST /depts body → 新增 public Result insert(@RequestBody Dept dept) { ... } @GetMapping("/{id}") // GET /depts/5 → 按 id 查询 public Result findById(@PathVariable("id") Integer id) { ... } @PutMapping // PUT /depts body → 修改 public Result update(@RequestBody Dept dept) { ... }}- RESTful 风格:同一 URL
/depts,靠 HTTP 方法区分动作(GET 查 / POST 增 / PUT 改 / DELETE 删) @PathVariable("id"):取 URL 路径{id}那一段(/depts/5→ id=5)@RequestBody:JSON 请求体 → 反序列化成 Java 对象(@RequestBody Dept dept= 前端 JSON 变 Dept);注意别和@ResponseBody(对象→JSON 返回)搞混——@RestController= Controller + ResponseBody- 简单类型参数
Integer id直接靠参数名绑定(Spring 用-parameters编译参数名,可省@RequestParam)
Result 统一返回(pojo/Result)
@Datapublic class Result { private Integer code; // 1 成功 / 0 失败 private String msg; // 提示信息 private Object data; // 业务数据 public static Result success() { ... } // 无数据成功 public static Result success(Object object) { ... } // 带数据成功 public static Result error(String msg) { ... } // 失败}- 统一返回格式:所有接口返回同一结构
{code, msg, data},前端好统一处理(判断 code 再取 data) - 静态工厂方法:
Result.success(deptList)取代手动 new——调用方写起来干净(工具类思路的复用)
驼峰映射(application.yml)
mybatis: configuration: map-underscore-to-camel-case: true # 下划线 → 驼峰 自动映射- 数据库
create_time→ 实体属性createTime自动对应,不用再手动写列别名create_time as createTime,一个开关搞定 - 底层是 MyBatis 结果封装机制的一部分:结果封装有三种形式——
<resultMap>显式映射、SQL 列别名、忽略大小写自动匹配;最终都是反射创建对象 + 反射赋值,驼峰映射就是这套机制的自动化版本
Service 层补基础字段(DeptServiceImpl)
public void insert(Dept dept) { dept.setCreateTime(LocalDateTime.now()); // 新增:两个时间都补 dept.setUpdateTime(LocalDateTime.now()); deptMapper.insert(dept);}public void update(Dept dept) { dept.setUpdateTime(LocalDateTime.now()); // 修改:只补 updateTime deptMapper.update(dept);}- 数据库时间字段由业务层(Service)补全,不让前端传——前端不可信
- Mapper 注解式 SQL(
@Select/@Insert/@Update/@Delete)复用 Day01 学的注解开发
POJO 极简(Dept)
@Data @NoArgsConstructor @AllArgsConstructorpublic class Dept { private Integer id; private String name; private LocalDateTime createTime; private LocalDateTime updateTime;}SLF4J 日志(LogTest + logback.xml)
@Slf4j + 日志级别
@Slf4j // Lombok 自动生成: private static final Logger log = LoggerFactory.getLogger(DeptController.class);public class DeptController { log.info("查询全部的部门数据"); log.debug("开始计算");}- 日志级别:
debug < info < warn < error(配了哪个级别,就输出它及更高级别) - 为什么用日志而不是
System.out.println:级别可控(生产关 debug)、能落文件、带时间/线程/类名,利于排查
logback.xml(控制台 + 文件滚动)
<appender name="STDOUT" class="ConsoleAppender"> <!-- 控制台 --> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern></appender><appender name="FILE" class="RollingFileAppender"> <!-- 滚动文件 --> <rollingPolicy class="SizeAndTimeBasedRollingPolicy"> <FileNamePattern>D:/tlias-%d{yyyy-MM-dd}-%i.log</FileNamePattern> <!-- 按日期分文件 + %i 序号 --> <MaxHistory>30</MaxHistory> <!-- 只留最近 30 天 --> <maxFileSize>10MB</maxFileSize> <!-- 单文件超 10MB 滚动 --> </rollingPolicy></appender><root level="ALL"><appender-ref ref="STDOUT"/><appender-ref ref="FILE"/></root>RollingFileAppender+SizeAndTimeBasedRollingPolicy= 日志按日期分文件 + 单文件超 10MB 滚动分卷(%i序号)→ 解决单文件无限膨胀- pattern:
%d日期 /%thread线程 /%-5level级别 /%logger{50}记录器名 /%msg消息
重点速记
- MyBatis XML 方式:namespace = 接口全限定名、id = 方法名、
#{参数}= 入参;接口仍无实现类(动态代理),SQL 从注解换成 XML #{}= 预编译占位符(防注入) vs${}= 原样文本替换(可注入,只用于动态列名/排序且须白名单)@Param("名字"):多参数时 XML 里#{名字}的绑定依据;单对象参数直接#{属性名}mapper-locations: classpath:com/ryuki/mapper/*.xml:指定 XML 位置- 结果封装三形式:
<resultMap>显式映射 / SQL 列别名 / 忽略大小写自动匹配;最后反射创建对象 + 反射赋值;map-underscore-to-camel-case是自动化版本 - XML 标签全家桶:select/insert/update/delete +
resultMap/sql+include/selectKey+ 动态 SQL 九标签(if/where/set/trim/foreach/choose/when/otherwise/bind) - RESTful:
@GetMapping/@PostMapping/@PutMapping/@DeleteMapping同 URL 方法定动作;@PathVariable取路径参数;@RequestBodyJSON→对象 - Result 统一返回:
{code, msg, data}+ 静态工厂 success()/error() - 驼峰映射:
map-underscore-to-camel-case: true让create_time→createTime - 三层 CRUD 分工:Controller 收请求 → Service 补业务字段(时间不让前端传)→ Mapper 存取
- 日志:
@Slf4j自动生成 logger;级别 debug<info<warn<error;logback 按日期+大小滚动、MaxHistory 30
代码瑕疵记录
springbott-mybatis-quickstart/:工程名拼写错误「springbott」少个 o(应为 springboot);UserMapper.java注释掉的注解式 SQL 残留可清理tlias-web-management/application.yml:数据库密码123456硬编码;serverTimezone=UTC与本地时区不一致logback.xml:<root level="ALL">全量输出,生产应info或按环境调;日志写死绝对路径D:/tlias-*.log,部署性差(应相对路径/配置化)DeptServiceImpl.deleteById:删除不存在的 id 返回 success 无感知(幂等但无提示);findById未做 null 处理直接进 ResultDeptController.deleteById(Integer id):靠参数名绑定省略@RequestParam,依赖-parameters编译,显式写更稳(但 Spring Boot 默认已开,属规范建议非 bug)
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
JavaWeb Day02:MyBatis XML 开发与 tlias 部门管理
https://ryuki.icu/posts/javaweb-day02/相关文章智能推荐
1
JavaWeb Day01:SpringBoot 入门与 MyBatis 注解开发
JavaWebSpringBoot Web 快速入门、三层分层与 IoC/DI 解耦、原生 JDBC、MyBatis 注解开发与 MySQL 基础。
2
JavaWeb Day03:Tlias 员工管理之分页查询
JavaWeb员工分页查询接口实现:LIMIT 物理分页、PageResult 泛型封装与 left join 关联部门名
3
JavaWeb Day05:全局异常、AOP 日志与管理模块
JavaWeb全局异常处理器与 AOP 操作日志切面,以及班级管理、学员管理和报表统计的业务实现
4
JavaWeb Day07:项目部署与 Docker 基础
JavaWebDocker 镜像、容器、仓库与分层存储概念,常用命令、数据卷、Dockerfile 与 Compose,以及三容器项目部署链路
5
JavaWeb Day04:条件分页、事务新增与本地上传
JavaWeb条件查询改为 POJO 对象收参并以 PageHelper 分页;员工新增覆盖事务回滚、批量插入、主键回填、操作日志与本地上传
随机文章随机推荐





















