12-MyBatis-SSM_MyBatisFramework加强

MyBatisFramework加强

MyBatis应用架构图

MyBatis是一个持久层框架,实现了对JDBC操作的封装,主要用于简化JDBC操作中的一些相对繁琐的步骤,例如参数的映射,结果的映射等。

思考:为何使用MyBatis实现数据持久层应用?

  1. 稳定/灵活(支持动态SQL),功能强大(池/日志/缓存)

  2. 解耦,SQL的可维护性高,可复用性高

  3. 学习及使用成本低


MyBatisAPI产品架构图

所有框架都要解决一些共性问题(持久化),都是一种半成品,MyBatis也不例外,它作为一个Java框架要解决相关问题;

如何解决问题?采用怎样的架构解决问题?这是我们要学习的一个点。

战略:挟天子以令诸侯。


DefaultSqlSessionFactory会话工厂对象创建流程图

DefaultSqlSessionFactory会话工厂对象创建步骤详解

注意:此创建过程指单独测试MyBatisFramework时,DefaultSqlSessionFactory的创建过程。

思考:如果用Spring整合MyBatis,DefaultSqlSessionFactory对象是如何创建的?

答:在SqlSessionFactoryBean类中重写了getObject方法,最终还是调用了SqlSessionFactoryBuilder类中的.build方法构建DefaultSqlSessionFactory会话工厂对象。

 

SqlSessionFactoryBuilder会话工厂建造者(请阅读此类源码)

提示:在package org.apache.ibatis.session;包下有个SqlSessionFactory顶层接口,此接口中定义了两个方法:查询源码

例码:InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

1.首先通过Resources对象将MyBatis全局配置文件读取到InputStream流对象中,形成字节信息;

2.在sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);之后开始执行配置信息封装

2.1XmlConfigBuilder对象从InputStream流对象中获取字节信息,将MyBatis全局配置文件封装到Environment环境对象中,并最终封装到Configuration配置对象中。

2.2XmlMapperBuilder对象从InputStream流对象中获取字节信息,将映射文件封装到MappedStatement映射声明对象中,

多个MappedStatement对象封装到Map集合中,并最终封装到Configuration配置对象中。

注意:在映射文件中一个元素对应一个MappedStatement对象,元素中封装了SQL语句;以namespace + id作为K,以SQL语句作为V。

3.SqlSessionFactoryBuilder对象需要依赖Configuration对象,通过.build方法构建DefaultSqlSessionFactory会话工厂对象。

 

分析单独测试MyBatis和Spring整合MyBatis的区别

注意:在Spring整合MyBatis之后,DefaultSqlSessionFactory会话工厂对象将交由Spring容器管理,且此对象是单例的;

拓展:在分布式软件架构中,DefaultSqlSessionFactory会话工厂对象可能是多例的,因为可能连接多个地方的不同数据库。

整合之后将取消mybatis-config.xml全局配置文件,文件中的数据源/事物等配置信息被封装到Druid阿里数据库连接池中。

思考:当DefaultSqlSessionFactory对象创建的时候,需要准备哪些元素?答:数据源/事务/连接池/映射文件

思考:当DefaultSqlSessionFactory对象被Spring整合的时候,需要哪些元素?答:数据源/事务/连接池/映射文件,其中前两者需要被封装到连接池中。

思考:如果没有数据源/事务/连接池/映射文件这些前提依赖条件,请问DefaultSqlSessionFactory对象能否被创建成功?

结论:整合了DefaultSqlSessionFactory对象,就相当于整合了整个MyBatisFramework,因为此对象是MyBatis的入口对象;

答:二者的区别就是创建DefaultSqlSessionFactory的方式不一样。


MyBatisFramework技术架构图

DefaultSqlSession会话对象的创建过程详解

思考:此会话对象在Spring整合MyBatis后会交由Spring容器管理吗?答:不会,因为一个事务对应一个DefaultSqlSession,一亿个用户对应一亿个DefaultSqlSession。

例码:SqlSession session = sqlSessionFactory.openSession();

源码:private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {}

通过openSession();方法获取DefaultSqlSession会话对象,此对象上层接口为SqlSession,顶层接口为java.lang.AutoCloseable,此接口中定义了void close()方法;

DefaultSqlSession实现类封装了Executor执行器对象和Configuration配置对象;其中定义了许多操作数据库(CRUD)的方法,根据永远找其他对象做事的面向对象宗旨:

