spring mybatis(保姆级教程)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

前言:Spring 与 MyBatis 的协同之道

在 Java 后端开发领域,Spring MyBatis 是一套被广泛采用的技术组合。Spring 框架以其强大的生态和模块化设计,为开发者提供了 IoC(控制反转)和 AOP(面向切面编程)等核心能力;而 MyBatis 则以灵活的 SQL 映射机制,解决了传统 ORM 框架(如 Hibernate)在复杂查询场景下的不足。两者的结合,既保留了 Spring 的优雅架构,又赋予了 MyBatis 的“精准控制”,成为企业级应用开发的黄金搭档。

本文将从零开始,逐步讲解如何通过 Spring 和 MyBatis 构建一个完整的数据访问层(DAO),并结合实战案例,帮助读者理解其核心原理与最佳实践。


核心概念:Spring 与 MyBatis 的角色定位

1. Spring 的职责

Spring 框架的核心在于“解耦”与“管理”。它通过以下方式简化开发:

  • IoC 容器:负责对象的创建、初始化和依赖注入,避免手动管理对象生命周期。
  • AOP 支持:将日志、事务、安全等横切逻辑从业务代码中分离,提升代码复用性。
  • 模块化设计:提供 Spring MVC(Web 层)、Spring Data(数据层)、Spring Security(安全层)等模块,实现分层解耦。

比喻:如果将系统比作一支交响乐团,Spring 就是指挥家,负责协调各个模块(乐手)的协作,确保整体流程顺畅。

2. MyBatis 的职责

MyBatis 是一款“半自动化”的 ORM 框架,其核心思想是手动编写 SQL 语句,并通过 XML 或注解将结果映射到 Java 对象。相比 Hibernate,它的优势在于:

  • 灵活控制 SQL:开发者可以直接编写原生 SQL,无需依赖框架的复杂映射规则。
  • 轻量级设计:仅需依赖少量核心类(如 SqlSession),学习成本较低。
  • 与 Spring 的天然适配:MyBatis 提供了 mybatis-spring 扩展包,无缝集成 Spring 的事务管理和依赖注入。

比喻:MyBatis 好比一位技艺精湛的乐手,专注于执行“演奏”(SQL 执行)的细节,而 Spring 则负责为其分配曲谱(数据库配置)和协调合奏(事务管理)。


环境搭建:快速启动项目

1. 依赖配置

使用 Maven 或 Gradle 管理项目依赖。以下以 Maven 为例:

<!-- 核心依赖 -->  
<dependency>  
    <groupId>org.springframework</groupId>  
    <artifactId>spring-context</artifactId>  
    <version>5.3.20</version>  
</dependency>  

<dependency>  
    <groupId>org.mybatis</groupId>  
    <artifactId>mybatis</artifactId>  
    <version>3.5.9</version>  
</dependency>  

<!-- Spring MyBatis 集成 -->  
<dependency>  
    <groupId>org.mybatis.spring.boot</groupId>  
    <artifactId>mybatis-spring-boot-starter</artifactId>  
    <version>2.2.0</version>  
</dependency>  

<!-- 数据库驱动(以 MySQL 为例) -->  
<dependency>  
    <groupId>mysql</groupId>  
    <artifactId>mysql-connector-java</artifactId>  
    <version>8.0.29</version>  
</dependency>  

2. 配置数据源

application.properties 中配置数据库连接信息:

spring.datasource.url=jdbc:mysql://localhost:3306/mydb?useSSL=false  
spring.datasource.username=root  
spring.datasource.password=123456  
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver  

