Mybatis实现Mapper接口的简单案例

本文深入探讨了MyBatis如何使用动态代理实现Mapper接口,通过示例代码展示了Service层调用Mapper接口的过程,解析了MyBatis动态代理的具体实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 用了很久mybatis,一直猜想mybatis是使用动态代理的方式实现mapper接口的,通过实践探索之后,果然如此,但是还是遇到了不少问题,在此粘出来供初学者参考。
首先我将案例代码粘出来,这段代码简单实现了从service层开始调用mapper接口的过程。

package com.star.ott.common.office;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TestClass {

    public static void main(String[] args) {
        new UserService().updateUserByUserName();
        new UserService().findUser("");
    }

}

/**
 * 我们需要操作的user实体
 */
class User {
    private String userName;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

/**
 * 这里是我们的mapper接口
 */
interface UserMapper {
    User findUserByUserName(String userName);

    void updateUserByUserName(User user);
}

/**
 * 此处是service层的业务类
 */
class UserService {
    //在实际开发中,此处的UserMapper是通过注解加载进来的,而其真正的
    //实现就类似于下面的实现方式,通过动态带来的方式创建实例
    UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{UserMapper.class}, new MybatisProxy());

    public User findUser(String userName) {
        return userMapper.findUserByUserName(userName);
    }

    public void updateUserByUserName() {
        userMapper.updateUserByUserName(new User());
    }
}

/**
 * 这里就是具体的代理类,在代理类中,通过数据库驱动与数据库获取
 * 连接,然后读取相关mapper.xml执行sql,最后获得操作结果,具体
 * 连接数据库,读取mapper.xml文件的方式就不再累赘
 */
class MybatisProxy implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("遍历参数" + method.getParameters()[0]);
        System.out.println("通过method的名字去mapper.xml文件中找到sql: " + method.getName());
        System.out.println("执行数据库操作,若果有异常等,直接在此处抛出,没有则拿到返回值");
        if(method.getReturnType().toString().equals("void")){
            System.out.println("无返回值的情况");
            return method.getGenericReturnType();
        }else {
            Object result = method.getReturnType().getDeclaredConstructor().newInstance();
            System.out.println("将得到的返回值赋值给result,然后返回" + method.getReturnType());
            return result;
        }
    }
}



需要注意的是,通过Proxy.newProxyInstance方式生成代理对象时,一般除了需要接口类的类类型之外,还需要实现类的实例作为代理类的参数,这样,我们就可以在代理对象中,也就是MybatisProxy中直接执行实现类实例的方法,然后返回。但是我们此处代理对象的特殊之处,就是代理了接口,实现逻辑完全写在代理类中,所以遇到没有返回值的方法时,需要做逻辑判断,返回一个method.getGenericReturnType(),否则这里会报错。
好了,这就是mybatis通过动态代理的方式实现mapper接口的大致方式,可以参考一下。

### MyBatis Mapper 接口代理实现原理 MyBatis 的核心功能之一是通过动态代理机制为开发者定义的 Mapper 接口生成其实现类。这种设计使得开发人员无需手动编写接口的具体实现逻辑,而是在运行时由框架自动生成并处理。 #### 动态代理的核心流程 1. **获取 Mapper 对象** 当调用 `sqlSession.getMapper(MapperInterface.class)` 方法时,MyBatis 会返回一个实现了指定接口的代理对象[^4]。此过程涉及以下几个关键步骤: 2. **创建代理工厂实例** MyBatis 使用 `MapperProxyFactory` 创建代理对象的工厂实例。该工厂负责管理代理对象的生命周期,并提供必要的上下文信息(如 `SqlSession` 和目标接口类型)[^3]。 3. **生成代理对象** 利用 Java 提供的动态代理技术(基于 JDK 或 CGLIB),MyBatis 调用 `Proxy.newProxyInstance` 方法生成具体的代理对象。在此过程中,`MapperProxy` 类作为 InvocationHandler 实现了方法拦截的功能[^2]。 4. **方法拦截与 SQL 执行** 当业务代码调用 Mapper 接口中定义的方法时,实际执行的是代理对象内部的 `invoke` 方法。该方法的主要职责包括解析方法签名、匹配对应的 SQL 映射语句以及最终调用 `mapperMethod.execute(sqlSession, args)` 完成数据库操作。 #### 头歌平台的应用案例 头歌教育平台可能在多个场景下使用 MyBatis 来简化数据访问层的设计与实现。以下是几个典型应用场景: 1. **课程管理系统** 在课程管理模块中,可以通过定义 Mapper 接口来完成对课程表的增删改查操作。例如,假设存在一张存储课程信息的表,则可以如下定义其 Mapper 接口及其映射文件: ```java public interface CourseMapper { @Select("SELECT * FROM courses WHERE id = #{id}") Course getCourseById(int id); @Insert("INSERT INTO courses (name, description) VALUES (#{name}, #{description})") void addCourse(Course course); } ``` 2. **用户权限控制** 用户登录验证及角色授权通常依赖于复杂的查询条件组合。借助 MyBatis 的动态 SQL 特性,能够灵活构建满足需求的查询语句。例如,在判断某个用户是否有权访问特定资源时,可采用以下方式: ```xml <select id="checkUserPermission" resultType="boolean"> SELECT EXISTS ( SELECT 1 FROM user_permissions up JOIN users u ON up.user_id = u.id WHERE u.username = #{username} AND up.resource_id = #{resourceId} ) </select> ``` 3. **批量导入导出** 针对学生作业提交记录的大规模导入导出任务,可通过 MyBatis 支持的批处理能力显著提升效率。具体做法是在 Mapper 中声明支持批量更新的方法,并配合事务管理确保一致性: ```java public interface SubmissionMapper { @Options(useGeneratedKeys = true, keyProperty = "id", flushCache = false) int insertSubmission(Submission submission); default void batchInsertSubmissions(List<Submission> submissions) { SqlSession sqlSession = Configuration.getDefaultSqlSessionFactory().openSession(); try { for (Submission s : submissions) { sqlSession.insert("insertSubmission", s); } sqlSession.commit(); } finally { sqlSession.close(); } } } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值