DefaultSqlSession依赖于Executor接口,依赖接口用于解耦;同时还依赖Configuration配置对象,因为此对象中封装了全局配置文件和映射文件配置信息;

源码:import org.apache.ibatis.executor.Executor;

源码:private final Executor executor;

源码:import org.apache.ibatis.session.Configuration;

源码:private final Configuration configuration;

DefaultSqlSession类中还依赖了(引入了)MappedStatement类,用于封装SQL语句;

源码:import org.apache.ibatis.mapping.MappedStatement;

 

Configuration对象中封装了全局配置文件信息和映射文件信息,其中映射文件中包含了SQL语句;

DefaultSqlSession类中的源码:

  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

在对应的增删改查方法中通过configuration.getMappedStatement(statement);方法获取SQL语句并封装到MappedStatement对象中;

 

思考:Configuration对象从何而来?

答:由SqlSessionFactoryBuilder类中的build方法:return new DefaultSqlSessionFactory(config);再到→

DefaultSqlSessionFactory类中的openSessionFromDataSource方法:return new DefaultSqlSession(configuration, executor, autoCommit);

 

源码:MappedStatement ms = configuration.getMappedStatement(statement);

注意:statement = namespace + id = K

思考:如果是基于Mapper接口开发,SQL语句如何拿到?

方法:基于Mapper接口类全名找到映射文件中的namespace;根据方法名找到SQL元素id,最终定位SQL = V。

结论:不管是单独使用MyBatis做测试还是Spring整合MyBatis,在DefaultSqlSessionFactory会话工厂对象创建的时候,映射文件中的SQL语句已经被封装到Map集合中了。

思考:SQL语句被封装到哪个Map集合中了?答:在Configuration类中第167行源码,有个HashMap<String, String>();集合。

只是:使用Mapper接口开发和使用直接手动获取SQL开发,拿到SQL语句的方式不同;相同点是都是根据namespace + id的方式。

 

Executor执行器对象执行相应方法,传入SQL,返回结果;

思考:Executor对象从何而来?答:在DefaultSqlSessionFactory类中的openSessionFromDataSource方法中诞生;

源码:final Executor executor = configuration.newExecutor(tx, execType);

 

DefaultSqlSession类中源码:return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

注意:Executor执行器对象还不是真正的执行者,真正的执行者是StatementHandler接口下的实现类的对象;

query方法内部具体是如何执行的?后续步骤尚不明确,待补充......

但是当对应的Executor对象创建的时候,说明已经从数据库连接池中拿到了连接对象和SQL语句了;首先拿到连接对象,然后再拿到SQL语句;

源码中在BaseExecutor抽象类中依赖(引入)import org.apache.ibatis.logging.jdbc.ConnectionLogger;

 

Executor执行器对象家族继承体系

Executor顶层接口:

BaseExecutor抽象类:

  • BatchExecutor类:

  • ClosedExecutor静态最终类:

  • ReuseExecutor类:

  • SimpleExecutor类:对应MyBatis一级缓存

CachingExecutor类:对应MyBatis二级缓存

注意:如果是CachingExecutor二级缓存执行器,则无需再拿连接对象;说明CachingExecutor类中没有引入数据库连接对象,因为无需再访问数据库。


MyBatis基于Mapper接口开发流程图

为什么要基于接口开发?

MyBatis是一个优秀的持久层框架,它支持普通SQL查询,实现了对JDBC的封装,能够更好的完成对象关系映射(ORM);

在用MyBatis开发中看不到JDBC代码,为了让开发人员更专注于SQL语句,所以MyBatis提供了接口开发;

在某种程度上用接口开发降低了性能,但是增强了程序的灵活性。

从上图中可以看到,MyBatis底层会基于Mapper接口通过反射机制创建代理对象(Proxy),当代理对象调用相应的增删改查方法,底层还是依赖DefaultSqlSession会话对象;

例码:AuthorDao dao = session.getMapper(AuthorDao.class);

例码:Map<String, Object> map1 = dao.findAuthor(1);

注意:以上两行代码MyBatis底层本质上还是调用了DefaultSqlSession类中的public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {}方法

思考:是会话工厂对象先创建还是代理对象先创建?答:会话工厂先创建,因为一个会话工厂对象对应一个数据库。

思考:如果创建了代理对象,那么此代理对象受不受Spring容器管理?答:受Spring容器管理。