mybatis.mapper-locations=classpath:mapper/*.xml  
mybatis.type-aliases-package=com.example.entity  

核心配置详解:Spring 与 MyBatis 的整合

1. 数据源与事务管理

通过 DataSourceTransactionManager,Spring 管理数据库连接和事务边界:

@Configuration  
@EnableTransactionManagement  
public class DataSourceConfig {  

    @Bean  
    @ConfigurationProperties(prefix = "spring.datasource")  
    public DataSource dataSource() {  
        return DataSourceBuilder.create().build();  
    }  

    @Bean  
    public PlatformTransactionManager transactionManager(  
            DataSource dataSource) {  
        return new DataSourceTransactionManager(dataSource);  
    }  
}  

2. MyBatis 配置

通过 SqlSessionFactorySqlSessionTemplate,将 MyBatis 的执行器与 Spring 整合:

@Configuration  
public class MyBatisConfig {  

    @Bean  
    public SqlSessionFactory sqlSessionFactory(  
            DataSource dataSource) throws Exception {  
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();  
        factory.setDataSource(dataSource);  
        return factory.getObject();  
    }  

    @Bean  
    public SqlSessionTemplate sqlSessionTemplate(  
            SqlSessionFactory sqlSessionFactory) {  
        return new SqlSessionTemplate(sqlSessionFactory);  
    }  
}  

基础操作:CRUD 的实现

1. 实体类与 Mapper 接口

定义一个简单的 User 实体类:

public class User {  
    private Long id;  
    private String name;  
    private Integer age;  
    // 省略 getter/setter  
}  

创建对应的 Mapper 接口,并使用 @Mapper 注解声明:

@Mapper  
public interface UserMapper {  
    @Select("SELECT * FROM user WHERE id = #{id}")  
    User selectById(Long id);  

    @Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})")  
    void insert(User user);  

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

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

2. 服务层与事务控制

在服务层通过 @Autowired 注入 Mapper,结合 @Transactional 管理事务:

@Service  
public class UserService {  

    @Autowired  
    private UserMapper userMapper;  

    @Transactional  
    public void transferMoney(Long fromId, Long toId, Integer amount) {  
        User fromUser = userMapper.selectById(fromId);  
        User toUser = userMapper.selectById(toId);  

        // 执行扣款和加款操作  
        fromUser.setAge(fromUser.getAge() - amount);  
        toUser.setAge(toUser.getAge() + amount);  

        userMapper.update(fromUser);  
        userMapper.update(toUser);  
    }  
}  

高级特性:动态 SQL 与缓存

1. 动态 SQL 的灵活使用

通过 MyBatis 的 <if><choose><foreach> 等标签,实现条件查询:

<!-- 在 UserMapper.xml 中 -->  
<select id="selectUsers" resultType="User">  
    SELECT * FROM user  
    WHERE 1=1  
    <if test="name != null">  
        AND name LIKE CONCAT('%', #{name}, '%')  
    </if>  
    <if test="age != null">  
        AND age >= #{age}  
    </if>  
</select>  

2. 二级缓存优化性能

MyBatis 的二级缓存可缓存跨 SQL 查询的结果,通过以下配置开启:

<!-- 在 MyBatis 配置文件中 -->  
<cache/>  

同时,需确保实体类实现 Serializable 接口,并在 Mapper 接口添加 @CacheNamespace 注解:

@CacheNamespace  
public interface UserMapper {  
    // 方法定义  
}  

常见问题与解决方案

1. 事务未生效

原因:未在方法上添加 @Transactional,或未开启事务管理。
解决:检查 @EnableTransactionManagement 是否存在于配置类,并确保方法处于 Spring 管理的 Bean 中。

2. SQL 注入漏洞

原因:直接拼接 SQL 字符串,未使用参数化查询。
解决:使用 #{} 占位符代替字符串拼接,例如:

<update id="updateUser">  
    UPDATE user SET name = #{name} WHERE id = #{id}  
</update>  

实战案例:用户管理系统

1. 需求分析

实现一个简单的用户管理系统,支持以下功能:

  • 新增用户
  • 查询用户列表(带分页)
  • 根据年龄筛选用户

2. 完整代码示例

Mapper 接口

@Mapper  
public interface UserMapper {  
    void insert(User user);  

    @Select({  
        "SELECT * FROM user",  
        "<where>",  
        "  <if test='age != null'>age &gt;= #{age}</if>",  
        "</where>"  
    })  
    List<User> selectUsers(@Param("age") Integer age);  

    @Select("SELECT * FROM user LIMIT #{offset}, #{pageSize}")  
    List<User> selectPage(@Param("offset") int offset,  
                         @Param("pageSize") int pageSize);  
}  

服务层实现

@Service  
public class UserService {  
    private final UserMapper userMapper;  

    public UserService(UserMapper userMapper) {  
        this.userMapper = userMapper;  
    }  

    public void addUser(User user) {  
        userMapper.insert(user);  
    }  

    public List<User> getUsers(Integer age, int page, int pageSize) {  
        int offset = (page - 1) * pageSize;  
        List<User> allUsers = userMapper.selectUsers(age);  
        // 分页逻辑(简化示例)  
        return allUsers.subList(offset, offset + pageSize);  
    }  
}  

结论:构建可持续演进的代码架构

通过本文的讲解,我们完成了从基础配置到高级特性的全链路实践。Spring MyBatis 的组合既保留了 Spring 的架构优势,又赋予了 MyBatis 的灵活性,适合需要精细控制 SQL 的复杂业务场景。

在实际开发中,建议遵循以下原则:

  1. 分层清晰:严格分离 Controller、Service、DAO 层,避免业务逻辑侵入数据访问层。
  2. 代码复用:通过 MyBatis 的 <include> 标签或公共 SQL 片段,减少重复代码。
  3. 性能优化:合理使用缓存、分页和索引,避免全表查询。

希望本文能帮助开发者快速掌握 Spring MyBatis 的核心思想,并在实际项目中游刃有余地应用这一技术栈。

最新发布