SpringBoot系列4

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

主要记录Spring Boot集成Mybatis数据库层开发相关知识

1 Java EE分层架构简介

Java EE架构将系统分成以下: 模型(Model), 数据访问对象(Data Access Object, Dao), 业务逻辑(Service),控制器(Controller), 视图(View).

  • 模型层(Model),也称为领域对象层(Domain Object).业务逻辑的实现,依赖于业务领域模型. 主要由一系列的POJO组成.
  • 数据访问对象层(Data Access Object, Dao),提供对应Model层中的领域对象映射到数据库表的CRUD操作.
  • 业务逻辑层(Service), 为实现各种功能, 综合使用Model对象和Dao提供的CRUD接口.
  • 控制器层(Controller),提供控制器,用来拦截并调用Service层的接口处理用户请求,并把处理结果送到视图View层.
  • 视图层(View),由一系列视图模型页面组成.

2 Mybatis的简介

1 Mybatis框架

Mybatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis .

iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架,包括SQL Maps和Data Access Objects(DAO).它支持定制化SQL,存储过程以及高级映射.可以避免几乎所有的JDBC代码和手动设置参数以及获取结果集的操作.

Mybatis可以使用XML配置和注解两种方式来配置和映射原生信息,可以将接口和Java的POJO映射成数据库中的记录,因其简单灵活的特点, 所以被广泛使用.

image-20211006154832270

最早使用的ORM框架(对象关系映射) 是Hibernate, 提供了POJO到数据库表的全套映射机制, 作为全自动化映射,使用时只需要定义好对应的POJO对象即可实现对数据库的操作.

Hibernate在以下场景不适用:

  • 安全考虑, 只能对外提供特定的SQL获取的数据,数据库表结构需要保密.
  • 系统数据处理量很大, 对性能要求严格,需要对SQL语句进行优化.

Mybatis相比Hibernate等全自动化框架, 属于半自动化的ORM实现. Mybatis只解决POJO与SQL之间的映射关系,具体的SQL语句,需要程序员自己来完成,然后Mybatis通过映射配置文件,将SQL所需的参数以及返回的结果字段映射到指定的POJO.

2 Mybatis框架组成

image-20211006160307389

1 接口层

Mybatis封装了对数据库的访问,且把对数据库的会话和事务控制放到SqlSession对象中.

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root
复制代码

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!--引入外部properties文件  -->
    <properties resource="jdbc.properties"></properties>

    <typeAliases>
        <typeAlias alias="user" type="com.cf.entity.User"/>
    </typeAliases>

<!--    <plugins>
        <plugin interceptor=""></plugin>
    </plugins>-->
    
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/cf/entity/UserMapper.xml"/>
    </mappers>
</configuration>
复制代码

UserMapper

public interface UserMapper {

//    @Select("select * from USER where id=#{id}")
    User selectOne(Integer id);
}
复制代码

Test类

    public static void main(String[] args) throws IOException {
        // MyBatis配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 创建sqlSessionFactory工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 打开会话
        SqlSession session = sqlSessionFactory.openSession();
        User user;
        try {
            //执行select操作
            user = (User) session.selectOne("com.cf.entity.UserMapper.selectOne", 1);
        } finally {
            //关闭会话
            session.close();
        }
        System.out.println(user);
    }
复制代码

image-20211006162054203

SqlSessionFactory,SqlSession是Mybatis接口层的核心类,尤其是SqlSession它实现了对于数据库操作常用的API, 具体源码可见org.apache.ibatis.session包.

image-20211006162609548

SqlSession接口中定义了对数据库的基本CRUD操作,包括select,update,insert,delete等,以及数据库事务的回滚rollback和事务提交commit的操作,对于数据库连接,Configuration配置,缓存,Mapper的操作的API.

Configuration是Mybatis中核心配置类,定义了对数据库需要配置的所有属性. Configuration对象与DefaultSqlSessionFactory是一一对应的,即在一个DefaultSqlSessionFactory衍生的所有SqlSession作用域里,Configuration对象是全局唯一的.且接口SqlSession中还定义了getConfiguration方法,用来获取Configuration对象, 即我们不断可以使用配置文件,还可以在代码里面动态配置Configuration属性.

2 数据处理层

项目启动时,Mybatis会去解析以下文件:

  • SqlMapConfig.xml(或使用Java Config注解的方式)
  • Mapper.xml(或直接在接口方法上使用注解方式)

SqlMapConfig.xml是在XMLConfigBuilder中解析的.

Mapper.xml是在XMLMapperBuilder中解析的,其中XMLMapperBuilder对Statement的解析(SELECT,INSERT,UPDATE,DELETE标签)是由XMLStatementBuilder完成的, 而其中的核心解析代码是在parseStatementNode方法中.

Mybatis执行流程图

image-20211006175556343

3 基础设施层

基础设施层中含有日志, IO,反射,异常,缓存,数据源&连接池,事务管理,类型映射等模块.

1 日志

Mybatis中定义了一套logging接口,根据常用的日志框架Log4j,log4j2,Apache Commons Log, java.util.logging,slf4j,stdout(控制台),都提供了相应的适配器.(源代码见org.apache.ibatis.logging包)

image-20211006180100914

常见的日志框架Log级别一般有9种,即All,FINEST,FINER,FINE,CONFIG,INFO,WARNING,SEVERE,OFF.Mybatis统一为了4种, 即trace,debug,warn,error.

