Mybatis 中 Dao 接口(Mapper 接口)的工作原理与重载问题详解
Mybatis 中 Dao 接口Mapper 接口的工作原理与重载问题详解在 Mybatis 开发中我们通常会为每一个 XML 映射文件编写一个对应的 Dao 接口又称 Mapper 接口。很多初学者会好奇这个接口并没有实现类为什么 Mybatis 能够正确执行 SQL 并返回结果接口中的方法能否重载本文将从底层原理出发结合动态代理、映射文件解析等机制深入剖析这些问题并辅以 UML 类图与流程图帮助理解。一、Dao 接口与 XML 映射文件的映射关系在 Mybatis 中Dao 接口的全限定名包名接口名与 XML 映射文件中的namespace一一对应接口中的方法名与映射文件中定义的 MappedStatement 的id一一对应。例如UserMapper.javapackagecom.example.mapper;publicinterfaceUserMapper{UserselectUserById(Integerid);}UserMapper.xmlmappernamespacecom.example.mapper.UserMapperselectidselectUserByIdresultTypecom.example.entity.UserSELECT * FROM user WHERE id #{id}/select/mapper当 Mybatis 启动时会解析 XML 文件为每个select|insert|update|delete标签创建一个MappedStatement对象并以namespace . id作为唯一键值存储到 Configuration 中。上述例子中键值为com.example.mapper.UserMapper.selectUserById。二、Dao 接口的工作原理JDK 动态代理由于 Dao 接口没有实现类Mybatis 在运行时通过JDK 动态代理为其生成代理对象。调用接口方法时代理对象会拦截该方法根据方法所属的接口全限定名 方法名定位到对应的MappedStatement然后执行 SQL 并返回结果。2.1 核心流程获取 Mapper 代理对象通过SqlSession.getMapper(ClassT type)方法Mybatis 使用MapperProxyFactory为接口创建动态代理MapperProxy实现了InvocationHandler。调用方法触发代理当调用代理对象的方法时MapperProxy.invoke()会被执行。定位 MappedStatementMapperMethod对象负责解析方法签名构造出MappedStatement的唯一 id接口全限定名 . 方法名并从 Configuration 中取出对应的MappedStatement。执行 SQL通过SqlSession执行 SQL根据 SQL 类型调用selectOne、insert、update、delete等方法并处理参数与返回值。返回结果将 SQL 执行结果或映射后的实体对象返回给调用方。2.2 流程图下图展示了从获取 Mapper 到执行 SQL 的完整过程调用 SqlSession.getMapperMapperProxyFactory 创建 MapperProxy返回 JDK 动态代理对象调用代理对象的接口方法MapperProxy.invoke 拦截获取 MapperMethod 对象根据接口全限定名方法名 构造 key从 Configuration 中获取 MappedStatement通过 SqlSession 执行 SQL返回结果2.3 类关系图UML为了更清晰地理解各组件之间的协作关系下图为简化后的 UML 类图createsusesuseslocates MappedStatementcontainsMapperProxyFactoryClass mapperInterfacenewInstance(SqlSession) : : ObjectMapperProxy-SqlSession sqlSession-MapMethod, MapperMethod methodCacheinvoke(Object proxy, Method method, Object[] args) : : ObjectMapperMethod-SqlCommand command-MethodSignature methodSignatureexecute(SqlSession, Object[] args) : : Object«interface»SqlSessionselectOne(String statement, Object parameter)insert(String statement, Object parameter)getMapper(Class type)ConfigurationgetMappedStatement(String id) : : MappedStatementMappedStatement-String id-SqlSource sqlSource-ResultMap resultMap三、Dao 接口中的方法能否重载答案是不能重载。原因在于 Mybatis 定位MappedStatement的 key 是由接口全限定名 方法名组成参数类型、参数数量、参数顺序等信息完全不参与 key 的生成。因此即便在同一个接口中定义两个同名但参数不同的方法它们在底层会被视为同一个MappedStatement从而导致冲突或执行错误。3.1 错误示例publicinterfaceUserMapper{UserselectUser(Integerid);// key: ...selectUserUserselectUser(Stringname);// 同名key 重复UserselectUser(Integerid,Stringname);// 同名key 重复}Mybatis 启动时会报错Caused by: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for com.example.mapper.UserMapper.selectUser3.2 为什么不支持重载设计理念Mybatis 将 Mapper 接口方法直接映射为 XML 中的唯一 SQL 标签方法名就是标签 id。XML 标签的 id 本身不允许重复这与 Java 的方法重载语义不兼容。实现简化Mybatis 在解析接口方法时只使用方法名作为标识无需处理复杂的参数类型解析避免了性能开销和实现复杂性。3.3 替代方案如果确实需要类似的“重载”效果可以采用以下方式不同方法名将方法名定义为具有业务含义的不同名称例如selectUserById、selectUserByName。使用Param注解 动态 SQL一个方法接受多个参数内部通过if等标签动态生成不同条件的 SQL。使用 Map 作为参数定义一个方法ListUser selectUser(MapString, Object params)在 Map 中传入不同的查询条件。四、总结关键点说明映射关系接口全限定名 → namespace方法名 → MappedStatement 的 id底层实现JDK 动态代理MapperProxy方法定位 key接口全限定名 . 方法名是否支持重载不支持。因为 key 不包含参数信息重名方法会导致 key 冲突替代方案使用不同方法名、Param 动态 SQL、Map 参数等方式解决类似需求理解 Dao 接口的工作原理不仅能帮助我们更正确地使用 Mybatis也能在遇到“方法找不到”、“参数绑定错误”等问题时快速定位原因。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2503695.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!