Mybatis如何做到dao.java与mapper.xml对应的

这里有两种方式解析,也就是我们平常xml文件放置位置的两种写法。
第一种是不加namespace,把xml文件放在和接口相同的路径下,同时xml的名字与接口名字相同,如接口名为Student.java,xml文件为Student.xml。在相同的包下。这种当时可以不加namespace.
第二种是加namespace,通过namespace来查找对应的xml.

转:http://www.jb51.net/article/116402.htm

本文主要介绍的是关于mybatis通过接口查找对应mapper.xml及方法执行的相关内容,下面话不多说,来看看详细的介绍:

在使用mybatis的时候,有一种方式是

1
BookMapper bookMapper = SqlSession().getMapper(BookMapper. class )

获取接口,然后调用接口的方法。只要方法名和对应的mapper.xml中的id名字相同,就可以执行sql。

那么接口是如何与mapper.xml对应的呢?

首先看下,在getMapper()方法是如何操作的。

在DefaultSqlSession.Java中调用了configuration.getMapper()

1
2
3
public  <T> T getMapper(Class<T> type) {
  return  configuration.<T>getMapper(type, this );
  }

在Configuration.java中调用了mapperRegistry.getMapper(type, sqlSession);

1
2
3
public  <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  return  mapperRegistry.getMapper(type, sqlSession);
  }

下面重点来了,在MapperRegistry.java中实现了动态代理

1
2
3
4
5
6
7
8
9
10
public  <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  final  MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  if  (mapperProxyFactory == null )
   throw  new  BindingException( "Type "  + type + " is not known to the MapperRegistry." );
  try  {
   return  mapperProxyFactory.newInstance(sqlSession);
  } catch  (Exception e) {
   throw  new  BindingException( "Error getting mapper instance. Cause: "  + e, e);
  }
  }

这个函数分两部分来看,首先是从map集合中获取接口代理,map集合的来源,第二部分获取代理后实例化,获取接口的方法,执行sql。

对于第一部分:集合的来源。

这个MapperRegistry.java中有个方法是addMappers();共有两个重载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public  void  addMappers(String packageName, Class<?> superType) {
  ResolverUtil<Class<?>> resolverUtil = new  ResolverUtil<Class<?>>();
  //通过包名,查找该包下所有的接口进行遍历,放入集合中
  resolverUtil.find( new  ResolverUtil.IsA(superType), packageName);
  Set<Class<? extends  Class<?>>> mapperSet = resolverUtil.getClasses();
  for  (Class<?> mapperClass : mapperSet) {
   addMapper(mapperClass);
  }
  }
 
  //解析包名下的接口
  public  void  addMappers(String packageName) {
  addMappers(packageName, Object. class );
  }

往上追溯该方法的调用是在SqlSessionFactory.build();时对配置文件的解析,其中对节点mappers的解析,这里先不赘述,

1
mapperElement(root.evalNode( "mappers" ));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private  void  mapperElement(XNode parent) throws  Exception {
  if  (parent != null ) {
   for  (XNode child : parent.getChildren()) {
   //使用package节点进行解析配置
   if  ( "package" .equals(child.getName())) {
    String mapperPackage = child.getStringAttribute( "name" );
    //注册包下的接口
    configuration.addMappers(mapperPackage);
   } else  {
   //使用mapper节点
    String resource = child.getStringAttribute( "resource" );
    String url = child.getStringAttribute( "url" );
    String mapperClass = child.getStringAttribute( "class" );
    if  (resource != null  && url == null  && mapperClass == null ) {
    ErrorContext.instance().resource(resource);
    InputStream inputStream = Resources.getResourceAsStream(resource);
    XMLMapperBuilder mapperParser = new  XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
    mapperParser.parse();
    } else  if  (resource == null  && url != null  && mapperClass == null ) {
    ErrorContext.instance().resource(url);
    InputStream inputStream = Resources.getUrlAsStream(url);
    XMLMapperBuilder mapperParser = new  XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
    mapperParser.parse();
    } else  if  (resource == null  && url == null  && mapperClass != null ) {
    Class<?> mapperInterface = Resources.classForName(mapperClass);
    configuration.addMapper(mapperInterface);
    } else  {
    throw  new  BuilderException( "A mapper element may only specify a url, resource or class, but not more than one." );
    }
   }
   }
  }
  }

这是调用addMapper()的顺序。

