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

Spring Boot 集成 MyBatis 全面讲解

在这里插入图片描述

Spring Boot 集成 MyBatis 全面讲解

MyBatis 是一款优秀的持久层框架,与 Spring Boot 集成后可以大大简化开发流程。本文将全面讲解如何在 Spring Boot 中集成 MyBatis,包括环境配置、基础操作、高级功能和最佳实践。


一、MyBatis 简介

1. SqlSession

SqlSession 是 MyBatis 的核心接口,负责执行 SQL 语句、获取映射器实例以及管理事务。

1.1 SqlSession 的创建

SqlSession 通常通过 SqlSessionFactory 获取。以下是创建 SqlSessionFactory 的典型代码:

InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {// 使用 session 进行数据库操作
}

注意:在 Spring 集成中,SqlSessionFactorySqlSession 的创建由框架管理,我们只需要通过依赖注入获取即可。


1.2 SqlSession 的常用方法

SqlSession 提供了多种方法,用于执行数据库操作:

  • 查询操作

    // 单条记录查询
    User user = session.selectOne("namespace.statementId", parameter);// 多条记录查询
    List<User> users = session.selectList("namespace.statementId", parameter);
    
  • 插入操作

    int rows = session.insert("namespace.statementId", parameter);
    
  • 更新操作

    int rows = session.update("namespace.statementId", parameter);
    
  • 删除操作

    int rows = session.delete("namespace.statementId", parameter);
    
  • 事务控制

    session.commit();  // 提交事务
    session.rollback();  // 回滚事务
    

2. Mapper 映射器

Mapper 映射器是 MyBatis 的核心功能,用于实现 SQL 和 Java 方法之间的映射。它可以通过注解或 XML 配置。

2.1 基于注解的 Mapper

注解方式直接将 SQL 写在 Mapper 接口中,简单高效,适合简单场景。

示例代码:

@Mapper
public interface UserMapper {@Select("SELECT * FROM user WHERE id = #{id}")User selectById(Long id);@Insert("INSERT INTO user (username, email) VALUES (#{username}, #{email})")int insertUser(User user);@Update("UPDATE user SET email = #{email} WHERE id = #{id}")int updateUser(User user);@Delete("DELETE FROM user WHERE id = #{id}")int deleteUser(Long id);
}

2.2 基于 XML 的 Mapper

XML 配置更加灵活,适合复杂查询场景。Mapper XML 文件通常位于 resources/mapper 目录。

Mapper XML 文件示例

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper"><!-- 查询 --><select id="selectById" parameterType="long" resultType="com.example.entity.User">SELECT * FROM user WHERE id = #{id}</select><!-- 插入 --><insert id="insertUser" parameterType="com.example.entity.User">INSERT INTO user (username, email)VALUES (#{username}, #{email})</insert><!-- 更新 --><update id="updateUser" parameterType="com.example.entity.User">UPDATE user SET email = #{email} WHERE id = #{id}</update><!-- 删除 --><delete id="deleteUser" parameterType="long">DELETE FROM user WHERE id = #{id}</delete>
</mapper>

2.3 Mapper 映射器的工作机制

Mapper 接口的方法名和参数需要与 XML 中的 idparameterType 对应。MyBatis 会通过动态代理为 Mapper 接口生成实现类,并调用对应的 SQL。


3. 配置文件

MyBatis 的配置文件包括全局配置文件(mybatis-config.xml)和映射文件(mapper.xml)。


3.1 全局配置文件

mybatis-config.xml 定义了数据库连接、日志设置、别名等全局配置。

典型配置示例

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 环境配置 --><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><!-- 别名配置 --><typeAliases><typeAlias type="com.example.entity.User" alias="User"/></typeAliases><!-- Mapper 映射文件 --><mappers><mapper resource="mapper/UserMapper.xml"/></mappers>
</configuration>

3.2 映射文件配置

映射文件定义了具体的 SQL 和 Java 对象之间的关系。以 UserMapper.xml 为例:

<mapper namespace="com.example.mapper.UserMapper"><select id="selectAll" resultType="User">SELECT * FROM user</select><resultMap id="UserResultMap" type="User"><id column="id" property="id"/><result column="username" property="username"/><result column="email" property="email"/></resultMap>
</mapper>

4. ResultMap

ResultMap 是 MyBatis 的强大特性之一,用于处理复杂查询结果与 Java 对象的映射关系。


4.1 什么是 ResultMap?

ResultMap 用于自定义数据库字段与 Java 对象属性的映射。它支持嵌套映射、别名和字段处理,适合复杂的对象映射场景。


4.2 ResultMap 配置示例

以下是一个带嵌套对象的 ResultMap 配置:

数据库表:
CREATE TABLE user (id BIGINT PRIMARY KEY,username VARCHAR(50),email VARCHAR(100)
);CREATE TABLE address (id BIGINT PRIMARY KEY,user_id BIGINT,city VARCHAR(50),FOREIGN KEY (user_id) REFERENCES user(id)
);
Java 对象:
@Data
public class User {private Long id;private String username;private String email;private Address address;
}@Data
public class Address {private Long id;private String city;
}
ResultMap 配置:
<resultMap id="UserWithAddress" type="User"><id column="id" property="id"/><result column="username" property="username"/><result column="email" property="email"/><association property="address" javaType="Address"><id column="address_id" property="id"/><result column="city" property="city"/></association>
</resultMap>
查询语句:
<select id="getUserWithAddress" resultMap="UserWithAddress">SELECT u.id, u.username, u.email, a.id AS address_id, a.cityFROM user uLEFT JOIN address a ON u.id = a.user_idWHERE u.id = #{id}
</select>

