spring boot+mybatis开发基础复习

spring 全栈开发的标准基础框架:entity->mapper->service->controller->vue

一.前言

1.常见的命名形式

2.规范的命名方式

|-----|--------------------------------------------------------------------------------------------------------------|
| 名字 | 适用场景 |
| 蛇形 | 数据库表 |
| 驼峰 | java变量,方法名 |
| 帕斯卡 | 类名 |
| 烤肉串 | URL(https://example.com/user-profile) |

3.规范命名的原因

首先,来说说数据库的命名,数据库不区分大小写,驼峰和帕斯卡会变形,所以,大部分人都会以蛇形的形式写数据库,mybatis的设计者的映射机制就是把数据库的蛇形转换为驼峰。

Java 用驼峰,是语言本身的规范,spring boot 常常搭配mybatis使用。

MyBatis 设计了 map-underscore-to-camel-case 这个配置,把数据库的蛇形自动转成 Java 的驼峰,省去了手动写别名的麻烦。

如果不规范,比如数据库也用驼峰,MyBatis 可能映射不上,或者每条 SQL 都要手动写别名,代码就变得很乱。所以团队里统一规范,数据库蛇形、Java 驼峰,是最省事、最不容易出错的做法。

一个标准的数据传输流程如下:

spring boot默认jackson转json是什么意思

后端 Java 对象:

java 复制代码
public class User {
    private String userName;
    private Integer userAge;
}

// 内存里是这样的
User user = new User("张三", 25);

前端需要的是 JSON 字符串:

java 复制代码
{
    "userName": "张三",
    "userAge": 25
}

Java 对象 → JSON 字符串 ,这个过程就叫序列化 (转 JSON)。

Jackson 就是做这个事的工具。

4.AOP和IOC

首先看一下官方答案:

控制反转(IoC)是面向对象设计中的一种原则,旨在降低组件之间的耦合度,它将对象的创建、组装及生命周期管理权从应用程序代码转移至外部容器(如Spring容器),通过依赖注入(DI)的方式实现组件间的解耦。面向切面编程(AOP)则是一种编程范式,它将日志记录、事务管理、权限控制等横切关注点从业务逻辑中分离出来,封装为独立的切面,并通过动态代理机制在运行时或编译期将这些切面透明地织入到目标方法的执行链中,从而在不修改原有代码的基础上增强方法的行为。两者相辅相成:IoC负责管理组件间的依赖关系,AOP负责处理跨多个模块的横切逻辑,共同构成了Spring框架的核心基础。

翻译成大白话

IoC(控制反转)------ 把"控制权"交出去(Inversion of Control)

IOC就是spring帮我们创建对象,不用自己New了,用@Autowired拿就行,AOP就是把日志、事务这种重复的代码抽出来,写一次,然后用@Transaction这种注解声明一下,spring就会自动帮我们加上。

java 复制代码
// 没有 IoC:你自己 new
UserService userService = new UserService();

// 有了 IoC:Spring 帮你 new,你用 @Autowired 拿
@Autowired
private UserService userService;

所以 IoC 的本质是:你不用管对象怎么来的,直接用就行。

AOP(面向切面编程)------ 把"重复代码"抽出来

(Aspect Oriented Programming)

java 复制代码
// 没有 AOP:每个方法里都要写开始事务、提交事务
public void addUser() {
    beginTransaction();  // 重复
    // ... 业务代码
    commitTransaction(); // 重复
}

// 有了 AOP:只写一个 @Transactional,Spring 自动帮你加事务
@Transactional
public void addUser() {
    // 只写业务代码
}

所以 AOP 的本质是:把重复代码抽出来,用注解声明一下就行。

5.常见的注解

|--------|--------------------------------------|
| 分类 | 核心注解 |
| 启动 | @SpringBootApplication |
| 声明Bean | @RestController、@Service、@RePository |
| 注入 | @Autowired |
| 请求映射 | @GetMapping、@PostMapping |
| 取参数 | @PathVariable、@RequestBody |
| 读配置 | @value |
[spring boot注解表]

|------------------------------------------------------------------|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|
| 注解 | 功能 | 示例/使用场景 | 注解类型 |
| @Select/@Update/@Delete/@Insert | CRUD | @Select("SELECT * FROM user WHERE id = #{id}") | CRUD注解 |
| @Results | 定义一组结果映射 | 表字段名与 Java 属性名不一致时 | 结果映射注解 |
| @Result | 单个字段的映射 | 配合 @Results 使用 | 结果映射注解 |
| @ResultMap | 复用已定义的映射 | 多个方法共享同一套映射规则 | 结果映射注解 |
| @Param | 给参数命名 (多参数时必须用) | @Select("SELECT * FROM user WHERE name = #{name} AND age = #{age}") User getByNameAndAge(@Param("name") String name, @Param("age") int age) | 参数处理注解 |
| @Options | 配置额外选项(如设置主键自增) | @Options(useGeneratedKeys = true, keyProperty = "id") | 主键生成与配置注解 |
| @SelectKey | 自定义获取主键的 SQL(用于非自增主键) | @SelectKey(statement = "SELECT SEQ_USER.NEXTVAL FROM DUAL", keyProperty = "id", before = true) | 主键生成与配置注解 |
| @SelectProvider/@InsertProvider /@DeleteProvider/@UpdateProvider | 用 Java 类动态生成sql语句 | @SelectProvider(type = UserSqlProvider.class, method = "getList") | 动态 SQL 注解(复杂查询) |
| @CacheNamespace | 在 Mapper 接口上定义二级缓存 | @CacheNamespace(eviction = FifoCache.class, flushInterval = 60000) | 缓存注解(性能优化) |
[mybatis注解]

6.配置文件

1.引入mybatis

java文件中:import org.apache.ibatis.annotations.*;

pom.xml中:

java 复制代码
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>

二.框架介绍

数据库建表 SQL

sql 复制代码
CREATE TABLE user (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    age INT
);

spring和mybatis交互的时间线如下:


1.entity

java 复制代码
public class user{

private Long id;
private Integer age;
private String name;
//构造函数
public user(String name,Integer age){//全参构造函数
this.name=name;
this.age=age;
}
public user(){}//无参构造函数
//getter和setter
public Long GetId(){return id;}
public void SetId(Long id){this.id=id;}
public Integer GetAge(){return age;}
public void SetAge(Integer age){this.age=age;}
public String GetName(){return name;}
public void SetName(String name){this.name=name;}
}

如果使用lombok工具,就可以省去一些代码。比如使用Data注解,就可以省去无参构造函数和getter和setter,equals(),hashCode(),toString()方法。

java 复制代码
import lombok.data;
@Data
public class User{
private Long id;
private String name;
private Integer age;
}

这个等价于

java 复制代码
public class User {
    private Long id;
    private String name;
    private Integer age;

    // 1. 无参构造函数(因为没有任何 final 或 @NonNull 字段)
    public User() {
    }

    // 2. 所有字段的 getter 方法
    public Long getId() {
        return this.id;
    }

    public String getName() {
        return this.name;
    }

    public Integer getAge() {
        return this.age;
    }

    // 3. 所有非 final 字段的 setter 方法
    public void setId(Long id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    // 4. equals 方法
    @Override
    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof User)) return false;
        User other = (User) o;
        if (!other.canEqual(this)) return false;
        // 比较 id、name、age 字段
        // ... 完整实现
    }

    protected boolean canEqual(Object other) {
        return other instanceof User;
    }

    // 5. hashCode 方法
    @Override
    public int hashCode() {
        // 基于 id、name、age 计算 hashCode
        // ... 完整实现
    }

    // 6. toString 方法
    @Override
    public String toString() {
        return "User(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")";
    }
}

