mybatis-mapper深入理解

35 阅读3分钟

在以前学 Java 基础的时候(比如学集合 ArrayList 或者面向对象时),流程通常是这样的:

  1. 写一个接口(Interface)。
  2. 必须写一个实现类(Class implements Interface)去写具体的逻辑代码。
  3. 然后 new 这个实现类来用。

但是 Mapper 非常“反直觉”:你只写了一个接口,也没写实现类,居然就能跑起来?

别急,我用一个**“自助点餐机”**的比喻,帮你彻底打通这个概念。


1. 什么是 Mapper?(生活类比)

想象你去麦当劳点餐:

  • 数据库 (MySQL) :就是后厨,里面堆满了面包、肉饼、生菜(数据)。

  • Mapper 接口:就是那台自助点餐机(的屏幕)

    • 屏幕上有一个按钮叫“巨无霸套餐”(对应方法 list())。
    • 按钮背后绑定了一张配方单(对应 SQL @Select("select * ..."))。

MyBatis 的魔法就在这里:

当你按下屏幕上的“巨无霸”按钮时,你不需要自己进后厨去煎肉饼。MyBatis 后台有一个“隐形的大厨”(代理对象),他看到你按了按钮,就会自动根据背后的配方(SQL),去后厨把饭做好,封装成精美的盒子(User 对象),直接递到你手里。

总结:Mapper 就是一个绑定了 SQL 语句的“操作面板”。


2. 它是怎么“无中生有”的?

你现在的困惑是:“我没写实现类,谁在干活?”

答案是:Spring Boot 和 MyBatis 在程序启动的那一瞬间,帮你偷偷写了一个“影子实现类”。

你的代码(表面上):

Java

@Mapper
public interface UserMapper {
    // 只有方法定义,没有大括号 {} 里的内容
    @Select("select * from user") 
    public List<User> list();
}

程序运行时(内存里偷偷发生的):

MyBatis 会利用 Java 的动态代理技术,在内存里自动生成一个这样的类(伪代码):

Java

// 这是 MyBatis 帮你自动生成的“影子类”,你看不到,但在内存里真实存在
public class UserMapper$Proxy implements UserMapper {
    
    public List<User> list() {
        // 1. 自动连接数据库(读取 application.properties)
        Connection conn = DriverManager.getConnection(...);
        
        // 2. 自动找到你在注解里写的 SQL
        String sql = "select * from user";
        
        // 3. 执行 SQL
        ResultSet rs = statement.executeQuery(sql);
        
        // 4. 自动把结果变成 User 对象(根据你的返回值类型)
        List<User> list = new ArrayList<>();
        while(rs.next()){
            User u = new User();
            u.setId(rs.getInt("id"));
            u.setName(rs.getString("name"));
            list.add(u);
        }
        return list;
    }
}

所以,当你调用 userMapper.list() 时,其实是在调用这个**“影子类”**里的代码。这就是为什么你不需要写实现类。


3. Mapper 的三个关键点(缺一不可)

为了让这个“自助点餐机”能工作,必须满足三个条件,这也就是你刚才报错的原因:

① 身份铭牌:@Mapper

  • 作用:告诉 Spring Boot:“这个接口不是普通的接口,这是个点餐机!请在启动时帮我生成那个‘影子实现类’。”
  • 后果:如果不加 @Mapper,Spring 就把它当成普通接口,不会生成代理对象。当你用 @Autowired 注入时,Spring 发现手里没货,就会报 Could not autowire(无法注入)。

② 绑定配方:@Select(...)

  • 作用:告诉“隐形大厨”,按下这个按钮(方法)后,该执行哪句 SQL。

③ 规定餐具:返回值 List<User>

  • 作用:告诉 MyBatis,数据查出来后,要封装成什么样子。

    • User,它就返给你一个对象。
    • List<User>,它就返给你一堆对象。

4. 为什么它比老办法好?

老办法 (JDBC / DAO 实现类):

你需要自己写连接、自己写 try-catch、自己写 while 循环把 rs.getString 塞进对象里。写 10 个查询就要写 10 遍这样的废话代码。

Mapper (MyBatis):

你只管定义“我要什么”(接口方法)和“怎么查”(SQL),剩下的脏活累活全交给框架。