我们可以把 Spring、Mybatis、Dubbo 这样的大型框架或者一些公司内部的较核心项目,都可以称为复杂的系统。这样的工程也不在是初学编程手里的玩具项目,没有所谓的CRUD,更多时候要面对的都是对系统分层的结构设计和聚合逻辑功能的实现,再通过层层转换进行实现和调用。
创新互联公司是一家以网络技术公司,为中小企业提供网站维护、成都网站设计、成都网站制作、网站备案、服务器租用、域名申请、软件开发、成都小程序开发等企业互联网相关业务,是一家有着丰富的互联网运营推广经验的科技公司,有着多年的网站建站经验,致力于帮助中小企业在互联网让打出自已的品牌和口碑,让企业在互联网上打开一个面向全国乃至全球的业务窗口:建站欢迎来电:13518219792
这对于很多刚上道的小码农来说,会感觉非常难受,不知道要从哪下手,但又想着可以一口吃个胖子。其实这是不现实的,因为这些复杂系统中的框架中有太多的内容你还没用了解和熟悉,越是硬搞越难受,信心越受打击。
其实对于解决这类复杂的项目问题,核心在于要将主干问题点缩小,具体的手段包括:分治、抽象和知识。运用设计模式和设计原则等相关知识,把问题空间合理切割为若干子问题,问题越小也就越容易理解和处理。就像你可以把很多内容做成单个独立的案例一样,最终在进行聚合使用。
在上一章节我们初步的了解了怎么给一个接口类生成对应的映射器代理,并在代理中完成一些用户对接口方法的调用处理。虽然我们已经看到了一个核心逻辑的处理方式,但在使用上还是有些刀耕火种的,包括:需要编码告知 MapperProxyFactory 要对哪个接口进行代理,以及自己编写一个假的 SqlSession 处理实际调用接口时的返回结果。
那么结合这两块问题点,我们本章节要对映射器的注册提供注册机处理,满足用户可以在使用的时候提供一个包的路径即可完成扫描和注册。与此同时需要对 SqlSession 进行规范化处理,让它可以把我们的映射器代理和方法调用进行包装,建立一个生命周期模型结构,便于后续的内容的添加。
鉴于我们希望把整个工程包下关于数据库操作的 DAO 接口与 Mapper 映射器关联起来,那么就需要包装一个可以扫描包路径的完成映射的注册器类。
当然我们还要把上一章节中简化的 SqlSession 进行完善,由 SqlSession 定义数据库处理接口和获取 Mapper 对象的操作,并把它交给映射器代理类进行使用。这一部分是对上一章节内容的完善。
有了 SqlSession 以后,你可以把它理解成一种功能服务,有了功能服务以后还需要给这个功能服务提供一个工厂,来对外统一提供这类服务。比如我们在 Mybatis 中非常常见的操作,开启一个 SqlSession。整个设计可以如图 3-1:
图 3-1 映射器的注册和使用
mybatis-step-02
└── src
├── main
│ └── java
│ └── cn.bugstack.mybatis
│ ├── binding
│ │ ├── MapperProxy.java
│ │ ├── MapperProxyFactory.java
│ │ └── MapperRegistry.java
│ └── session
│ ├── defaults
│ │ ├── DefaultSqlSession.java
│ │ └── DefaultSqlSessionFactory.java
│ ├── SqlSession.java
│ └── SqlSessionFactory.java
└── test
└── java
└── cn.bugstack.mybatis.test.dao
├── dao
│ ├── ISchoolDao.java
│ └── IUserDao.java
└── ApiTest.java
工程源码:https://t.zsxq.com/bmqNFQ7。
映射器标准定义实现关系,如图 3-2:
图 3-2 映射器标准定义实现关系
源码详见:cn.bugstack.mybatis.binding.MapperRegistry。
public class MapperRegistry {
/**
* 将已添加的映射器代理加入到 HashMap
*/
private final Map, MapperProxyFactory>> knownMappers = new HashMap<>();
publicT getMapper(Class type, SqlSession sqlSession) {
final MapperProxyFactorymapperProxyFactory = (MapperProxyFactory ) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new RuntimeException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new RuntimeException("Error getting mapper instance. Cause: " + e, e);
}
}
publicvoid addMapper(Class type) {
/* Mapper 必须是接口才会注册 */
if (type.isInterface()) {
if (hasMapper(type)) {
// 如果重复添加了,报错
throw new RuntimeException("Type " + type + " is already known to the MapperRegistry.");
}
// 注册映射器代理工厂
knownMappers.put(type, new MapperProxyFactory<>(type));
}
}
public void addMappers(String packageName) {
Set> mapperSet = ClassScanner.scanPackage(packageName);
for (Class> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
}
源码详见:cn.bugstack.mybatis.session.SqlSession。
public interface SqlSession {
/**
* Retrieve a single row mapped from the statement key
* 根据指定的SqlID获取一条记录的封装对象
*
* @paramthe returned object type 封装之后的对象类型
* @param statement sqlID
* @return Mapped object 封装之后的对象
*/
T selectOne(String statement);
/**
* Retrieve a single row mapped from the statement key and parameter.
* 根据指定的SqlID获取一条记录的封装对象,只不过这个方法容许我们可以给sql传递一些参数
* 一般在实际使用中,这个参数传递的是pojo,或者Map或者ImmutableMap
*
* @paramthe returned object type
* @param statement Unique identifier matching the statement to use.
* @param parameter A parameter object to pass to the statement.
* @return Mapped object
*/
T selectOne(String statement, Object parameter);
/**
* Retrieves a mapper.
* 得到映射器,这个巧妙的使用了泛型,使得类型安全
*
* @paramthe mapper type
* @param type Mapper interface class
* @return a mapper bound to this SqlSession
*/
T getMapper(Class type);
}
在 SqlSession 中定义用来执行 SQL、获取映射器对象以及后续管理事务操作的标准接口。
目前这个接口中对于数据库的操作仅仅只提供了 selectOne,后续还会有相应其他方法的定义。
源码详见:cn.bugstack.mybatis.session.defaults。
public class DefaultSqlSession implements SqlSession {
/**
* 映射器注册机
*/
private MapperRegistry mapperRegistry;
@Override
publicT selectOne(String statement, Object parameter) {
return (T) ("你被代理了!" + "方法:" + statement + " 入参:" + parameter);
}
@Override
publicT getMapper(Class type) {
return mapperRegistry.getMapper(type, this);
}
}
源码详见:cn.bugstack.mybatis.session.SqlSessionFactory。
public interface SqlSessionFactory {
/**
* 打开一个 session
* @return SqlSession
*/
SqlSession openSession();
}
源码详见:cn.bugstack.mybatis.session.defaults.DefaultSqlSessionFactory
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final MapperRegistry mapperRegistry;
public DefaultSqlSessionFactory(MapperRegistry mapperRegistry) {
this.mapperRegistry = mapperRegistry;
}
@Override
public SqlSession openSession() {
return new DefaultSqlSession(mapperRegistry);
}
}
在同一个包路径下,提供2个以上的 Dao 接口:
public interface ISchoolDao {
String querySchoolName(String uId);
}
public interface IUserDao {
String queryUserName(String uId);
Integer queryUserAge(String uId);
}
@Test
public void test_MapperProxyFactory() {
// 1. 注册 Mapper
MapperRegistry registry = new MapperRegistry();
registry.addMappers("cn.bugstack.mybatis.test.dao");
// 2. 从 SqlSession 工厂获取 Session
SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(registry);
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3. 获取映射器对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
// 4. 测试验证
String res = userDao.queryUserName("10001");
logger.info("测试结果:{}", res);
}
在单元测试中通过注册机扫描包路径注册映射器代理对象,并把注册机传递给 SqlSessionFactory 工厂,这样完成一个链接过程。
之后通过 SqlSession 获取对应 DAO 类型的实现类,并进行方法验证。
测试结果
22:43:23.254 [main] INFO cn.bugstack.mybatis.test.ApiTest - 测试结果:你被代理了!方法:queryUserName 入参:[Ljava.lang.Object;@50cbc42f
Process finished with exit code 0
通过测试大家可以看到,目前我们已经在一个有 Mybatis 影子的手写 ORM 框架中,完成了代理类的注册和使用过程。
首先要从设计结构上了解工厂模式对具体功能结构的封装,屏蔽过程细节,限定上下文关系,把对外的使用减少耦合。
从这个过程上读者伙伴也能发现,使用 SqlSessionFactory 的工厂实现类包装了 SqlSession 的标准定义实现类,并由 SqlSession 完成对映射器对象的注册和使用。
本章学习要注意几个重要的知识点,包括:映射器、代理类、注册机、接口标准、工厂模式、上下文。这些工程开发的技巧都是在手写 Mybatis 的过程中非常重要的部分,了解和熟悉才能更好的在自己的业务中进行使用。
新闻名称:如何实现映射器的注册和使用
浏览地址:http://www.shufengxianlan.com/qtweb/news4/26354.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联