MyBatis学习之路

MyBatis

1.     简介

1.1.      基于ORM半自动化轻量级持久层框架

1.1.1.   底层是对jdbc代码进行封装、对jdbc中频繁创建释放数据库连接、硬编码、手动封装参数和结果集等问题进行了规避和解决(2010年6月由apache迁移大google ibatis3.X更名为mybatis)

1.1.2.   ORM对象-关系映射

  • 基于XML定义POJO与数据库对应关系
  • 需要手动编写SQL语句
  • 启动时需要资源少
  • SQL编写在XML配置文件中解决了硬编码问题
  • SQL和Java代码分开功能边界清晰
  • 手动编写SQL语句、灵活性较高可对SQL语句进行优化提高执行效率
  • selectOne(statementId,args)
  • selectList(statementId,args)
  • insert(statementId,args)
  • update(statementId,args)
  • delete(statementId,args)
  •     

1.1.3.   半自动化

1.1.4.   轻量

1.1.5.   优点

1.1.6.   Hibernate 只需操作POJO持久化对象就可完成对数据库的操作,无需编写SQL语句,功能强大,失去了对SQL语句优化的可能性

2.     基本开发步骤

2.1.       导入MyBatis相关jar包

2.2.      创建sqlMapConfig.xml文件

2.3.      创建POJO实体类

2.4.      创建实体类对应的mapper.xml映射文件

2.5.      基本CRUD操作

2.5.1.   构建sqlSessionFactory对象获取sqlSession对象访问数据库  【调用sqlSession相关方法】

3.     配置文件

3.1.      映射配置文件mapper.xml

3.1.1.   dtd约束头

3.1.2.   根标签 mapper
属性namespace定义命名空间

•      select标签【定义查询操作】
属性 id 定义 唯一标识  和 namespace共同组成
属性parameterType 定义参数类型
属性resultType 定义返回值类型
属性resultMap自定义返回值 返回结果不是POJO基本表类型

•      resultMap标签(自定义返回结果集、复杂映射开发、多表操作)
属性 id=“xx” 定义唯一标识
type=“xx” 主表全限定类名

•      OneToOne

•      result标签定义主表POJO实体与表【查询结果集】字段对应关系
属性 property=“” 定义POJO字段名
属性column=“” 定义POJO对应的查询结果集字段名

•      association标签 定义关联表POJO实体与表【查询结果集】字段对应关系
属性property=“” 对应主表POJO实体里面定义的关联表的POJO实体对象名称
属性javaType=“” 关联表的POJO实体的全限定类名

•      result标签定义关联表POJO实体与关联表字段对应关系
属性 property=“” 定义关联表POJO字段名
属性column=“” 定义关联表POJO对应的字段名

•      OneToMany

•      result标签定义主表POJO实体与表【查询结果集】字段对应关系
属性 property=“” 定义POJO字段名
属性column=“” 定义POJO对应的查询结果集字段名

•      collection标签 定义子表POJO实体与表【查询结果集】字段对应关系
属性property=“” 对应主表POJO实体里面定义的子表的POJO实体对象名称
属性ofType=“” 子表的POJO实体的全限定类名

•      result标签定义子表POJO实体与子表字段对应关系
属性 property=“” 定义子表POJO字段名
属性column=“” 定义子表POJO对应的字段名

•      ManyToMany
【实际上是双向的一对多查询】

•      insert标签【定义添加操作】
相同属性同select
属性useGeneratedKeys=true/false制定是否使用数据库主键自增生成策略
属性keyProperty 指定 POJO对应主键字段名
属性keyColumn  指定数据库表对应字段名

•      selectKey标签【指定序列类型的数据库主键生成】
属性keyProperty 指定 POJO对应主键字段名
属性keyColumn  指定数据库表对应字段名
属性order =before/after 定义执行前后顺序
属性resultType 定义返回值类型
标签内容【select sequence.nextval from dual】

•      update标签【定义修改操作】属性同上

•      delete标签【定义删除操作】属性同上

•      标签中的SQL
【select * from user where id =#{id}】
【 #{}   中的字段名与 POJO字段名对应 】

•      动态SQL: <where>标签
包裹if标签 自动为SQL语句
加上where关键字并去除SQL语句中第一个and

•      动态SQL: if标签
属性:test=“” 判断POJO属性从而动态拼接SQL

