在以前学 Java 基础的时候(比如学集合 ArrayList 或者面向对象时),流程通常是这样的:
- 写一个接口(Interface)。
- 必须写一个实现类(Class implements Interface)去写具体的逻辑代码。
- 然后
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),剩下的脏活累活全交给框架。