同时在改方法中还有一个方法很重要

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public  <T> void  addMapper(Class<T> type) {
if  (type.isInterface()) {
  if  (hasMapper(type)) {
  throw  new  BindingException( "Type "  + type + " is already known to the MapperRegistry." );
  }
  boolean  loadCompleted = false ;
  try  {
  knownMappers.put(type, new  MapperProxyFactory<T>(type));
  //根据接口名寻找同包下同名的xml或者mapper的namespace是该接口的xml
  //找到对用的xml后进行解析mapper节点里面的节点
  MapperAnnotationBuilder parser = new  MapperAnnotationBuilder(config, type);
  parser.parse();
  loadCompleted = true ;
  } finally  {
  if  (!loadCompleted) {
   knownMappers.remove(type);
  }
  }
}
}

这是通过接口的全路径来查找对应的xml。这里有两种方式解析,也就是我们平常xml文件放置位置的两种写法。

第一种是不加namespace,把xml文件放在和接口相同的路径下,同时xml的名字与接口名字相同,如接口名为Student.java,xml文件为Student.xml。在相同的包下。这种当时可以不加namespace.

第二种是加namespace,通过namespace来查找对应的xml.

到这就是接口名和xml的全部注册流程。

下面再说下第二部分就是通过动态代理获取接口名字来对应xml中的id。

主要有两个类MapperProxyFactory.java和MapperProxy.java

对于MapperProxyFactory.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public  class  MapperProxyFactory<T> {
 
  private  final  Class<T> mapperInterface;
  private  Map<Method, MapperMethod> methodCache = new  ConcurrentHashMap<Method, MapperMethod>();
  //构造函数,获取接口类
  public  MapperProxyFactory(Class<T> mapperInterface) {
  this .mapperInterface = mapperInterface;
  }
 
  public  Class<T> getMapperInterface() {
  return  mapperInterface;
  }
 
  public  Map<Method, MapperMethod> getMethodCache() {
  return  methodCache;
  }
 
  @SuppressWarnings ( "unchecked" )
  protected  T newInstance(MapperProxy<T> mapperProxy) {
  return  (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new  Class[] { mapperInterface }, mapperProxy);
  }
//供外部调用
  public  T newInstance(SqlSession sqlSession) {
  final  MapperProxy<T> mapperProxy = new  MapperProxy<T>(sqlSession, mapperInterface, methodCache);
  return  newInstance(mapperProxy);
  }
 
}

在MapperProxy.java中进行方法的执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 t) {
   throw  ExceptionUtil.unwrapThrowable(t);
   }
  }
  final  MapperMethod mapperMethod = cachedMapperMethod(method);
  //方法的执行
  return  mapperMethod.execute(sqlSession, args);
  }
 
  private  MapperMethod cachedMapperMethod(Method method) {
  MapperMethod mapperMethod = methodCache.get(method);
  if  (mapperMethod == null ) {
   mapperMethod = new  MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
   methodCache.put(method, mapperMethod);
  }
  return  mapperMethod;
  }

至此,就是mybatis所有接口和xml的加载,以及通过动态代理来进行接口的执行的过程。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 MyBatis Oracle Mapper.xml 示例: ```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.example.dao.UserDao"> <select id="getUserById" parameterType="int" resultType="com.example.entity.User"> SELECT * FROM users WHERE id = #{id} </select> <insert id="insertUser" parameterType="com.example.entity.User"> INSERT INTO users (name, age) VALUES (#{name}, #{age}) </insert> <update id="updateUser" parameterType="com.example.entity.User"> UPDATE users SET name = #{name}, age = #{age} WHERE id = #{id} </update> <delete id="deleteUser" parameterType="int"> DELETE FROM users WHERE id = #{id} </delete> </mapper> ``` 这个示例包含了四个 SQL 操作,分别是通过 `id` 获取用户信息、插入用户信息、更新用户信息和删除用户信息。这些操作都是通过 SQL 语句来实现的。 在 MyBatis 中,每个 Mapper.xml 文件都定义了一个 namespace,这个 namespace 用于在应用程序中唯一标识这个 Mapper.xml 文件对应DAO 接口。在这个示例中,namespace 是 `com.example.dao.UserDao`。 每个操作都有一个唯一的 `id`,这个 `id` 用于在代码中唯一标识这个操作。`parameterType` 和 `resultType` 分别表示操作的输入参数类型和输出结果类型。在这个示例中,`parameterType` 和 `resultType` 都是 Java 对象类型。 注意,这个示例中的 SQL 语句都是 Oracle 数据库的语法。如果你使用的是其他数据库,你需要根据相应的语法修改这些 SQL 语句。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值