mybatis 接口原理 接口怎么变成类的

Mybatis接口编程原理分析_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
Mybatis接口编程原理分析
&&Mybatis接口编程原理分析
阅读已结束,下载文档到电脑
想免费下载本文?
定制HR最喜欢的简历
下载文档到电脑,方便使用
还剩9页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢详解mybatis通过mapper接口加载映射文件
作者:YSOcean
字体:[ ] 类型:转载 时间:
本篇文章主要介绍了mybatis通过mapper接口加载映射文件 ,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
通过 mapper 接口加载映射文件,这对于后面 ssm三大框架 的整合是非常重要的。那么什么是通过 mapper 接口加载映射文件呢?
我们首先看以前的做法,在全局配置文件 mybatis-configuration.xml 通过 &mappers& 标签来加载映射文件,那么如果我们项目足够大,有很多映射文件呢,难道我们每一个映射文件都这样加载吗,这样肯定是不行的,那么我们就需要使用 mapper 接口来加载映射文件
以前的做法:
改进做法:使用 mapper 接口来加载映射文件
1、定义 userMapper 接口
package com.ys.
import org.apache.ibatis.annotations.D
import org.apache.ibatis.annotations.I
import org.apache.ibatis.annotations.S
import org.apache.ibatis.annotations.U
import com.ys.po.U
public interface UserMapper {
//根据 id 查询 user 表数据
public User selectUserById(int id) throws E
//向 user 表插入一条数据
public void insertUser(User user) throws E
//根据 id 修改 user 表数据
public void updateUserById(User user) throws E
//根据 id 删除 user 表数据
public void deleteUserById(int id) throws E
2、在全局配置文件 mybatis-configuration.xml 文件中加载 UserMapper 接口(单个加载映射文件)
3、编写UserMapper.xml 文件
&?xml version="1.0" encoding="UTF-8" ?&
&!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"&
&mapper namespace="com.ys.mapper.UserMapper"&
&!-- 根据 id 查询 user 表中的数据
id:唯一标识符,此文件中的id值不能重复
resultType:返回值类型,一条数据库记录也就对应实体类的一个对象
parameterType:参数类型,也就是查询条件的类型
&select id="selectUserById"
resultType="com.ys.po.User" parameterType="int"&
&!-- 这里和普通的sql 查询语句差不多,后面的 #{id}表示占位符,里面不一定要写id,写啥都可以,但是不要空着 --&
select * from user where id = #{id1}
&!-- 根据 id 更新 user 表的数据 --&
&update id="updateUserById" parameterType="com.ys.po.User"&
update user u
&!-- &set&
&if test="username != null and username != ''"&
u.username = #{username},
&if test="sex != null and sex != ''"&
u.sex = #{sex}
&/set& --&
&trim prefix="set" suffixOverrides=","&
&if test="username != null and username != ''"&
u.username = #{username},
&if test="sex != null and sex != ''"&
u.sex = #{sex},
where id=#{id}
&!-- 向 user 表插入一条数据 --&
&insert id="insertUser" parameterType="com.ys.po.User"&
&!-- 将插入的数据主键返回到 user 对象中
keyProperty:将查询到的主键设置到parameterType 指定到对象的那个属性
select LAST_INSERT_ID():查询上一次执行insert 操作返回的主键id值,只适用于自增主键
resultType:指定 select LAST_INSERT_ID() 的结果类型
order:AFTER,相对于 select LAST_INSERT_ID()操作的顺序
&selectKey keyProperty="id" resultType="int" order="AFTER"&
select LAST_INSERT_ID()
&/selectKey&
insert into user(username,sex,birthday,address)
value(#{username},#{sex},#{birthday},#{address})
&!-- 根据 id 删除 user 表的数据 --&
&delete id="deleteUserById" parameterType="int"&
delete from user where id=#{id}
//根据id查询user表数据
public void testSelectUserById(){
/*这个字符串由 userMapper.xml 文件中 两个部分构成
&mapper namespace="com.ys.po.userMapper"& 的 namespace 的值
&select id="selectUserById" & id 值*/
String statement = "com.ys.mapper.UserMapper.selectUserById";
User user = session.selectOne(statement, 1);
System.out.println(user);
session.close();
5、批量加载映射文件
&!--批量加载mapper
指定 mapper 接口的包名,mybatis自动扫描包下的mapper接口进行加载
&package name="com.ys.mapper"/&
&/mappers&
1、UserMapper 接口必须要和 UserMapper.xml 文件同名且在同一个包下,也就是说 UserMapper.xml 文件中的namespace是UserMapper接口的全类名
2、UserMapper接口中的方法名和 UserMapper.xml 文件中定义的 id 一致
3、UserMapper接口输入参数类型要和 UserMapper.xml 中定义的 parameterType 一致
4、UserMapper接口返回数据类型要和 UserMapper.xml 中定义的 resultType 一致
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具博客分类:
整合Spring3及MyBatis3
对于整合Spring及Mybatis不作详细介绍,可以参考:,贴出我的主要代码如下: UserMapper Interface:
package org.denger.
import org.apache.ibatis.annotations.P
import org.apache.ibatis.annotations.S
import org.denger.po.U
public interface UserMapper {
@Select("select * from tab_uc_account where id=#{userId}")
User getUser(@Param("userId") Long userId);
package org.denger.
import org.apache.ibatis.annotations.P
import org.apache.ibatis.annotations.S
import org.denger.po.U
public interface UserMapper {
@Select("select * from tab_uc_account where id=#{userId}")
User getUser(@Param("userId") Long userId);
application-context.xml:
version="1.0" encoding="UTF-8"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"
id="dataSourceProxy" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy"
bean="dataSource"
id="dataSource" class="com.mchange.boPooledDataSource" destroy-method="close"
name="driverClass" value="com.mysql.jdbc.Driver"
name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/noah"
name="user" value="root"
name="password" value="123456"
id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
name="dataSource" ref="dataSource"
class="org.mybatis.spring.mapper.MapperScannerConfigurer"
name="sqlSessionFactory"
ref="sqlSessionFactory"
name="basePackage" value="org.denger.mapper"
&?xml version="1.0" encoding="UTF-8"?&
&beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"&
&!-- Provided by annotation-based configuration
&context:annotation-config/&
&!--JDBC Transaction
Manage --&
&bean id="dataSourceProxy" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy"&
&constructor-arg&
&ref bean="dataSource" /&
&/constructor-arg&
&!-- The JDBC c3p0 dataSource bean--&
&bean id="dataSource" class="com.mchange.boPooledDataSource" destroy-method="close"&
&property name="driverClass" value="com.mysql.jdbc.Driver" /&
&property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/noah" /&
&property name="user" value="root" /&
&property name="password" value="123456" /&
&!--MyBatis integration with Spring as define sqlSessionFactory
&bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"&
&property name="dataSource" ref="dataSource" /&
&bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"&
&property name="sqlSessionFactory"
ref="sqlSessionFactory"/&
&property name="basePackage" value="org.denger.mapper"&&/property&
test Class:
package org.denger.
import org.junit.A
import org.junit.T
import org.springframework.beans.factory.annotation.A
import org.springframework.test.context.ContextC
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextT
@ContextConfiguration(locations = { "/application-context.xml"})
public class UserMapperTest extends AbstractJUnit4SpringContextTests{
@Autowired
public UserMapper userM
public void testGetUser(){
Assert.assertNotNull(userMapper.getUser(300L));
package org.denger.
import org.junit.A
import org.junit.T
import org.springframework.beans.factory.annotation.A
import org.springframework.test.context.ContextC
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextT
@ContextConfiguration(locations = { "/application-context.xml"})
public class UserMapperTest extends AbstractJUnit4SpringContextTests{
@Autowired
public UserMapper userM
public void testGetUser(){
Assert.assertNotNull(userMapper.getUser(300L));
实现原理分析
对于以上极其简单代码看上去并无特殊之处,主要亮点在于 UserMapper 居然不用实现类,而且在调用 getUser 的时候,也是使用直接调用了UserMapper实现类,那么Mybatis是如何去实现 UserMapper的接口的呢?
可能你马上能想到的实现机制就是通过方式,好吧,看看MyBatis整个的代理过程吧。
首先在Spring的配置文件中看到下面的Bean:
class="org.mybatis.spring.mapper.MapperScannerConfigurer"
name="sqlSessionFactory"
ref="sqlSessionFactory"
name="basePackage" value="org.denger.mapper"
&bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"&
&property name="sqlSessionFactory"
ref="sqlSessionFactory"/&
&property name="basePackage" value="org.denger.mapper"&&/property&
以上的MapperScannerConfigurer class的注释中描述道:
从base 包中搜索所有下面所有 interface,并将其注册到 Spring Bean容器中,其注册的class bean是MapperFactoryBean。
好吧,看看它的注册过程,下面方法来从 MapperScannerConfigurer中的Scanner类中抽取,下面方法在初始化以上application-content.xml文件时就会进行调用。 主要用于是搜索 base packages 下的所有mapper class,并将其注册至 spring 的 benfinitionHolder中。
protected Set&BeanDefinitionHolder& doScan(String... basePackages) {
Set&BeanDefinitionHolder& beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("No MyBatis mapper was found in '" + MapperScannerConfigurer.this.basePackage
+ "' package. Please check your configuration.");
for (BeanDefinitionHolder holder : beanDefinitions) {
GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
if (logger.isDebugEnabled()) {
logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '"+ definition.getBeanClassName() + "' mapperInterface");
definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
definition.setBeanClass(MapperFactoryBean.class);
return beanD
* Calls the parent search that will search and register all the candidates. Then the
* registered objects are post processed to set them as MapperFactoryBeans
protected Set&BeanDefinitionHolder& doScan(String... basePackages) {
Set&BeanDefinitionHolder& beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("No MyBatis mapper was found in '" + MapperScannerConfigurer.this.basePackage
+ "' package. Please check your configuration.");
for (BeanDefinitionHolder holder : beanDefinitions) {
GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
if (logger.isDebugEnabled()) {
logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '"+ definition.getBeanClassName() + "' mapperInterface");
definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
definition.setBeanClass(MapperFactoryBean.class);
return beanD
#1: 了解Spring初始化Bean过程的人可能都知道,Spring 首先会将需要初始化的所有class先通过BeanDefinitionRegistry进行注册,并且将该Bean的一些属性信息(如scope、className、beanName等)保存至BeanDefinitionHolder中;Mybatis这里首先会调用Spring中的ClassPathBeanDefinitionScanner.doScan方法,将所有Mapper接口的class注册至BeanDefinitionHolder实例中,然后返回一个Set&BeanDefinitionHolder&,其它包含了所有搜索到的Mapper class BeanDefinitionHolder对象。
#2: 首先,由于 #1:  中注册的都是接口class, 可以肯定的是接口是不能直接初始化的;实际 #2: 中for循环中替换当前所有 holder的 className为 MapperFactoryBean.class,并且将 mapper interface的class name setter 至 MapperFactoryBean 属性为 mapperInterface 中,也就是 #3: 代码所看到的。   再看看 MapperFactoryBean,它是直接实现了 Spring 的 FactoryBean及InitializingBean 接口。其实既使不看这两个接口,当看MapperFactoryBean的classname就知道它是一个专门用于创建 Mapper 实例Bean的工厂。   至此,已经完成了Spring与mybatis的整合的初始化配置的过程。
接着,当我在以上的 Test类中,需要注入 UserMapper接口实例时,由于mybatis给所有的Mapper 实例注册都是一个MapperFactory的工厂,所以产生UserMapper实现仍需要 MapperFactoryBean来进行创建。接下来看看 MapperFactoryBean的处理过程。
先需要创建Mapper实例时,首先在 MapperFactoryBean中执行的方法是:
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = this.sqlSession.getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
configuration.addMapper(this.mapperInterface);
* {@inheritDoc}
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = this.sqlSession.getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
configuration.addMapper(this.mapperInterface);
上面方法中先会检测当前需要创建的 mapperInterface在全局的 Configuration中是否存在,如果不存在则添加。呆会再说他为什么要将 mapperInterface 添加至 Configuration中。该方法调用完成之后,就开始调用 getObject方法来产生mapper实例,看到MapperFactoryBean.getObject的该方法代码如下:
public T getObject() throws Exception {
return this.sqlSession.getMapper(this.mapperInterface);
* {@inheritDoc}
public T getObject() throws Exception {
return this.sqlSession.getMapper(this.mapperInterface);
通过Debug可以看到 sqlSession及mapperInterface对象:
到目前为止我们的 UserMapper实例实际上还并未产生; 再进入org.mybatis.spring.SqlSessionTemplate中的getMapper方法,该方法将 this及mapper interface class 作为参数传入 org.apache.ibatis.session.Configuration的getMapper() 方法中,代码如下:
public &T& T getMapper(Class&T& type) {
return getConfiguration().getMapper(type, this);
* {@inheritDoc}
public &T& T getMapper(Class&T& type) {
return getConfiguration().getMapper(type, this);
于是,再进入 org.apache.ibatis.session.Configuration.getMapper中代码如下:
public &T& T getMapper(Class&T& type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
public &T& T getMapper(Class&T& type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
其中 mapperRegistry保存着当前所有的 mapperInterface class. 那么它在什么时候将 mapperinterface class 保存进入的呢?其实就是在上面的 afterPropertiesSet 中通过 configuration.addMapper(this.mapperInterface) 添加进入的。
再进入 org.apache.ibatis.binding.MapperRegistry.getMapper方法,代码如下:
public &T& T getMapper(Class&T& type, SqlSession sqlSession) {
if (!knownMappers.contains(type))
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
return MapperProxy.newMapperProxy(type, sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
public &T& T getMapper(Class&T& type, SqlSession sqlSession) {
  //首先判断当前knownMappers是否存在mapper interface class.因为前面说到 afterPropertiesSet 中已经将当前的 mapperinterfaceclass 添加进入了。
if (!knownMappers.contains(type))
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
return MapperProxy.newMapperProxy(type, sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
 嗯,没错,看到 MapperProxy.newMapperProxy后可以肯定的是它确实采用的代理模式,再进入一看究竟吧:   
public static &T& T newMapperProxy(Class&T& mapperInterface, SqlSession sqlSession) {
ClassLoader classLoader = mapperInterface.getClassLoader();
Class[] interfaces = new Class[]{mapperInterface};
MapperProxy proxy = new MapperProxy(sqlSession);
return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
public static &T& T newMapperProxy(Class&T& mapperInterface, SqlSession sqlSession) {
ClassLoader classLoader = mapperInterface.getClassLoader();
Class[] interfaces = new Class[]{mapperInterface};
MapperProxy proxy = new MapperProxy(sqlSession);
return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
JDK的动态代理就不说了,至此,基本Mybatis已经完成了Mapper实例的整个创建过程,也就是你在具体使用 UserMapper.getUser 时,它实际上调用的是 MapperProxy,因为此时 所返回的 MapperProxy是实现了 UserMapper接口的。只不过 MapperProxy拦截了所有对userMapper中方法的调用,如下:  
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!OBJECT_METHODS.contains(method.getName())) {
final Class declaringInterface = findDeclaringInterface(proxy, method);
final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession);
final Object result = mapperMethod.execute(args);
if (result == null && method.getReturnType().isPrimitive()) {
throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
} catch (SQLException e) {
e.printStackTrace();
return null;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//如果调用不是 object 中默认的方法(如equals之类的)
if (!OBJECT_METHODS.contains(method.getName())) {
//因为当前MapperProxy代理了所有 Mapper,所以他需要根据当前拦截到的方法及代理对象获取 MapperInterface
class,也就是我这里的 UserMapper.class
final Class declaringInterface = findDeclaringInterface(proxy, method);
//然后再根据UserMapper.class、及当前调用的Method(也就是getUser)及SqlSession构造一个 MapperMethod,在这里面会获取到 getUser方法上的 @Select() 的SQL,然后再通过 sqlSession来进行执行
final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession);
//execute执行数据库操作,并返回结果集
final Object result = mapperMethod.execute(args);
if (result == null && method.getReturnType().isPrimitive()) {
throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
} catch (SQLException e) {
e.printStackTrace();
为什么说它拦截了呢?可以看到, 它并没有调用 method.invoke(object)方法,因为实际上 MapperProxy只是动态的 implement 了UserMapper接口,但它没有真正实现 UserMapper中的任何方法。至于结果的返回,它也是通过 MapperMethod.execute 中进行数据库操作来返回结果的。 说白了,就是一个实现了 MapperInterface 的 MapperProxy 实例被MapperProxy代理了,可以debug看看 userMapper实例就是 MapperProxy。
feiyeguohai
浏览: 297975 次
来自: 杭州
稍微明白了点,,有点萌萌哒
终于明白了,谢谢!
提供了两个链接还是有用的。
[flash=200,200][/flash]
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'

我要回帖

更多关于 mybatis mapper接口 的文章

 

随机推荐