如果要加上全参构造函数,可以加上@AllArgsConstructor注解。


2.mapper

java 复制代码
package com.你的包名.mapper;

import com.你的包名.entity.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

@Mapper
public interface UserMapper {

    @Select("SELECT * FROM user")
    List<User> findAll();

    @Select("SELECT * FROM user WHERE id = #{id}")
    User findById(Long id);

    @Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(User user);

    @Update("UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}")
    int update(User user);

    @Delete("DELETE FROM user WHERE id = #{id}")
    int delete(Long id);
}

这里的@Select这些CRUD注解是告诉mybatis去执行sql。这个 @Select("SELECT * FROM user")里面的sql语句和数据库查询语法基本一致,select这些都是不区分大小写。

要注意,这里的@Insert/@Update/@Delete 返回 int 是 JDBC 规范,代表数据库受影响的行数

java 复制代码
@Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(User user);

图4.1

1)这里的insert方法里面的参数是User,为什么不是name,age呢?

因为 SQL 语句中的 #{name}#{age}#{id} 需要从一个对象中获取对应的属性值。

2)#{name},#{age}这是什么?

#{name}#{age}MyBatis 的参数占位符,作用如下:

  • 获取 Java 对象的属性值(或 Map 的 key 对应的值)

  • 预编译参数,防止 SQL 注入(安全)

工作原理

假设你有一个 Java 对象User,当mybatis执行图4.1的代码时,实际执行编译后的代码是

sql 复制代码
INSERT INTO user(name, age) VALUES(?, ?)

3)下面代码里面的update要是更新了name,而没有更新age,我只传了name,会发生什么?

sql 复制代码
@Update("UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}")
    int update(User user);

答案是name更新成功,但是age变成null。因为上面的这种写法用的是mybatis。如果用mybatis-plug就可以实现智能更新,

