mybatis知识点分析之Mapper映射器

本文详细探讨了MyBatis中Mapper接口的工作原理,包括MapperRegistry如何注册Mapper接口,MapperProxyFactory如何创建Mapper代理对象,MapperProxy在数据库交互中的作用,以及MapperMethod在执行SQL操作中的关键角色。通过这些机制,MyBatis实现了接口方法与数据库操作的映射,使得持久层操作得以顺利进行。

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

我们知道在mybatis中,Mapper接口方法是不能被其他类重写的,而该接口就相当于。

接口:UserMapper.java

@Mapper
package cn.jxufe.web.mybatis;

public interface UserMapper {
    public void insertUser(User user);
    public User getUser(String name);
}

对应的映射文件:UserMapper.xml


    <mapper namespace="cn.jxufe.web.mybatis.UserMapper">

    <insert id="insertUser" parameterType="User">
        insert into user(name,age) values(#{name},#{age}) 
   </insert>

    <select id="getUser" resultType="User" parameterType="java.lang.String">
        select * from user where name=#{name}
    </select>
</mapper>

其中:UserMapper.xml中每个标签中的id对应UserMapper接口中的方法。

那映射文件时如何根据对应的接口实现持久层中与数据库进行交互的呢?解析如下:

1.MapperRegistry

应用程序启动的时候就会创建这个类的实例,它有一个HashMap类型的属性用于存储每个Mapper接口(key)和相应的MapperProxyFactory(value);

另外有两个重要的方法getMapper()和addMapper(),分别用于获取和注册Mapper接口到这个HashMap中。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if(mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                return mapperProxyFactory.newInstance(sqlSession);//通过Mapper接口的type返回一个相应的mapperProxyFactory的一个实例
 
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }
public <T> void addMapper(Class<T> type) {
        if(type.isInterface()) {
            if(this.hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }
 
            boolean loadCompleted = false;
 
            try {
                this.knownMappers.put(type, new MapperProxyFactory(type));//将这个Mapper接口“注册”
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if(!loadCompleted) {
                    this.knownMappers.remove(type);
                }
 
            }
        }
    }

2.MapperProxyFactory

作用:用于创建mapper代理对象的工厂,而mapper代理对象负责代替mapper接口完成POJO到数据库的映射。

在MapperProxyFactory中的两个重要方法如下,用于创建mapper代理类的一个实例。

protected T newInstance(MapperProxy<T> mapperProxy) {
	//用了java的动态代理,获得真正的代理对象
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }
 
public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }

3. MapperProxy

通过调用特定的mapper代理对象的invoke()方法,实现到数据库的映射,实际上invoke()中又调用了MapperMethod中的execute方法,execute方法主要是用到sqlSession的insert、update、select、delete等。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(Object.class.equals(method.getDeclaringClass())) {
            try {
                return method.invoke(this, args);
            } catch (Throwable var5) {
                throw ExceptionUtil.unwrapThrowable(var5);
            }
        } else {
            MapperMethod mapperMethod = this.cachedMapperMethod(method);
            return mapperMethod.execute(this.sqlSession, args);
        }
    }

 

4. MapperMethod

public Object execute(SqlSession sqlSession, Object[] args) {
        Object param;
        Object result;
        if(SqlCommandType.INSERT == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
        } else if(SqlCommandType.UPDATE == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
        } else if(SqlCommandType.DELETE == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
        } else if(SqlCommandType.SELECT == this.command.getType()) {
            if(this.method.returnsVoid() && this.method.hasResultHandler()) {
                this.executeWithResultHandler(sqlSession, args);
                result = null;
            } else if(this.method.returnsMany()) {
                result = this.executeForMany(sqlSession, args);
            } else if(this.method.returnsMap()) {
                result = this.executeForMap(sqlSession, args);
            } else if(this.method.returnsCursor()) {
                result = this.executeForCursor(sqlSession, args);
            } else {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(this.command.getName(), param);
            }
        } else {
            if(SqlCommandType.FLUSH != this.command.getType()) {
                throw new BindingException("Unknown execution method for: " + this.command.getName());
            }

举例:

UserDAO被@Autowired注解,由spring完成bean的自动装配,其中spring会去获取一个MyBatis的mapperProxy代理对象作为这个bean,它是一个真正的对象而不是一个Interface类型,由这个代理人完成到数据库的映射。即持久层由代理人来完成。

 public class UserDamoImpl{
        @Autowired
	UserDAO userDAO;
 
	public void contextLoads() {
		User user = new User();
		user.setName("user1");
		user.setPassword("");
		user.setSalt("");
		user.setHeadUrl("xxx.png");
		userDAO.addUser(user);
        }
  }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值