2 IO

Mybatis中I/O主要包含的功能:

  • 读取资源文件的API
  • 封装Mybatis自身所需要的ClassLoader和加载顺序

3 反射

Mybatis中,使用反射的场景很多,包括参数映射处理,结果映射处理,读取Class元数据,反射调用get/set等.(源代码见org.apache.ibatis.reflection包)

image-20211006180835051

4 异常

Mybatis中异常处理很简单.(源代码见org.apache.ibatis.exceptions包),其中常见抛出的异常是:org.apache.ibatis.exceptions.PersistenceException

image-20211006180950643

5 缓存

Mybatis的缓存有两种: 一级缓存和两级缓存. 其中一级缓存默认是开启的,org.apache.ibatis.cache.impl.PerpetualCache对象中的HashMap就是一级缓存内容. 当我们的方法级别指定flushCache为true时,每次调用都会清理缓存, 相当于每次调用都重新读取数据库和写入缓存. flushCache为false时, 每次调用不清除缓存,即第二次调用获取数据时,直接从缓存中获取.

Mybatis的二级缓存,可以使用默认定义自带的, 也可以通过实现org.apache.ibatis.cache.Cache接口自定义.

image-20211006181319578

6 数据源和连接池

Mybatis提供的数据源和连接池(源代码在org.apache.ibatis.datasource包), 其中PooledDataSource实现类包含了最大活动连接数,最大空闲连接数,最长取出时间,连接不够时的等待时间等.

image-20211006190651427

  public class PooledDataSource implements DataSource {

  ...
  // OPTIONAL CONFIGURATION FIELDS
  // 最大活动连接数
  protected int poolMaximumActiveConnections = 10;
  // 最大空闲连接数
  protected int poolMaximumIdleConnections = 5;
  // 最大检查时间
  protected int poolMaximumCheckoutTime = 20000;
  // 等待时间
  protected int poolTimeToWait = 20000;
  // 连接容错数
  protected int poolMaximumLocalBadConnectionTolerance = 3;
  protected String poolPingQuery = "NO PING QUERY SET";
  protected boolean poolPingEnabled;
  protected int poolPingConnectionsNotUsedFor;

  private int expectedConnectionTypeCode;
      
  ...
  }      
复制代码

7 事务

Mybatis中事务处理, org.apache.ibatis.session.TransactionIsolationLevel对象中定义了隔离级别:

public enum TransactionIsolationLevel {
    // 无事务
  NONE(Connection.TRANSACTION_NONE),
    // 读已提交
  READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
    // 读未提交
  READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
    // 可重复读
  REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
    // 顺序读
  SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);

  private final int level;

  TransactionIsolationLevel(int level) {
    this.level = level;
  }

  public int getLevel() {
    return level;
  }
}
复制代码

Mybatis事务不支持内嵌事务,因为事务在持久层,所以开发中经常使用Spring来管理事务的隔离.

8 类型映射

Mybatis的类型映射处理(源代码在org.apache.ibatis.type包中). 通过org.apache.ibatis.type.TypeAliasRegistry类来注册并维护与Java基本类型的映射.

public class TypeAliasRegistry {

    // 存储类型映射关系的HashMap
  private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<>();

  public TypeAliasRegistry() {
      // String类型
    registerAlias("string", String.class);
	  // 基本类型的包装类型
    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);
	  // 基本类型的包装数组类型
    registerAlias("byte[]", Byte[].class);
    registerAlias("long[]", Long[].class);
    registerAlias("short[]", Short[].class);
    registerAlias("int[]", Integer[].class);
    registerAlias("integer[]", Integer[].class);
    registerAlias("double[]", Double[].class);
    registerAlias("float[]", Float[].class);
    registerAlias("boolean[]", Boolean[].class);
	  // 基本类型
    registerAlias("_byte", byte.class);
    registerAlias("_long", long.class);
    registerAlias("_short", short.class);
    registerAlias("_int", int.class);
    registerAlias("_integer", int.class);
    registerAlias("_double", double.class);
    registerAlias("_float", float.class);
    registerAlias("_boolean", boolean.class);
	  // 基本数组类型
    registerAlias("_byte[]", byte[].class);
    registerAlias("_long[]", long[].class);
    registerAlias("_short[]", short[].class);
    registerAlias("_int[]", int[].class);
    registerAlias("_integer[]", int[].class);
    registerAlias("_double[]", double[].class);
    registerAlias("_float[]", float[].class);
    registerAlias("_boolean[]", boolean[].class);
	  // 日期,十进制,大整数,对象类等内置类型
    registerAlias("date", Date.class);
    registerAlias("decimal", BigDecimal.class);
    registerAlias("bigdecimal", BigDecimal.class);
    registerAlias("biginteger", BigInteger.class);
    registerAlias("object", Object.class);
 	  // 日期,十进制,大整数,对象类等内置类型的数组
    registerAlias("date[]", Date[].class);
    registerAlias("decimal[]", BigDecimal[].class);
    registerAlias("bigdecimal[]", BigDecimal[].class);
    registerAlias("biginteger[]", BigInteger[].class);
    registerAlias("object[]", Object[].class);
	  // 基本集合类类型 
    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class);
    registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);
      // 结果集类型
    registerAlias("ResultSet", ResultSet.class);
  }

复制代码

猜你喜欢

转载自juejin.im/post/7015913973430091789
今日推荐