4.3 嵌套集合映射

对于一对多的嵌套关系,可以使用 <collection>

<resultMap id="UserWithPosts" type="User"><id column="id" property="id"/><result column="username" property="username"/><collection property="posts" ofType="Post"><id column="post_id" property="id"/><result column="title" property="title"/></collection>
</resultMap>

总结

SqlSessionMapper配置文件ResultMap 是 MyBatis 的核心概念。通过灵活的配置和映射,MyBatis 可以高效地处理各种复杂的数据库操作需求。熟练掌握这些特性可以让开发者在项目中更高效地处理数据访问逻辑。


三、Spring Boot 集成 MyBatis

MyBatis 是一种轻量级的持久层框架,与 Spring Boot 集成后可以极大地提升开发效率。以下是集成的完整步骤,包括项目配置、数据库设计和基本操作。


1. 创建 Spring Boot 项目

在创建项目时,可以使用 Spring Initializr 快速生成骨架项目。以下依赖是集成 MyBatis 所必需的:

  • Spring Web:用于创建 REST API。
  • MyBatis Framework:MyBatis 的核心依赖。
  • MySQL Driver:连接 MySQL 数据库。
  • Lombok:简化实体类的开发,减少样板代码。

2. 配置 pom.xml

以下是需要在 pom.xml 中添加的 Maven 依赖:

<dependencies><!-- Spring Boot Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- MyBatis Starter --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.0</version></dependency><!-- MySQL Driver --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>
</dependencies>

这些依赖包括 Spring Boot 核心、MyBatis 框架、MySQL 数据库驱动和 Lombok。版本号可以根据项目需求进行调整。


3. 配置数据库连接

src/main/resources 目录下创建 application.yml 文件,用于配置项目的数据库连接。

application.yml 示例
spring:datasource:url: jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&serverTimezone=UTCusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Drivermybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.example.demo.entity

说明

  • url:数据库连接地址。
  • usernamepassword:数据库的用户名和密码。
  • mapper-locations:指定 MyBatis 的 XML 映射文件路径。
  • type-aliases-package:指定实体类所在的包,用于启用简化的类名映射。

4. 创建数据库表

使用以下 SQL 语句创建一个简单的用户表:

