首页 > 代码库 > mybatis入门-mapper代理原理

mybatis入门-mapper代理原理

  原始dao层开发

    在我们用mybatis开发了第一个小程序后,相信大家对于dao层的开发其实已经有了一个大概的思路了。其他的配置不用变,将原来的test方法,该为dao的方法,将原来的返回值,直接在dao层进行一下接收就可以了。依然是老一套,先是大框架,然后写配置文件及UserMapper.xml文件这一系列的操作。如果不明白的,请参考本人博客《入门第一个程序》。

    我们需要做的就是,首先建立一个会话工厂(SqlSessionFactory),然后用会话工厂创建会话(SqlSession)。然后通过读取配置文件得到sql语句,执行,然后返回数据给dao层的对象。具体操作如下

    首先,在原来的基础上创建一个dao层的接口。

    技术分享

    然后写一个dao层接口的实现类

 1 public class UserDaoImpl implements UserDao {
 2  
 3 // 需要向dao实现类中注入SqlSessionFactory
 4 // 这里通过构造方法注入
 5 private SqlSessionFactory sqlSessionFactory;
 6  
 7 public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
 8 this.sqlSessionFactory = sqlSessionFactory;
 9 }
10  
11 @Override
12 public User findUserById(int id) throws Exception {
13 SqlSession sqlSession = sqlSessionFactory.openSession();
14  
15 User user = sqlSession.selectOne("test.findUserById", id);
16  
17 // 释放资源
18 sqlSession.close();
19  
20 return user;
21  
22 }
23  
24 @Override
25 public void insertUser(User user) throws Exception {
26 SqlSession sqlSession = sqlSessionFactory.openSession();
27  
28 //执行插入操作
29 sqlSession.insert("test.insertUser", user);
30  
31 // 提交事务
32 sqlSession.commit();
33  
34 // 释放资源
35 sqlSession.close();
36  
37 }
38  
39 @Override
40 public void deleteUser(int id) throws Exception {
41 SqlSession sqlSession = sqlSessionFactory.openSession();
42  
43 //执行插入操作
44 sqlSession.delete("test.deleteUser", id);
45  
46 // 提交事务
47 sqlSession.commit();
48  
49 // 释放资源
50 sqlSession.close();
51  
52 }
53  
54 }

然后就是对我们写的代码的测试

技术分享

    这样开发的话有一些问题:

    1.dao接口的实现类中存在大量的重复代码,这些代码会增加程序员的工作量。

    2.在实现类中,SqlSession的方法在调用映射文件中的sql语句时将statement的id(其中的"test.findUserById"便是statment的id)硬编码了。

    技术分享

    3.调用SqlSession方法时传入的变量,由于sqlsession方法使用泛型,即使变量类型传入的不是Dao方法中的参数类型,在编译的时候也不会报错,不利于程序的开发。

    

    为了解决这些问题,我们通过满足一些规范就将实体层的实现类的开发省去,由mybatis框架提供的代理实现类替我们去动态的调用我们的映射文件,然后去执行类中的方法。

  mapper代理方法的原理

    这之前,先声明一下,使用mapper代理的时候,命名和之前的不太一样。这里的接口命名由原来的** 改为**Mapper,比如说原来的User接口,现改为UserMapper。其内容和本质没有变,只是换了一个命名规范。除了接口外映射文件也是由User.xml改为UserMapper.xml其内容不变。

    mapper代理方法的思路是,程序员接着写持久层的接口。

    技术分享

 

    和对应每个接口的配置文件。

    技术分享

   在我们的原来的方法中需要再写一个接口的实现类,用来构建会话工厂(SqlSessionFactory)和对应的会话(SqlSession)然后通过会话去读取映射文件中的sql语句(这里不准确,不过可以这么理解)。那么我么就可以在这个实体类上做点文章了:

    1.我们可以创建一个代理类,在我们相应的接口被调用的时候,代理类就会创建这个对象,创建会话工厂和会话并不难。

    2.还有就是我们的sqlSession对象调用的方法名称,这个很好理解,对应的是我们映射文件的标签。

    3.那么接下来就是传入参数和接收返回值的问题了:

    3.1 传入参数:我们总共需要传入两个参数,一个是映射文件的statement的id的字符串,另外一个是执行需要的参数。 比较困难的是,这个字符串的生成问题。我们的每个方法到底对应的是哪个映射文件的statement的id。对于这个问题,mybatis给出了我们的解决方法:我们的实体层对象读到的字符串是这样的:test.insertUser

    可以将这个id分为两部分,其中的test是我们的映射文件中的namespace的值,这个值让它和持久层接口的地址一致,这样就使得代理对象可以通过接口的地址动态的生成id的前半部分,也就是test的这一部分;而后半部分则是让映射文件中的Statement的id和接口的方法一致。

    也就是说,用户在调用接口的某一个方法的时候,mybatis可以根据接口地址和对应的方法名生成sqlsession对象的方法传入的字符串。这样就解决了传入参数中的字符串的问题了。

    还有就是第二个问题:我们的传入具体参数,mybatis规定,传入参数类型必须和接口的传入参数类型一致。这样,我们的传入参数类型就搞定了。

    3.2传入参数搞定了以后,返回值也就搞定了,跟传入参数的规定一样,返回值的类型要求和接口返回值一致,这样就可以在代理类中将返回值传出了。

    这些搞定了,我们就可以使用代理对象代替我们的实体层具体实现类了。

    至此,这些问题就差不多都解决了,不过还有一个问题:我们的select方法返回值问题,代理对象到底是用selectont方法去查询数据库呢还是使用selectlist方法呢?这个跟我们的接口(mapper)的返回值类型有关——如果mapper方法返回单个pojo对象(非集合对象),代理对象内部通过selectOne查询数据库;如果mapper方法返回集合对象,代理对象内部通过selectList查询数据库。

  mapper代理方法

    原理讲完了,使用其实就基本上没啥要说的了。

    首先我们要创建一个接口。

    技术分享

    然后写对应接口的xml文件(截取部分,namespace处就不截图了)。

     技术分享

    最后别忘了在SqlMapConfig.xml文件中加载UserMapper.xml。

    技术分享

    测试,这里有必要说一下,我们的代理对象是通过SqlSession.getMapper(接口字节码文件)获取的。:

    技术分享

    至此,我们的mybatis的实体层的实体层实现类就可以省略不写了。不过这样做的坏处是加强了代码的耦合性,每个接口和每个映射文件必须对应。不过还是感觉通过遵守一定的规范而使得代码量大大减少更爽呢。

mybatis入门-mapper代理原理