小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
主要记录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映射成数据库中的记录,因其简单灵活的特点, 所以被广泛使用.
最早使用的ORM框架(对象关系映射) 是Hibernate, 提供了POJO到数据库表的全套映射机制, 作为全自动化映射,使用时只需要定义好对应的POJO对象即可实现对数据库的操作.
Hibernate在以下场景不适用:
- 安全考虑, 只能对外提供特定的SQL获取的数据,数据库表结构需要保密.
- 系统数据处理量很大, 对性能要求严格,需要对SQL语句进行优化.
Mybatis相比Hibernate等全自动化框架, 属于半自动化的ORM实现. Mybatis只解决POJO与SQL之间的映射关系,具体的SQL语句,需要程序员自己来完成,然后Mybatis通过映射配置文件,将SQL所需的参数以及返回的结果字段映射到指定的POJO.
2 Mybatis框架组成
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);
}
复制代码
SqlSessionFactory,SqlSession是Mybatis接口层的核心类,尤其是SqlSession它实现了对于数据库操作常用的API, 具体源码可见org.apache.ibatis.session包.
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执行流程图
3 基础设施层
基础设施层中含有日志, IO,反射,异常,缓存,数据源&连接池,事务管理,类型映射等模块.
1 日志
Mybatis中定义了一套logging接口,根据常用的日志框架Log4j,log4j2,Apache Commons Log, java.util.logging,slf4j,stdout(控制台),都提供了相应的适配器.(源代码见org.apache.ibatis.logging包)
常见的日志框架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包)
4 异常
Mybatis中异常处理很简单.(源代码见org.apache.ibatis.exceptions包),其中常见抛出的异常是:org.apache.ibatis.exceptions.PersistenceException
5 缓存
Mybatis的缓存有两种: 一级缓存和两级缓存. 其中一级缓存默认是开启的,org.apache.ibatis.cache.impl.PerpetualCache对象中的HashMap就是一级缓存内容. 当我们的方法级别指定flushCache为true时,每次调用都会清理缓存, 相当于每次调用都重新读取数据库和写入缓存. flushCache为false时, 每次调用不清除缓存,即第二次调用获取数据时,直接从缓存中获取.
Mybatis的二级缓存,可以使用默认定义自带的, 也可以通过实现org.apache.ibatis.cache.Cache接口自定义.
6 数据源和连接池
Mybatis提供的数据源和连接池(源代码在org.apache.ibatis.datasource包), 其中PooledDataSource实现类包含了最大活动连接数,最大空闲连接数,最长取出时间,连接不够时的等待时间等.
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);
}
复制代码
近期评论