SQL 示例
CREATE DATABASE IF NOT EXISTS mybatis_demo;USE mybatis_demo;CREATE TABLE user (id BIGINT PRIMARY KEY AUTO_INCREMENT,username VARCHAR(50) NOT NULL,password VARCHAR(50) NOT NULL,email VARCHAR(100) NOT NULL,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

此 SQL 创建了一个名为 user 的表,用于存储用户信息。字段包括用户 ID、用户名、密码、电子邮件以及创建时间。


5. 编写实体类

创建与数据库表对应的 Java 实体类。

User.java
package com.example.demo.entity;import lombok.Data;
import java.time.LocalDateTime;@Data
public class User {private Long id;private String username;private String password;private String email;private LocalDateTime createdAt;
}

说明

  • 使用了 Lombok 的 @Data 注解,自动生成 gettersettertoString 等方法。
  • 字段名称与数据库表的列名保持一致,便于自动映射。

6. 创建 Mapper 接口

MyBatis 的 Mapper 接口用于定义数据库操作。可以选择使用注解方式或者 XML 配置方式编写 SQL。

注解方式 Mapper

以下是一个基于注解的 Mapper 接口示例:

package com.example.demo.mapper;import com.example.demo.entity.User;
import org.apache.ibatis.annotations.*;import java.util.List;@Mapper
public interface UserMapper {@Insert("INSERT INTO user (username, password, email) VALUES (#{username}, #{password}, #{email})")int insertUser(User user);@Select("SELECT * FROM user WHERE id = #{id}")User selectById(Long id);@Select("SELECT * FROM user")List<User> selectAllUsers();@Update("UPDATE user SET username = #{username}, password = #{password}, email = #{email} WHERE id = #{id}")int updateUser(User user);@Delete("DELETE FROM user WHERE id = #{id}")int deleteUser(Long id);
}

XML 配置方式 Mapper

XML 配置方式更灵活,适合复杂查询场景。以下是对应的 XML 映射文件。

文件位置src/main/resources/mapper/UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper"><insert id="insertUser" parameterType="com.example.demo.entity.User">INSERT INTO user (username, password, email)VALUES (#{username}, #{password}, #{email})</insert><select id="selectById" parameterType="long" resultType="com.example.demo.entity.User">SELECT * FROM user WHERE id = #{id}</select><select id="selectAllUsers" resultType="com.example.demo.entity.User">SELECT * FROM user</select><update id="updateUser" parameterType="com.example.demo.entity.User">UPDATE user SET username = #{username}, password = #{password}, email = #{email} WHERE id = #{id}</update><delete id="deleteUser" parameterType="long">DELETE FROM user WHERE id = #{id}</delete></mapper>

在 Spring Boot 中,MyBatis 会自动扫描 mapper 文件夹下的 XML 文件。


7. 创建 Service 层

为了更好地分离业务逻辑,建议将 Mapper 操作封装到 Service 层中。

UserService.java
package com.example.demo.service;import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class UserService {private final UserMapper userMapper;public UserService(UserMapper userMapper) {this.userMapper = userMapper;}public int createUser(User user) {return userMapper.insertUser(user);}public User getUserById(Long id) {return userMapper.selectById(id);}public List<User> getAllUsers() {return userMapper.selectAllUsers();}public int updateUser(User user) {return userMapper.updateUser(user);}public int deleteUser(Long id) {return userMapper.deleteUser(id);}
}

8. 创建 Controller 层

最后,为了提供对外接口,创建 Controller。

UserController.java
package com.example.demo.controller;import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
@RequestMapping("/api/users")
public class UserController {private final UserService userService;public UserController(UserService userService) {this.userService = userService;}@PostMappingpublic String createUser(@RequestBody User user) {userService.createUser(user);return "User created successfully!";}@GetMapping("/{id}")public User getUserById(@PathVariable Long id) {return userService.getUserById(id);}@GetMappingpublic List<User> getAllUsers() {return userService.getAllUsers();}@PutMappingpublic String updateUser(@RequestBody User user) {userService.updateUser(user);return "User updated successfully!";}@DeleteMapping("/{id}")public String deleteUser(@PathVariable Long id) {userService.deleteUser(id);return "User deleted successfully!";}
}

9. 启动应用

创建项目主类 MyBatisDemoApplication.java

package com.example.demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class MyBatisDemoApplication {public static void main(String[] args) {SpringApplication.run(MyBatisDemoApplication.class, args);}
}

启动项目,使用工具(如 Postman 或 CURL)测试接口。


四、MyBatis 基础操作详解

以下将详细讲解 MyBatis 的基础操作,包括如何创建实体类、Mapper 接口、XML 映射文件,以及如何通过 Service 和 Controller 层完成基础的增删改查功能。


1. 创建实体类

实体类用于表示数据库中的表记录,在 MyBatis 中,实体类字段与数据库表的列进行一一对应。

示例代码:User.java
package com.example.demo.entity;import lombok.Data;
import java.time.LocalDateTime;@Data
public class User {private Long id;             // 用户IDprivate String username;     // 用户名private String password;     // 密码private String email;        // 邮箱private LocalDateTime createdAt; // 创建时间
}

说明

  • 使用 @Data 注解自动生成 gettersettertoString 等方法。
  • 字段名称与数据库表的列名保持一致,便于 MyBatis 自动映射。

2. 创建 Mapper 接口

Mapper 接口定义了对数据库表的操作。MyBatis 支持两种方式:基于注解和基于 XML 映射文件。

基于注解的 Mapper 接口

以下是使用注解定义的基础增删改查操作:

package com.example.demo.mapper;import com.example.demo.entity.User;
import org.apache.ibatis.annotations.*;import java.util.List;@Mapper
public interface UserMapper {// 插入用户@Insert("INSERT INTO user (username, password, email) VALUES (#{username}, #{password}, #{email})")int insertUser(User user);// 查询所有用户@Select("SELECT * FROM user")List<User> getAllUsers();// 根据 ID 查询用户@Select("SELECT * FROM user WHERE id = #{id}")User getUserById(Long id);// 更新用户@Update("UPDATE user SET username = #{username}, password = #{password}, email = #{email} WHERE id = #{id}")int updateUser(User user);// 删除用户@Delete("DELETE FROM user WHERE id = #{id}")int deleteUser(Long id);
}

注意

  • 使用 @Mapper 注解让 Spring 容器自动扫描 Mapper 接口。
  • 注解方式适合简单的 SQL 语句,对于复杂查询建议使用 XML。

3. 配置 XML 映射文件

在复杂查询场景中,XML 配置文件更加灵活。

文件位置
src/main/resources/mapper/UserMapper.xml
UserMapper.xml 示例
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.example.demo.mapper.UserMapper"><!-- 定义字段与属性的映射 --><resultMap id="UserResultMap" type="com.example.demo.entity.User"><id column="id" property="id" /><result column="username" property="username" /><result column="password" property="password" /><result column="email" property="email" /><result column="created_at" property="createdAt" /></resultMap><!-- 查询所有用户 --><select id="getAllUsers" resultMap="UserResultMap">SELECT * FROM user</select><!-- 插入用户 --><insert id="insertUser" parameterType="com.example.demo.entity.User">INSERT INTO user (username, password, email)VALUES (#{username}, #{password}, #{email})</insert><!-- 更新用户 --><update id="updateUser" parameterType="com.example.demo.entity.User">UPDATE userSET username = #{username}, password = #{password}, email = #{email}WHERE id = #{id}</update><!-- 删除用户 --><delete id="deleteUser" parameterType="long">DELETE FROM user WHERE id = #{id}</delete></mapper>

注意

  • namespace 必须与 Mapper 接口的全路径名称一致。
  • <resultMap> 定义了表字段与实体类属性之间的映射关系。
  • #{} 用于参数占位,MyBatis 会根据参数类型自动替换。

4. 创建 Service 层

为了实现业务逻辑与数据访问的分离,建议通过 Service 层封装 Mapper 的操作。

示例代码:UserService.java
package com.example.demo.service;import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class UserService {private final UserMapper userMapper;public UserService(UserMapper userMapper) {this.userMapper = userMapper;}// 添加用户public int addUser(User user) {return userMapper.insertUser(user);}// 获取所有用户public List<User> getAllUsers() {return userMapper.getAllUsers();}// 根据 ID 查询用户public User getUserById(Long id) {return userMapper.getUserById(id);}// 更新用户public int updateUser(User user) {return userMapper.updateUser(user);}// 删除用户public int deleteUser(Long id) {return userMapper.deleteUser(id);}
}

说明

  • 通过依赖注入的方式引入 UserMapper
  • 将所有的数据库操作封装为独立的方法,便于管理和复用。

5. 创建 Controller 层

Controller 层提供 RESTful API 接口,供外部访问 Service 方法。

示例代码:UserController.java
package com.example.demo.controller;import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
@RequestMapping("/api/users")
public class UserController {private final UserService userService;public UserController(UserService userService) {this.userService = userService;}// 创建用户@PostMappingpublic String createUser(@RequestBody User user) {userService.addUser(user);return "User created successfully!";}// 获取所有用户@GetMappingpublic List<User> getAllUsers() {return userService.getAllUsers();}// 根据 ID 获取用户@GetMapping("/{id}")public User getUserById(@PathVariable Long id) {return userService.getUserById(id);}// 更新用户@PutMappingpublic String updateUser(@RequestBody User user) {userService.updateUser(user);return "User updated successfully!";}// 删除用户@DeleteMapping("/{id}")public String deleteUser(@PathVariable Long id) {userService.deleteUser(id);return "User deleted successfully!";}
}

说明

  • 使用 @RestController 标注类,返回 JSON 数据。
  • 通过 @RequestBody 接收前端传递的 JSON 数据。
  • 通过 @PathVariable 获取 URL 中的动态参数。

五、高级功能

1. 动态 SQL

动态 SQL 是 MyBatis 的强大功能之一,可以根据输入条件动态生成 SQL 语句。相比手动拼接 SQL,这种方式更加安全、高效且可维护。

1.1 动态 SQL 标签

MyBatis 提供了以下动态 SQL 标签:

  1. <if>:用于条件判断。
  2. <choose>:类似于 Java 的 switch-case
  3. <where>:自动添加 WHERE 关键字并处理多个条件。
  4. <set>:动态生成 SET 子句,常用于更新语句。
  5. <foreach>:用于迭代生成 SQL(如 IN 子句或批量插入)。
  6. <trim>:自定义 SQL 前后缀(如添加括号、处理多余逗号等)。

1.2 动态 SQL 示例

(1)条件查询:根据用户输入动态生成查询条件

XML 配置文件:

<select id="searchUsers" resultMap="UserResultMap">SELECT * FROM user<where><if test="username != null">AND username = #{username}</if><if test="email != null">AND email = #{email}</if></where>
</select>

注意<where> 标签会自动处理条件的拼接,并在至少有一个条件成立时自动添加 WHERE 关键字。

Java 调用代码:

Map<String, Object> params = new HashMap<>();
params.put("username", "John");
params.put("email", null);List<User> users = userMapper.searchUsers(params);

(2)动态更新:根据非空字段更新用户信息

在实际场景中,往往需要对部分字段进行更新,MyBatis 的动态 SQL 可以轻松实现。

XML 配置文件:

<update id="updateUser" parameterType="User">UPDATE user<set><if test="username != null">username = #{username},</if><if test="password != null">password = #{password},</if><if test="email != null">email = #{email},</if></set>WHERE id = #{id}
</update>

注意

  • <set> 标签会自动处理逗号,确保生成的 SQL 语句语法正确。
  • null 值的字段会被忽略,避免误更新。

Java 调用代码:

User user = new User();
user.setId(1L);
user.setUsername("NewName");int rows = userMapper.updateUser(user);

(3)批量查询:使用 <foreach> 生成 IN 子句

XML 配置文件:

<select id="findUsersByIds" resultMap="UserResultMap">SELECT * FROM userWHERE id IN<foreach collection="idList" item="id" open="(" separator="," close=")">#{id}</foreach>
</select>

说明

  • collection 指定输入参数(一般为 List 或数组)。
  • item 是每次迭代的变量。
  • openseparatorclose 分别定义 SQL 子句的开头、分隔符和结尾。

Java 调用代码:

List<Long> idList = Arrays.asList(1L, 2L, 3L);
List<User> users = userMapper.findUsersByIds(idList);

生成的 SQL:

SELECT * FROM user WHERE id IN (1, 2, 3);

2. 分页查询

分页查询是 Web 应用中最常见的功能之一。在 MyBatis 中,可以借助 PageHelper 插件实现高效分页。


2.1 使用 PageHelper 插件

(1)引入依赖

pom.xml 中添加以下依赖:

<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.0</version>
</dependency>

(2)分页查询示例

在 Service 层调用分页方法:

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;public List<User> getUsersByPage(int pageNum, int pageSize) {// 启用分页PageHelper.startPage(pageNum, pageSize);List<User> users = userMapper.getAllUsers();// 封装分页结果return new PageInfo<>(users).getList();
}

(3)自定义分页

如果不想引入插件,也可以通过手动拼接分页 SQL:

XML 配置文件:

<select id="getUsersByPage" resultMap="UserResultMap">SELECT * FROM userLIMIT #{offset}, #{pageSize}
</select>

Mapper 接口:

List<User> getUsersByPage(@Param("offset") int offset, @Param("pageSize") int pageSize);

Java 调用代码:

int offset = (pageNum - 1) * pageSize;
List<User> users = userMapper.getUsersByPage(offset, pageSize);

3. 复杂对象映射

3.1 一对多映射

场景:一个用户有多个订单。

数据库表设计:

CREATE TABLE orders (id BIGINT PRIMARY KEY,user_id BIGINT,order_name VARCHAR(255),FOREIGN KEY (user_id) REFERENCES user(id)
);

XML 配置文件:

<resultMap id="UserWithOrders" type="User"><id column="id" property="id"/><result column="username" property="username"/><collection property="orders" ofType="Order"><id column="order_id" property="id"/><result column="order_name" property="orderName"/></collection>
</resultMap><select id="getUserWithOrders" resultMap="UserWithOrders">SELECT u.id, u.username, o.id AS order_id, o.order_nameFROM user uLEFT JOIN orders o ON u.id = o.user_idWHERE u.id = #{id}
</select>

3.2 嵌套查询

对于复杂的多表查询,可以使用嵌套查询实现。

XML 配置:

<resultMap id="OrderResultMap" type="Order"><id column="id" property="id"/><result column="order_name" property="orderName"/>
</resultMap><resultMap id="UserWithOrders" type="User"><id column="id" property="id"/><result column="username" property="username"/><collection property="orders" resultMap="OrderResultMap" column="id"/>
</resultMap><select id="getUserWithOrders" resultMap="UserWithOrders">SELECT * FROM user WHERE id = #{id};
</select>

六、最佳实践

1. 分层设计
  • Controller 层:负责接收请求和返回响应。
  • Service 层:封装业务逻辑。
  • Mapper 层:专注于数据库交互。
2. 避免 N+1 查询

一对多、多对多场景中,优先使用联合查询或嵌套查询,避免多个 SQL 执行。

3. 启用日志

application.yml 中启用 MyBatis 日志:

mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
4. 动态 SQL
  • 使用 <foreach> 实现批量操作。
  • 使用 <if> 结合 <set> 实现动态更新。

在这里插入图片描述

总结

MyBatis 的高级功能如动态 SQL、分页查询和复杂对象映射,为开发者提供了极大的灵活性。在项目中,结合实际场景选择合适的实现方式,可以显著提高开发效率并降低维护成本。如果有任何疑问,欢迎在评论区留言讨论!
在这里插入图片描述

相关文章:

Spring Boot 集成 MyBatis 全面讲解

Spring Boot 集成 MyBatis 全面讲解 MyBatis 是一款优秀的持久层框架&#xff0c;与 Spring Boot 集成后可以大大简化开发流程。本文将全面讲解如何在 Spring Boot 中集成 MyBatis&#xff0c;包括环境配置、基础操作、高级功能和最佳实践。 一、MyBatis 简介 1. SqlSession …...

SpringBoot | SpringBoot原理分析

SpringBoot原理分析&#xff08;一&#xff09;.jpg SpringBoot原理分析&#xff08;二&#xff09;.jpg 核心要点&#xff1a; 1、SpringBootApplication 2、SpringBootConfiguration 3、ComponentScan 4、EnableAutoConfiguration 5、AutoConfigurationPackages 6、import(Au…...

解决“VMware虚拟机报Intel VT-x”错误

今天&#xff0c;在windows系统上&#xff0c;打开VMware WorkStation v15软件里的虚拟机&#xff0c;弹出"Intel VT-x处于禁用状态"错误&#xff0c;如图(1)所示&#xff1a; 图(1) 虚拟机报"Intel VT-x"错误 问题原因&#xff1a;当前电脑的BIOS没有开启…...

LeetCode200.岛屿数量

题目 给你一个由 ‘1’&#xff08;陆地&#xff09;和 ‘0’&#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。 岛屿总是被水包围&#xff0c;并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外&#xff0c;你可以假设…...

python学opencv|读取图像(七)抓取像素数据顺利修改图像大小

【1】引言 前序我们已经学习图像的基本读取操作&#xff0c;文章链接为&#xff1a; python学opencv|读取图像-CSDN博客 也掌握了彩色图像的保存&#xff1a; python学opencv|读取图像&#xff08;二&#xff09;保存彩色图像_python opencv 读取图像转为彩色-CSDN博客 以…...

Qt Pro 常用配置

Part1: Summary Qt 开发中 Pro 文件的内容很多&#xff0c;需要不断的去学习和使用&#xff0c;现系统性的整理一下。以备录&#xff1b; 1.创建pro文件 1.1 步骤&#xff1a; Qt Creator--->New Project--->应用程序--->Qt Widgets Application--->名称为&…...

源码分析之Openlayers中默认Controls控件渲染原理

概述 Openlayers 中默认的三类控件是Zoom、Rotate和Attribution 源码分析 defaults方法 Openlayers 默认控件的集成封装在defaults方法中&#xff0c;该方法会返回一个Collection的实例&#xff0c;Collection是一个基于数组封装了一些方法&#xff0c;主要涉及到数组项的添…...

银河麒麟桌面操作系统添加WPS字体

【使用场景】 银河麒麟桌面操作系统支持添加WPS字体。在银河麒麟桌面操作系统中使用WPS软件编辑文档时存在需要添加WPS字体的情况,例如字体缺失或者需要特殊字体时,需要添加WPS字体。 【操作方法】 步骤一:在互联网上搜索并下载.ttf格式的字体文件。 步骤二:下载完成后,在…...

利用Python实现多元回归预测汽车价格

引言: AI技术的热门使得大家对机器学习有了更多的关注,作为与AI技术息息相关的一门课程,从头了解基础的机器学习算法就显得十分有必要,如:梯度下降,线性回归等。 正文: 本文将讲解线性回归中多元回回归的案例 机器学习大致可以分为监督学习,非监督学习、半监督学习还…...

16、PyTorch中进行卷积残差模块算子融合

文章目录 1. 1x1卷积核-> 3x3卷积核2. 输入x --> 3x3卷积核&#xff0c;无变化3. 代码 1. 1x1卷积核-> 3x3卷积核 假设我们有一个1x1的卷积核&#xff0c;需要通过填充变为一个3x3的卷积核,实现的是像素之间无关联 [ 4 ] → [ 0 0 0 0 4 0 0 0 0 ] \begin{equation}…...

CMake简单使用(二)

目录 五、scope 作用域5.1 作用域的类型5.1.1 全局作用域5.1.2 目录作用域5.1.3 函数作用域 六、宏6.1 基本语法6.2 演示代码 七、CMake构建项目7.1 全局变量7.2 写入源码路径7.3 调用子目录cmake脚本7.4 CMakeLists 嵌套(最常用) 八、CMake 与库8.1 CMake生成动静态库8.1.1 动…...

React和Vue中暴露子组件的属性和方法给父组件用,并且控制子组件暴露的颗粒度的做法

React 在 React 中&#xff0c;forwardRef 是一种高级技术&#xff0c;它允许你将 ref 从父组件传递到子组件&#xff0c;从而直接访问子组件的 DOM 节点或公开的方法。这对于需要操作子组件内部状态或 DOM 的场景非常有用。为了使子组件能够暴露其属性和方法给父组件&#xf…...

MATLAB 识别色块和数量

文章目录 前言步骤 1: 读取图像步骤 2: 转换为 HSV 颜色空间步骤 3: 定义颜色范围步骤 4: 创建颜色掩码步骤 5: 应用形态学操作&#xff08;可选&#xff09;步骤 6: 标记和显示结果完整代码步骤七 返回色块坐标 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&…...

.NET 9 已发布,您可以这样升级或更新

.NET 9 已经发布&#xff0c;您可能正在考虑更新您的 ASP.NET Core 应用程序。 我们将介绍更新应用程序所需的内容。从更新 Visual Studio 和下载 .NET SDK 到找出可能破坏应用程序的任何重大更改。 下载 .NET 9 SDK 这些是下载 .NET 9 SDK 所需的步骤。 更新 Visual Studi…...

VMware ubuntu16.04怎么设置静态IP联网

1.将VMware桥接到当前电脑使用的网络上面&#xff1b; 2.点击网络符号&#xff0c;编辑连接&#xff1b; 3.双击有线连接1&#xff1b; 4.选择IPv4设置&#xff0c;将地址&#xff0c;子网掩码&#xff0c;网关&#xff0c;DNS服务器设置好&#xff0c;保存&#xff1b; 5.在终…...

#渗透测试#漏洞挖掘#红蓝攻防#js分析(上)

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…...

.NET 6.0 中接入 Log4net 和 NLog

一、接入Log4net 1.按日期和大小混合分割日志 nuget包安装 log4net Microsoft.Extensions.Logging.Log4Net.AspNetCore 配置文件 配置文件内容为 <?xml version"1.0" encoding"utf-8"?> <log4net> <!-- Define some output appe…...

linux从frame buffer中将qt界面拷贝出来放到u盘的操作方法

使用的是gsnap工具&#xff0c;源码可以在百度上搜,以Imx6为例的使用方法 rootimx6qsabresd:~# rootimx6qsabresd:~# rootimx6qsabresd:~# df Filesystem 1K-blocks Used Available Use% Mounted on /dev/root 289293 197510 76423 73% / devtmpfs …...

最新全开源IM即时通讯系统源码(PC+WEB+IOS+Android)部署指南

全开源IM&#xff08;即时通讯&#xff09;系统源码部署是一个复杂但系统的过程&#xff0c;涉及多个组件和步骤。以下是一个详细的部署指南&#xff0c;旨在帮助开发者或系统管理员成功部署一个全开源的IM系统&#xff0c;如OpenIM。      IM即时通讯系统源码准备工作   …...

使用Linux的logrotate工具切割日志:Tomcat、NGINX(journal文件清理)

文章目录 引言I Tomcat日志切割配置轮转参数验证码II NGINX访问文件的配置和切割access.log 访问日志的配置使用Linux的logrotate工具切割日志验证文件切割III /run/log/journaljournalctl文件清理引言 journal文件清理: 只保留过去两天,清理之前的文件 journalctl --vacuu…...

shell脚本1

运行脚本 1、先创建一个sh脚本文件,里面输入一个输出网站的命令&#xff0c;比如echo www.baidu.com vim 1.sh2、可以利用以下三种方式、这三种其实不管哪种脚本解释器最后调用的还是这个dash,下面是解释为什么调用都是dash&#xff0c;我们ls可以发现最后目录都是指向了dash…...

Qt-chart 画折线图(以时间为x轴)

上图 代码 #include <iostream> #include <random> #include <qcategoryaxis.h>void MainWindow::testLine() {//1、创建图表视图QChartView* view new QChartView(this);//2.创建图表QChart* chart new QChart();//3.将图表设置给图表视图view->setCh…...

【kubernetes】kubectl get nodes报NotReady

目录 1. 说明2. 问题描述3. kube-flannel.yml 1. 说明 1.这里k8s的版本是v1.17.4。2.若kube-flannel.yml中的镜像拉取不下来&#xff0c;可以下载本文章的文件资源&#xff0c;手动docker load -i ***.tar的方式。3.v1.17.4的kube-flannel.yml参考下面代码。4.通过kubectl get…...

分布式开发学习

1、kratos的特点 gRPC&#xff1a;Kratos 默认支持 gRPC&#xff0c;提供高性能的远程调用能力&#xff0c;适用于微服务间通信。 HTTP &#xff1a;同时支持 HTTP/1.1 和 HTTP/2&#xff0c;方便微服务与外部系统交互。 Protocol Buffers&#xff1a; protoc 工具生…...

软件测试的几种方法详解

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、从是否关心内部结构来看 (1)白盒测试&#xff1a;又称为结构测试或逻辑驱动测试&#xff0c;是一种按照程序内部逻辑结构和编码结构&#xff0c;设计测试数据并…...

C语言---int r 与 int r=数的区别

int r 在这里面声明了一个变量r&#xff0c;创建了内存空间 int r 2 声明了一个整数类型的变量r&#xff0c;并给它赋值&#xff08;初始值&#xff09;为2&#xff0c;创建内存空间并赋值。...

matlab测试ADC动态性能的原理

目录 摘要&#xff1a; 简介&#xff1a; 动态规范和定义 动态规格&#xff1a; 双面到单边的功率谱转换 摘要&#xff1a; 模数转换器&#xff08;adc&#xff09;代表了接收器、测试设备和其他电子设备中的模拟世界和数字世界之间的联系。正如本文系列的第1部分中所概述…...

XDOJ 877 图的深度优先遍历

题目&#xff1a;图的深度优先遍历 问题描述 已知无向图的邻接矩阵&#xff0c;以该矩阵为基础&#xff0c;给出深度优先搜索遍历序列&#xff0c;并且给出该无向图的连通分量的个数。在遍历时&#xff0c;当有多个点可选时&#xff0c;优先选择编号小的顶点。&#xff08;即…...

内网穿透讲解

什么是内网穿透 内网穿透是一种网络技术&#xff0c;它允许外网或者其他局域网的用户来访问这个局域网的服务器资源&#xff0c;让资源的利用率更高&#xff0c;更加灵活&#xff0c;但是也要确保网络安全。 工作原理 如果你在公司&#xff0c;但是你需要使用到你家里的那台电…...

怎么规划一个呼叫中心大模型呼入部门?设置哪些岗位?

怎么规划一个呼叫中心大模型呼入部门&#xff1f;设置哪些岗位&#xff1f; 原作者&#xff1a;开源呼叫中心FreeIPCC&#xff0c;其Github&#xff1a;https://github.com/lihaiya/freeipcc 规划一个呼叫中心大模型呼入部门是一个复杂而细致的过程&#xff0c;涉及多个层面的…...

企业级Nginx Web服务优化实战(上)

一 &#xff0c;Nginx基本安全优化 1.1 调整参数隐藏Nginx软件版本号信息 一般来说 &#xff0c;软件的漏洞都和版本有关 &#xff0c;这个很像汽车的缺陷 &#xff0c;同一批次的要有问题就 都有问题 &#xff0c;别的批次可能就都是好的。 因此 &#xff0c;我们应尽量隐藏或…...

Redisson常用方法

Redisson 参考: 原文链接 定义&#xff1a;Redisson 是一个用于与 Redis 进行交互的 Java 客户端库 优点&#xff1a;很多 1. 入门 1.1 安装 <!--redission--> <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifa…...

【树莓派4B】MindSpore lite 部署demo

一个demo&#xff0c;mindspore lite 部署在树莓派4B ubuntu22.04中&#xff0c;为后续操作开个门&#xff01; 环境 开发环境&#xff1a;wsl-ubuntu22.04分发版部署环境&#xff1a;树莓派4B&#xff0c;操作系统为ubuntu22.04mindspore lite版本&#xff1a;mindspore-li…...

ESP32-S3模组上跑通ES8388(30)

接前一篇文章:ESP32-S3模组上跑通ES8388(29) 二、利用ESP-ADF操作ES8388 2. 详细解析 上一回终于解析完了es8388_init函数的所有代码。本回回到调用它的地方,继续往下讲解。 我们是从ESP32-S3模组上跑通ES8388(7)-CSDN博客开始进入es8388_init函数,展开对于它的解析的…...

网络安全渗透测试概论

渗透测试&#xff0c;也称为渗透攻击测试是一种通过模拟恶意攻击者的手段来评估计算机系统、网络或应用程序安全性的方法。 目的 旨在主动发现系统中可能存在的安全漏洞、脆弱点以及潜在风险&#xff0c;以便在被真正的恶意攻击者利用之前&#xff0c;及时进行修复和加固&…...

.NET6 WebAPI从基础到进阶--朝夕教育

1、环境准备 1. Visual Studio 2022 2. .NET6 平台支持 3. Internet Information Services 服务器&#xff08; IIS &#xff09; 4. Linux 服务器 【 CentOS 系统】 ( 跨平台部署使用 ) 5. Linux 服务器下的 Docker 容器&#xff08; Docker 部署使用&#xff09; …...

opencv通过3种算子进行边缘提取

Scharr算子是效果比较好的,但从肉眼看sobel算子比较顺眼 # 导入OpenCV库&#xff0c;用于图像处理 import cv2 import numpy as np # 从matplotlib库中导入pyplot模块&#xff0c;用于绘制图像 from matplotlib import pyplot as plt # 创建一个名为window的窗口&#xff0c;窗…...

【KodExplorer】可道云KodExplorer-个人网盘安装使用

说明&#xff1a;安装kodExplorer &#xff08;不是Kodbox&#xff09;&#xff1b;Kodbox需求服务器至少2核4G内存&#xff0c;要求环境具备php/redis/mysql/。安装kodExplorer 就是比较方便简单部署&#xff0c;个人版免费。 一、安装环境需求 服务器: Windows&#xff0c;…...

并查集基础

abstract 并查集&#xff08;Union-Find Set&#xff09;是一种数据结构&#xff0c;主要用于处理动态连通性问题&#xff08;Dynamic Connectivity Problem&#xff09;&#xff0c;例如在图论中判断两点是否属于同一个连通分量&#xff0c;以及动态地合并集合。 它广泛应用…...

FPGA 16 ,Verilog中的位宽:深入理解与应用

目录 前言 一. 位宽的基本概念 二. 位宽的定义方法 1. 使用向量变量定义位宽 ① 向量类型及位宽指定 ② 位宽范围及位索引含义 ③ 存储数据与字节数据 2. 使用常量参数定义位宽 3. 使用宏定义位宽 4. 使用[:][-:]操作符定义位宽 1. 详细解释 : 操作符 -: 操作符 …...

day2 数据结构 结构体的应用

思维导图 小练习&#xff1a; 定义一个数组&#xff0c;用来存放从终端输入的5个学生的信息【学生的信息包含学生的姓名、年纪、性别、成绩】 1>封装函数 录入5个学生信息 2>封装函数 显示学生信息 3>封装函数 删除第几个学生信息&#xff0c;删除后调用显示学…...

原创 传奇996_55——后端如何点击npc隐藏主界面

点击图片退出&#xff0c;举例&#xff1a; |linkexit Img|ax0.5|ay0.5|percentx50|percenty50|imgpublic/touming2.png|hideMain1|linkexit <Img|x0|y0|esc1|show4|bg1|move0|imgcustom/new/longhun/bg.png|loadDelay0|reset1|hideMain1>...

java+springboot+mysql法律咨询网

项目介绍&#xff1a; 使用javaspringbootmysql开发的法律咨询网&#xff08;文书&#xff09;&#xff0c;系统包含管理员、用户角色&#xff0c;功能如下&#xff1a; 管理员&#xff1a;登录系统&#xff1b;用户管理&#xff1b;文章管理&#xff08;法律知识&#xff09…...

【AIGC】如何高效使用ChatGPT挖掘AI最大潜能?26个Prompt提问秘诀帮你提升300%效率的!

还记得第一次使用ChatGPT时&#xff0c;那种既兴奋又困惑的心情吗&#xff1f;我是从一个对AI一知半解的普通用户&#xff0c;逐步成长为现在的“ChatGPT大神”。这一过程并非一蹴而就&#xff0c;而是通过不断的探索和实践&#xff0c;掌握了一系列高效使用的技巧。今天&#…...

HarmonyOS-高级(四)

文章目录 应用开发安全应用DFX能力介绍HiLog使用指导HiAppEvent &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;HarmonyOS专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年12月11日11点18分 应用开发安全 应用隐私保护 隐私声明弹窗的作…...

国科大智能设备安全-APK逆向分析实验

APK逆向分析实验 使用APK常用逆向分析工具&#xff0c;对提供的移动应用程序APK文件进行逆向分析&#xff0c;提交逆向后代码和分析报告。具体任务如下&#xff1a; 任务一&#xff1a;安装并熟悉Apktool、Jadx等APK常用逆向工具的使用方法&#xff0c;对提供的Facebook Updat…...

Batch Normalization和 Layer Normalization

Batch Normalization和 Layer Normalization Batch Normalization (BN) 和 Layer Normalization (LN) 是深度学习中常用的归一化技术&#xff0c;它们的主要目的是加速训练、提高模型的收敛速度和稳定性。以下是对这两种归一化技术的详细讲解&#xff1a; 1. Batch Normalizat…...

数据结构——顺序表

顺序表的简单介绍 顺序表的概念&#xff1a;顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存储。在数组上完成数据的增删查改。 特点&#xff1a;顺序表的特点是可以通过元素的索引快速访问数据&#xff0c;其访问时间复杂度…...

C语言中互斥锁、信号量和条件变量的所有相关函数、参数、返回值和说明

1. 互斥锁&#xff08;Mutex&#xff09;的使用 互斥锁用于保护共享数据&#xff0c;防止多个线程同时修改它。 #include <pthread.h> #include <stdio.h> #include <stdlib.h> // 定义一个全局共享变量 int shared_counter 0; // 定义一个互斥锁 pthrea…...

(前序 简单)leetcode 226翻转二叉树

代码随想录说用前序遍历和后序遍历方便&#xff0c;而中序遍历比较绕。 活用自定义函数使程序结构更为清晰 这里用的是前序遍历&#xff0c;根结点root指向left和root&#xff0c;交换root的左右指向 执行过程&#xff1a; 也就是交换下例的 2 &#xff0c;7 使得交换后&…...