•      动态SQL: foreach标签 【用于条件多值查询】
属性collection=“array” 定义 参数类型
属性open = “id in (”  定义 SQL 开始内容
属性close = ")" 定义SQL结束内容
属性item= “id” 定义 接收变量
属性 separator = “,”定义SQL多值分隔符

•      动态SQL:sql标签【SQL片段抽取】
属性id=“xx” 定义SQL片段唯一标识
在需要调用的地方使用
include标签 【引用SQL片段】
属性refid=“xxx” 值为引用sql片段的id唯一标识

3.2.      核心配置文件sqlMapConfig.xml

3.2.1.   dtd约束头

3.2.2.   根标签configuration
根标签下的子标签有固定的从上到下的编写顺序

  • properties标签【加载外部的数据配置文件】
    属性 resource = “配置文件路径”
    <properties resource="jdbc.properties">
    </properties>

•      创建properties配置文件【定义数据库驱动、用户名、密码等相关信息】

 

•      EL表达式注入运行环境中的数据源

 

  • settings【mybatis全局参数设置】

•      setting具体参数设置
属性name="cacheEnabled"  配置是否开启二级缓存 值为true/false
属性name="logImpl"  配置是否开启二级缓存 值为LOG4J/SLF4J/..  制定日志文件输出
....... 各种全局配置

  • typeAliases标签 【给POJO实体类的全限定类名起别名】

•      typeAlias标签【给单独的实体起别名】
属性 type = “全限定类名”
属性 alias = “别名”

•      package 标签 【批量起别名,类名为该包下所有类的类名,不区分大小写】
属性 name = "全限定包名"

•      MyBatis默认定义的常用数据类型别名
string 【String】、long【Long】
int 【Integer】、double【Double】
boolean【Boolean】

  • plugins标签【用于引入自定义插件或者MyBatis第三方插件】

•      plugin标签【定义具体要引入的插件】
属性interceptor=“xx”要引入插件的全限定类名

•      property标签【用于定义插件的一些初始化参数】
属性name='xx' 定义参数名称
属性 value=“xx” 定义参数值

  • environments标签【定义运行环境支持多环境配置】
    属性default=development 定义默认运行环境
    可使用子标签定义多种环境
    【开发环境】【测试环境】【生成环境】

•      environment标签
属性id=“development” 定义唯一标识

•      transactionManager标签【事务管理器】定义事务管理方式 【2种】
属性 type=JDBC  定义事务使用JDBC进行提交/回滚,依赖于数据源获取的连接管理事务
属性 type=MANAGED  对事物什么都不处理,交由容器【Tomcat/jboss】来管理事务
一般不用这种模式

•      dataSource标签【定义mybatis数据源】
属性 type【3种】
type=UNPOOLED 【每次请求数据时获取连接用完关闭】
type=POOLED 【使用连接池管理JDBC连接】
type=JNDI 【使用web容器管理】

•      property标签定义数据库驱动、用户名等连接信息

  • mappers标签【定义加载映射配置文件】

•      mapper标签【引入映射配置文件3种属性方式引入】
属性 resource=“XML文件项目相对路径路径”
属性 url=“盘符文件路径”
属性 class = “接口/实现类的全限定类名”

•      package标签【基于package目录自动扫描引入】
属性 name="全限定包名"
要求接口和对应的的映射配置文件同包同名

4.     相关API

4.1.      Resource工具类【配置文件的加载,把配置文件加载成字节输入流】
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

 

4.2.      解析配置文件并创建SQLSessionFactory工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

 

4.3.      生成SQLSession
sqlSession = sqlSessionFactory.openSession();
【默认开启一个事务但不会自动提交】
【增、删、改、时要手动提交事务】
.openSession(true); 自动提交事务

 

4.4.      SQLSession调用方法

 

4.4.1.   selectOne(statementId,args)

4.4.2.   selectList(statementId,args)

4.4.3.   insert(statementId,args)

4.4.4.   update(statementId,args)

4.4.5.   delete(statementId,args)

4.4.6.   getMapper(接口.class)返回接口的代理类对象

4.5.      sqlSession.commit();

 

5.     DAO层传统开发方式

5.1.      手动编写接口及其实现类
在实现类中重写接口方法调用相关API
创建实现类对象调用实现类中方法完成数据访问

6.     DAO层代理开发方式

6.1.      只需编写mapper接口【相当于Dao接口】
由MyBatis框架通过JDK动态代理自动生成Mapper接口代理对象【相当于手动编写的Dao接口实现类】
使用相关API中的getMapper(接口.class)调用
编写mapper接口中的方法完成数据访问

6.1.1.   遵循以下规则
mapper.xml中的namespace与mapper接口的全限定名相同
mapper接口中的方法名和mapper.xml文件中statementId相同
mapper接口方法的输入参数和mapper.xml文件中parameterType的类型相同
mapper接口方法的返回值类型和mapper.xml文件中resultType的类型相同

7.     注解开发
不用再编写映射配置文件

7.1.      @Insert【实现新增】

7.1.1.   @Insert("insert into user values(#{id},#{username})")

7.2.      @Update【实现修改】

7.2.1.   @Update("update user set username = #{username} where id = #{id}")

7.3.      @Delete【实现删除】

7.3.1.       @Delete("delete from user where id = #{id}")

7.4.      @select【实现查询】

7.4.1.    @Select("select * from user")

7.5.      @Result【实现结果集封装】

7.6.      @Results【与@Result一起使用】封装多个结果集

7.7.      @One【实现一对一结果集封装】

7.7.1.   @Select("select * from orders") 
@Results({
            @Result(property = "id",column = "id"),
            @Result(property = "orderTime",column = "orderTime"),
            @Result(property = "total",column = "total"),
            @Result(property = "user",column = "uid",javaType = User.class,
                    one=@One(select = "com.lagou.mapper.IUserMapper.findUserById"))
    })

7.8.      @Many【实现一对多结果集封装】

7.8.1.       @Select("select * from user")
    @Results({
            @Result(property = "id",column = "id"),
            @Result(property = "username",column = "username"),
            @Result(property = "orderList",column = "id",javaType = List.class,
                many=@Many(select = "com.lagou.mapper.IOrderMapper.findOrderByUid"))
    })

8.                         MyBatis缓存
【缓存就是内存中的数据,是对数据中查询结果的保存,减少与数据库的交互提高查询效率】

8.1.      一级缓存【SqlSession级别】默认开启

8.1.1.   底层数据结构是一个hashMap集合,不同的SqlSession之间互补影响
map中的CacheKey组成【statementId、parameters、boundSql、rowBounds】
map中的value为查询结果实体对象

8.1.2.   添加/删除/修改/手动提交事务 都会自动清空一级缓存
手动调用clearCache()方法也可清空一级缓存

8.2.      二级缓存【mapper/namespace 级别】不同sqlSession共享同一个namespace二级缓存
默认关闭
需要在sqlMapConfig.xml配置文件中使用settings标签手动开启
单独开启二级缓存 可在映射配置文件中使用cache标签或者使用注解方式在接口类上使用注解@CacheNamespace
@CacheNamespace 可手动制定具体的缓存实现类 方式为
@CacheNamespace(implementation = RedisCache.class)

8.2.1.   默认使用PerpetualCache类实现二级缓存
同样使用haspMap作为底层数据结构缓存数据

8.2.2.   二级缓存缓存介质是多样的不一定缓存在内存中所以POJO类都必须实现序列化接口

8.2.3.   可以在mappe.xml文件 select标签中使用
属性useCache=true/false 单独开启或禁用二级缓存
属性flushCache=true/false 单独设置是否刷新二级缓存

8.3.      使用redis实现二级缓存

8.3.1.   导入mybatis-redis.jar

8.3.2.   @CacheNamespace(implementation = RedisCache.class)手动指定二级缓存为redis二级缓存

8.3.3.   创建redis.properties定义redis连接信息

8.3.4.   底层使用hash结构存储

9.     MyBatis插件
本质是对MyBatis功能进行拓展
增强核心组件的功能,其本质就是使用了拦截器对核心组件进行拦截,基于JDK动态代理增强核心组件

9.1.      Executor执行器

9.2.       StatementHandler 【SQL语句构建器】

9.2.1.   ParameterHandler参数处理器

9.2.2.   ResultHandler结果集处理器

10. 自定义MyBatis插件

10.1.  创建一个类ExamplePlugin
实现MyBatisplugin包下的Interceptor接口

10.2.  在ExamplePlugin类上使用注解@Intercepts
将该类加入MyBatis拦截器链中
@Signature 定义要拦截的哪个核心组件类中的哪个方法

10.2.1.@Intercepts({
        @Signature(type= StatementHandler.class,
                  method = "prepare",
                  args = {Connection.class,Integer.class})
})

  • setProperties(Properties properties)方法用于获取配置文件中初始化参数
  •  plugin(Object target) 方法{
     Object wrap = Plugin.wrap(target, this);
    }
    把当前的拦截器生成代理存到拦截器链中
  • intercept(Invocation invocation)方法
    对方法进行增强并继续执行原方法
     public Object intercept(Invocation invocation) throws Throwable {
            System.out.println("对方法进行了增强....");
            return invocation.proceed(); //原方法执行
        }
  • 基于XML配置
  • 基于注解
  • Executor
    MyBatis执行器负责动态语句生成、
    查询缓存的维护

10.3.  使用plugins标签将该插件定义
在sqlMapConfig.xml这样mybatis在启动时才可以加载该插件

10.3.1. <plugins>
<plugin interceptor="com.lagou.plugin.MyPlugin">
 </plugin>
</plugins>

11. MyBatis第三方分页插件pageHelper

11.1.  导入pageHelper相关jar包

11.2.  将pageHandler插件加入sqlMapConfig.xml
不同数据库的分页实现方式不一样
所以定义属性描述对应数据库的方言

11.2.1.        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql"/>
        </plugin>

11.3.  编写相关调用方法

11.3.1.PageHelper.startPage(1,1);
List<User> users = userMapper.selectUser();
PageInfo<User> pageInfo = new PageInfo<>(users);
pageInfo.getTotal()、pageInfo.getPages()、pageInfo.getPageNum()、pageInfo.getPageSize()

12. MyBatis第三方插件通用mapper
主要用于实现单表CRUD
不需要再编写SQL语句
不需要在mapper接口编写具体方法

12.1.  导入通用mapper相关jar包

12.2.  将通用mappe插件加入sqlMapConfig.xml

12.2.1.        <plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
            <!--指定当前通用mapper接口使用的是哪一个-->
            <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
        </plugin>

12.3.  使用注解@Table在POJO实体类上描述与表的对应关系

12.3.1.@Table(name = "user")

12.4.  使用注解@Id和@GeneratedValue
在POJO实体类上主键字段描述与表的的对应关系及主键生成策略

12.4.1.    @Id //对应的是注解id
    @GeneratedValue(strategy = GenerationType.IDENTITY) //设置主键的生成策略
    private Integer id;

12.5.  创建具体mapper接口类
继承通用Mapper<User>类即可实现功能

12.6.  编写相关调用方法

12.6.1.UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setId(1);
        User user1 = mapper.selectOne(user);
        System.out.println(user1);

12.7.  Example方法实现CRUD

12.7.1.Example example = new Example(User.class);
        example.createCriteria().andEqualTo("id",1);
        List<User> users = mapper.selectByExample(example);
        for (User user2 : users) {
            System.out.println(user2);
        }

13. MyBatis源码分析

13.1.  接口层
提供对外访问的API接口

13.1.1.基于statementId

13.1.2. 基于mappe接口动态代理

13.2.  数据处理层

13.2.1.ParameterHandler
参数映射

13.2.2.SqlSource
SQL解析

13.2.3.Executor
SQL执行

13.2.4.ResultSetHandler
结果处理和映射

13.3.  框架支撑层

13.3.1.SQL语句配置方式

13.3.2.事务管理

13.3.3.连接池管理

13.3.4.缓存机制

13.4.  层次结构和调用流程

13.4.1.sqlSession
顶层API接口作为会话访问完成CRUD

•      StatementHandler
负责处理jdbcstatement的交互、设置参数返回结果集转换

•      ParameterHandler
将传递参数对 [ JDBC ] statement进行设置

•      TypeHandler
jdbc 类型 与java类型的专类

•      ResultSetHandler
将 【jdbc】resultSet结果集装换为list返回

•      TypeHandler
jdbc 类型 与java类型的专类

13.4.2.configuration

  • MappedStatement

•      SqlSouce

•      ResultMap

上一篇:Mybatis 一对一 一对多 嵌套查询 嵌套结果


下一篇:阿里巴巴编程规范——POJO使用包装类型