思考:当代理对象创建时,SQL语句有没有拿到?答:没有,因为取SQL的任务必须交给DefaultSqlSession会话对象。


代理对象的创建过程详解

注意:此代理对象的创建过程尚不明确,待补充......

什么事代理设计模式?

代理模式分为:动态代理和静态代理

动态代理:代理类不明确代理对象的具体类型,代理类中直接用Object类型来接受代理对象,这样灵活一些;

静态代理:代理类明确代理对象的具体类型,可以在代理类中依赖代理对象的顶层接口,这样效率高一些。


MyBatisFramework一级缓存&二级缓存

MyBatis一级缓存图

SimpleExecutor类:对应MyBatis一级缓存

MyBatis一级缓存(SqlSession),当SqlSession关闭时一级缓存失效。在同一个事务内部多次执行同一个查询,后续查询会从此缓存取数据。

原理:在SimpleExecutor类的抽象父类BaseExecutor中有个PerpetualCache属性,是用protected修饰的;

源码:protected PerpetualCache localCache;

在PerpetualCache对象中封装了一个HashMap散列表,用于存放从数据库查询的数据信息;

源码:private Map<Object, Object> cache = new HashMap<Object, Object>();

注意:底层以namespace + id作为K,以pojo缓存对象作为V,封装到散列表中。

测试:在pom.xml文件中添加log4j日志框架,以发送SQL的次数查看是否从缓存中调了数据。

 

MyBatis二级缓存图

CachingExecutor类:对应MyBatis二级缓存

MyBatis二级缓存(SqlSessionFactory),跨session共享数据时,可以打开和配置二级缓存。

注意:要想真正开启MyBatis二级缓存,必须要满足两个条件:

1.全局缓存开关必须为true,此配置需在MyBatis全局配置文件中配置;全局缓存开关默认是开启的,可到MyBatis官网查询验证。

例码:<setting name="cacheEnabled" value="true" /> <!-- 手动开/关二级缓存 -->

2.映射文件中必须加cache标签

例码:<cache readOnly="false"/>

注意:MyBatis的全局缓存开关默认是开启的,一级缓存默认也是开启的;一级缓存不能手动开关闭,二级缓存可以手动开关闭;

若想开启二级缓存必须满足以上两个条件,若全局缓存开关为false,那么就算在映射文件中添加cache标签也不起作用;

注意:底层以namespace + id作为K,以pojo缓存对象作为V,封装到散列表中。(错,要看具体二级缓存算法和数据结构)

测试:在pom.xml文件中添加log4j日志框架,以发送SQL的次数查看是否从缓存中调了数据。

 

cache标签详解:(以下代码可在MyBatis官网查看)

<cache
    eviction="FIFO" eviction:缓存数据结构
    flushInterval="60000" flushInterval:刷新频率
    size="512" size:缓存大小
    type="lruCache" type:默认缓存算法
    readOnly="true" readOnly:是否设置缓存只读
/>

readOnly="true" readOnly:是否设置缓存只读

注意:MyBatis二级缓存牵扯到pojo对象序列化的问题;

readOnly="true"表示每个SqlSession不会拷贝pojo对象到另一块内存空间,所以无需序列化pojo对象;

readOnly="false"表示每个SqlSession会拷贝pojo对象到另一块内存空间,必须要序列化pojo对象;

设置为true表示pojo无需序列化,这样效率会高一些;设置为false表示pojo必须序列化,这样数据安全一些,建议设置为false。


Spring整合MyBatis加强

Spring中有两大类型的Bean对象

1.实现FactoryBean<T>接口类型Bean对象:Spring底层会创建两个Bean对象,其中有个Bean对象没有实际作用;

注意:实现了FactoryBean<T>接口类型的Bean对象不支持延时加载;也就是一些复杂的Bean对象,不支持延时加载。

2.未实现FactoryBean<T>接口类型Bean对象:Spring底层只会创建一个Bean对象;

FactoryBean<T>接口中只定义了三个抽象方法,其中包括T getObject() throws Exception;,具体需查看源码。

说明:由此看出规则是Spring定义的,如果第三方框架中有工厂类,需要实现FactoryBean<T>接口。

 

