文档站点
Skip to content

MyBatis 入门 ​

国内 Java 企业最主流的持久层框架。相比 JPA 的「ORM 自动映射」,MyBatis 的理念是 SQL 你自己写、映射你说了算 —— 对写惯了复杂 SQL 的 PHP 开发者非常友好。它和 JdbcTemplate 有点像,但多了「自动映射 + 动态 SQL」两大武器。

对比 JPA:选谁 ​

维度JPA(类 Eloquent)MyBatis
SQL 由谁写框架生成(findByXxx)你自己写
映射自动半自动(SQL 结果 → 实体)
复杂 SQL 掌控吃力,要 JPQL/原生 SQL完全掌控
学习曲线概念多(懒加载、EntityGraph)简单直接
国内企业占比小占比大
适合简单 CRUD、ORM 爱好者SQL 重度、复杂查询多

TIP

如果你本来就爱写 SQL(很多 PHP 开发者习惯 Query Builder + 复杂查询),MyBatis 会非常顺手。如果你的需求主要是简单 CRUD,JPA 更快。两者都能进 Spring Boot,也可共存(不用二选一)。

三步上手 ​

1. 依赖 ​

xml
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>

2. 配置 ​

yaml
mybatis:
  mapper-locations: classpath:mapper/*.xml     # XML Mapper 位置
  type-aliases-package: com.example.blog.post  # 实体别名包
  configuration:
    map-underscore-to-camel-case: true         # user_id → userId 自动映射(强烈推荐)

TIP

map-underscore-to-camel-case: true 让数据库的 user_id 自动映射到实体的 userId,省掉大量手写映射。这一行基本必开。

3. 建实体 + Mapper ​

实体(普通 POJO,和 JPA 实体一样用 Lombok):

java
@Data
public class Post {
    private Long id;
    private String title;
    private String content;
    private Long userId;
    private Integer status;
    private LocalDateTime createdAt;
}

Mapper 接口(对应 Repository):

java
package com.example.blog.post;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface PostMapper {
    Post findById(Long id);
    List<Post> findAll();
    int insert(Post post);
    int update(Post post);
    int deleteById(Long id);
}

TIP

@Mapper 标记接口,MyBatis 启动时扫描并生成实现。方法名 + SQL(XML 或注解)绑定,不用写实现类。对应 JPA 的 Repository 接口、Eloquent 的 Model 查询。

SQL 写在哪:XML(推荐) vs 注解 ​

XML 方式:src/main/resources/mapper/PostMapper.xml ​

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.blog.post.PostMapper">

    <select id="findById" resultType="Post">
        SELECT * FROM posts WHERE id = #{id}
    </select>

    <select id="findAll" resultType="Post">
        SELECT * FROM posts ORDER BY id DESC
    </select>

    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO posts (title, content, user_id, status)
        VALUES (#{title}, #{content}, #{userId}, #{status})
    </insert>

    <update id="update">
        UPDATE posts SET title = #{title}, content = #{content} WHERE id = #{id}
    </update>

    <delete id="deleteById">
        DELETE FROM posts WHERE id = #{id}
    </delete>
</mapper>
  • id 对应 Mapper 接口方法名
  • resultType="Post" 用配置的别名(type-aliases-package)自动映射列到实体字段
  • #{title} 是参数占位(预编译,防注入,等同 JdbcTemplate 的 ?)

注解方式:SQL 写在接口上 ​

java
@Mapper
public interface PostMapper {

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

    @Insert("INSERT INTO posts (title, content) VALUES (#{title}, #{content})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(Post post);
}

XML vs 注解

  • 简单 SQL:注解够用
  • 复杂 SQL / 动态 SQL:XML 更清晰,可复用、可维护(国内企业普遍 XML)
  • 本教程推荐 XML:和「SQL 与 Java 分离」的 Laravel 心智更接近

使用 Mapper ​

java
@Service
public class PostService {
    private final PostMapper postMapper;

    public PostService(PostMapper postMapper) {
        this.postMapper = postMapper;
    }

    public Post get(Long id) {
        Post post = postMapper.findById(id);
        if (post == null) {
            throw BusinessException.notFound("帖子不存在: " + id);
        }
        return post;
    }

    public List<Post> list() {
        return postMapper.findAll();
    }

    public Long create(PostDto dto) {
        Post post = new Post();
        post.setTitle(dto.getTitle());
        post.setContent(dto.getContent());
        post.setUserId(dto.getUserId());
        post.setStatus(1);
        postMapper.insert(post);
        return post.getId();       // useGeneratedKeys 回填主键
    }
}

对照速查

Eloquent / Query BuilderMyBatis
Post::find($id)postMapper.findById(id)
Post::where('status',1)->get()XML 里 SELECT ... WHERE status = #{status}
Post::create($data)insert + useGeneratedKeys
$post->save()update
Post::destroy($id)deleteById
模型关联 $post->user自定义 JOIN SQL

参数传递的三种方式 ​

java
// 1. 单参数:直接 #{名字}
Post findById(Long id);

// 2. 多参数:用 @Param 命名
List<Post> findByUser(@Param("userId") Long userId, @Param("status") Integer status);
xml
<select id="findByUser" resultType="Post">
    SELECT * FROM posts WHERE user_id = #{userId} AND status = #{status}
</select>
java
// 3. 对象参数:直接用属性名
int insert(Post post);   // #{title} 取 post.title

常见问题 ​

报错原因
Invalid bound statement (not found)Mapper 接口方法在 XML 里没对应 id,或 XML 没被扫描
A query was run and no Result Maps were found返回类型没配 resultType
字段全是 null没开 map-underscore-to-camel-case,或 resultType 写错
XML 找不到mapper-locations 路径配错

下一步 ​

面向 PHP / Laravel 开发者的 Spring Boot 中文文档