java 复制代码
// 1. Mapper 接口继承 BaseMapper
@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 无需再写 @Update 注解,直接继承方法即可
}
java 复制代码
// 2. 业务代码调用
public void updateUser() {
    User user = new User();
    user.setId(1L);          // 必须设置主键 ID
    user.setName("李四");     // 只修改名字
    // age 字段不设置,保持为 null

    // 生成的 SQL: UPDATE user SET name = '李四' WHERE id = 1;
    // age 不会被更新,保留数据库原值
    userMapper.updateById(user);
}

4)mapper里面的为什么是接口,而不是类,具体的实现类去哪里了?

因为mybatis帮你生成了一个实现这个类的接口,你不需要自己写实现类了。

java 复制代码
@Mapper
public interface UserMapper{
@Select("select * from user")
List<User> findAll();

}

mybatis在背后帮你生成了下面的实现类,你看不见

java 复制代码
public class UserMapperImpl implements UserMapper{

@override
public List<User> findAll(){
//执行SQL,把结果封装成List<user>返回
}
}

3. Service(业务逻辑层)

java 复制代码
package com.你的包名.service;

import com.你的包名.entity.User;
import com.你的包名.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;//自动注解(让spring自动创建对象加进来)
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public List<User> findAll() {
        return userMapper.findAll();
    }

    public User findById(Long id) {
        return userMapper.findById(id);
    }

    public int add(User user) {
        return userMapper.insert(user);
    }

    public int update(User user) {
        return userMapper.update(user);
    }

    public int delete(Long id) {
        return userMapper.delete(id);
    }
}

这里重点说一下Autowired注解

java 复制代码
@Autowired
private UserMapper userMapper;

上面的代码等价于

java 复制代码
private UserMapper userMapper=new UserMapperImpl()

4. Controller(控制层)

java 复制代码
package com.你的包名.controller;

import com.你的包名.entity.User;
import com.你的包名.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping
    public List<User> findAll() {
        return userService.findAll();
    }

    @GetMapping("/{id}")
    public User findById(@PathVariable Long id) {
        return userService.findById(id);
    }

    @PostMapping
    public String add(@RequestBody User user) {
        int result = userService.add(user);
        return result > 0 ? "添加成功" : "添加失败";
    }

    @PutMapping
    public String update(@RequestBody User user) {
        int result = userService.update(user);
        return result > 0 ? "修改成功" : "修改失败";
    }

    @DeleteMapping("/{id}")
    public String delete(@PathVariable Long id) {
        int result = userService.delete(id);
        return result > 0 ? "删除成功" : "删除失败";
    }
}

控制层是前后端交互的"接口",

这里的重点说一下@RestController。``@RestController 是 Spring Boot 中用于构建 RESTful API 的核心注解,它的作用是让一个 Java 类成为处理 HTTP 请求的控制器,并自动将返回结果转为 JSON 格式

1)@RestController的核心作用:

2)与普通 Controller 的区别

传统 @Controller(返回页面)

java 复制代码
@Controller
public class UserController {
    
    @GetMapping("/user")
    public String getUser(Model model) {
        model.addAttribute("name", "张三");
        return "userPage";  // 返回视图名称,跳转到 userPage.html
    }
}

@RestController(返回数据)

java 复制代码
@RestController
public class UserController {
    
    @GetMapping("/user")
    public User getUser() {
        User user = new User();
        user.setName("张三");
        user.setAge(25);
        return user;  // 自动转为 JSON:{"name":"张三","age":25}
    }
}

3)@ResponseBody

它的主要作用是将返回的对象,自动转换成客户端能直接接收的数据格式(通常是 JSON 或 XML),并作为 HTTP 响应的 body 内容。

相关推荐
这个DBA有点耶1 小时前
死锁排查进阶:从日志到根因的完整分析链
java·开发语言·数据库·sql·运维开发·学习方法·dba
叫我少年1 小时前
C# 文件级 using(global using)
后端
郝学胜_神的一滴1 小时前
系统设计 014:缓存深度实战:如何用 Cache 优雅优化数据库读写?
前端·后端·面试
ai程序羊沸沸1 小时前
Spring Cloud 微服务入门:从组件清单到问题驱动的学习路径
后端·微服务
JAVA9651 小时前
JAVA面试-并发篇 06-ReentrantLock如何实现公平锁的以及可重入吗
java·开发语言·面试
铁皮饭盒1 小时前
sharp.js安装不上, Bun.Image说: 我不用安装
前端·后端
无风听海1 小时前
ASP.NET Core 中的重定向(Redirect)深度解析
后端·asp.net
掘金者阿豪2 小时前
Node.js 连金仓数据库(下篇):连接池、事务和那些坑
后端
二等饼干~za8986682 小时前
geo优化系统源码搭建保姆式搭建教程
java·开发语言·django·php·音视频