Spring整合MyBatis_XML配置例码:

     <!--整合SqlSesionFactoryBean对象(通过此对象创建SqlSessionFactory) -->
     <bean id="sqlSessionFactory"  class="org.mybatis.spring.SqlSessionFactoryBean">
           <property name="DataSource" ref="dataSource" />
           <!-- 思考:如果单独测试MyBatis的时候,以上数据源/事务在哪里配置? -->
           <property name="MapperLocations"  value="classpath*:mapper/sys/*Mapper.xml" />
           <!-- 思考:如果单独测试MyBatis的时候,以上映射文件在哪里配置? -->
     </bean>
    

     <!-- 配置dao接口扫描,底层会基于dao接口创建这个接口的代理对象,这个代理 对象内部会通过mybatis访问数据库 -->
     <bean  class="org.mybatis.spring.mapper.MapperScannerConfigurer">
           <property name="BasePackage" value="com.jt.**.dao" />
           <property name="SqlSessionFactoryBeanName"  value="sqlSessionFactory" />
     </bean>

SqlSessionFactoryBean对象详解

思考:为什么要整合SqlSessionFactoryBean对象?答:为了创建DefaultSqlSessionFactory会话工厂对象。

说明:此类属于package org.mybatis.spring;包下,其实现了FactoryBean<T>接口。

源码:public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {

说明:Spring底层在IOC的时候会创建两个Bean对象。

此类部分属性源码:

private Resource configLocation;
private Configuration configuration;
private Resource[] mapperLocations;
private DataSource dataSource;
private TransactionFactory transactionFactory;
private Properties configurationProperties;
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
private SqlSessionFactory sqlSessionFactory;

从以上属性可以看出此类依赖Configuration对象/Resource[]数组(映射文件数组对象)/DataSource数据库连接池对象/SqlSessionFactoryBuilder会话工厂建造者对象等;

第339行源码:protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

第538行源码:return this.sqlSessionFactoryBuilder.build(configuration);

F3跟进build方法进入SqlSessionFactoryBuilder类内部源码:return new DefaultSqlSessionFactory(config);

说明:Spring底层首先会创建SqlSessionFactoryBean类型对象,然后通过此对象调用getObject()方法,最终创建DefaultSqlSessionFactory对象;

由此可以看出底层还是依赖了SqlSessionFactoryBuilder会话工厂建造者类来构建会话工厂对象。

总结:无论是单独测试MyBatis操作数据库,还是使用Spring整合MyBatis;DefaultSqlSessionFactory对象必须先被创建;

并且无论是前者还是后者,在会话工厂对象创建的时候,数据源/事务/连接池/映射文件等这些必要元素已经被读取并封装好,需要的时候方便取到;

只是:使用Mapper接口开发和使用直接手动获取SQL开发,拿到SQL语句的方式不同;相同点是都是根据namespace + id = K的方式取到SQL。


MyBatis数据结果处理_ORM(对象关系映射)

注意:以上笔记研究的是如何连接数据库并通过SQL语句操作数据库,以下笔记研究的是如何处理从数据库查询出来的结果。


MyBatisFramework开发拓展思维_单独测试MyBatis&Spring整合MyBatis对比

单独使用MyBatis做CRUD测试

  1. 手动获取SQL开发:需要通过SqlSessionFactoryBuilder.build(InputStream)方法,创建DefaultSqlSessionFactory对象。

  2. 使用Mapper接口开发

    1. 使用Mapper接口代理对象开发:通过DefaultSqlSession的getMapper(Mapper Interface)方法,创建代理对象。

    2. 手写Mapper接口实现类开发:需要依赖DefaultSqlSessionFactory对象。

使用Spring整合MyBatis开发

  1. 手动获取SQL开发:Spring需要整合SqlSessionFactoryBean对象,DefaultSqlSessionFactory需要交给Spring容器管理。

  2. 使用Mapper接口开发

    1. 使用Mapper接口代理对象开发:Spring需要整合MapperScannerConfigurer对象,Spring扫描Mapper接口创建代理对象,然后交由Spring管理。

    2. 手写Mapper接口实现类开发:此实现类对象需要交给Spring容器管理。

注意:如果使用Spring整合MyBatis手写Mapper接口实现类开发,想要从Spring容器中获取实现类的对象,要么采用配置Bean标签的方式IOC,要么采用配置注解的方式IOC;

配置注解的方式前提是要配置包扫描器;如果使用Mapper接口代理对象开发,以上两部都无需配置,只要配置MapperScannerConfigurer对象Bean标签即可。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

从码农到码到成功

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值