核心组件
SqlSessionFactoryBuilder 是一个构造器,用于构造SqlSessionFactory
SqlSessionFactory 是一个与数据库交互的连接池,它是一个接口,有两个具体的实现类,DefaultSqlSessionFactory , 用于单线程,SqlSessionManager,用于多线程。
SqlSession 是一个连接,是一个核心接口,主要作用是 获取Mapper接口,发送SQL给数据库,控制数据库事务。
SQLMapper 是一个映射器,有Mapper接口和 映射xml文件组成 ,可以使用@Mapper注解表明这是一个mapper接口
如何构造SqlSessionFactory对象,有使用配置文件方式;也有使用Java代码方式构建,用到的类有Configuration ,这个类相当于配置文件。
生命周期:
- SqlSessionFactoryBuilder的用途是构建SqlSessionFactory,所以它在构造完成后,就要被销毁;
- SqlSessionFactory相当于一个数据库连接池,所以它的生命周期 可以说是整个mybatis的生命周期,它是一个单例模式,毕竟一个应用只需要一个数据库连接池;
- SqlSession相当于一次连接,只存在与一次请求过程中,请求完成后,释放连接;
- Mapper代表请求过程中的一次处理步骤,随着SqlSession关闭,它的生命周期也结束了。
配置文件构成
configuration
properties 属性
settings
typeAliases
typeHandlers 类处理器,
objectFactory 对象工厂
plugins
envrionments
environment
transactionManager
dataSource
databaseIdProvider 厂商标识
mappers
properties 配置
拥有三种方式:1 引入外部的properties属性文件,2 使用property子元素 3 程序代码传递 。推荐使用第一种,可以与配置文件分离,如果需要加密,可以采用最后一种进行加密。
settings 全局配置,用到的很少,比如缓存,级联设置,懒加载,自动映射。
typeAliases 别名,可以采用注解 @Alias("name") 系统自定义了一些别名, Java的大部分基础类型,它都支持数组形式的。
别名 | Java类型 |
_byte | byte 基本类型 |
byte | Byte对象 |
date | Date |
map/hashmap | Map/HashMap |
list/arraylist | List/ArrayList |
collection | Collection |
iterator | Iterator |
ResultSet | ResultSet |
object | Object |
mybatis 如何注册 别名的?
使用TypeAliasRegistry类的registerAlias方法,注册别名。 registerAlias("map",Map.class)
使用Configuration类 可以获取TypeAliasRegistry对象。
typeHandler 配置中 ,类型分为 javaType jdbcType, 这个类用于两个类型的转换。
在mybatis中typeHandler的实现,需要实现 TypeHandler 接口,这个接口有四个方法,一般需要继承 BaseTypeHandler 抽象类,这个类是泛型,泛型是返回类型,或者参数。
自定义的typeHandler 一般不用代码注册,而是使用 typeHandler子元素使用。
public interface TypeHandler<T> { void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException; /** * Gets the result. * * @param rs * the rs * @param columnName * Colunm name, when configuration <code>useColumnLabel</code> is <code>false</code> * @return the result * @throws SQLException * the SQL exception */ T getResult(ResultSet rs, String columnName) throws SQLException; T getResult(ResultSet rs, int columnIndex) throws SQLException; T getResult(CallableStatement cs, int columnIndex) throws SQLException; }
绝大多数情况,typeHandler一般用在枚举类型,而mybatis 已经自定义了两个类 支持枚举类型的转换。
EnumOrdinalTypeHandler mybatis根据枚举数组下标索引的方式进行匹配,它要求数据库返回一个整数作为下标,这也是默认的枚举转换类
EnumTypeHandler 采用字符串的形式匹配,它要求字符串返回一个字符串
mybatis也支持文件操作,支持BLOB字段,提供了一个BlobTypeHandler , 它在Java中对应的 Java类型是 byte[] 。
transactionManager 配置
在mybatis中,transactionManager 提供了一个 Transaction 接口,两个实现类 JdbcTransactionFactory 和 ManagedTransactionFactory ,它们对应spring提供的JDBC, MANAGED。
其中JDBC 采用 数据库的方式对事务进行操作 ;而MANAGED 它的提交和回滚不会对事务进程操作,而是把它交给容器处理。
如果想要实现一个自定义的事务工厂,那么 可以继承 TransactionFactory 接口。
public interface TransactionFactory { /** * Sets transaction factory custom properties. * @param props * the new properties */ default void setProperties(Properties props) { // NOP } /** * Creates a {@link Transaction} out of an existing connection. * @param conn Existing database connection * @return Transaction * @since 3.1.0 */ Transaction newTransaction(Connection conn); /** * Creates a {@link Transaction} out of a datasource. * @param dataSource DataSource to take the connection from * @param level Desired isolation level * @param autoCommit Desired autocommit * @return Transaction * @since 3.1.0 */ Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit); }
dataSource配置
mybatis提供了三个类,支持数据库配置。
PooledDataSourceFactory , UnpooledDataSourceFactory , JndiDataSourceFactory 。
相应的类型是POOLED, UNPOOLED, JNDI。
也支持使用第三方数据源,只需要提供一个自定义类 实现 DataSourceFactory 接口。
public interface DataSourceFactory { void setProperties(Properties props); DataSource getDataSource(); }
映射器
根元素mapper 的 命名空间 namespace="Mapper接口的全限定路径"
元素
select / insert / delete / update
sql
resultMap
cache / cache-ref
select元素
常用属性 id , parameterType , resultType ,resultMap , flushCache , useCache 。
全局配置
自动映射autoMappingBehavior 推荐使用,拥有三个值,NONE,PARTIAL默认值,对嵌套的的结果集不使用映射,FULL。
驼峰映射mapUnderscoreToCamelcase 非常严格,一般不会使用,
传递的参数
基本Java类型
map类型 一般推荐使用 ,#{key}
POJO类型 #{property}
@Param #{param}
分页参数 RowBounds类 , offSet是第几页,limit条数,只能在少量的查询中使用,加入参数即可,在sql语句中没有显示。
public class RowBounds { public static final int NO_ROW_OFFSET = 0; public static final int NO_ROW_LIMIT = Integer.MAX_VALUE; public static final RowBounds DEFAULT = new RowBounds(); private final int offset; private final int limit; public RowBounds() { this.offset = NO_ROW_OFFSET; this.limit = NO_ROW_LIMIT; } public RowBounds(int offset, int limit) { this.offset = offset; this.limit = limit; } public int getOffset() { return offset; } public int getLimit() { return limit; } }
对于5个以上的参数,推荐使用POJO。
返回结果集:
基本属性,POJO,resulMap,不推荐使用Map,因为还要定义主键,而且阅读性差。
insert元素
常用属性 useGeneratedKeys ,keyProperty, keyColumn
可以采用<selectKey> 自定义主键的增长规则
sql元素
<sql >使用标签定义SQL或者部分SQL,可以在其他的SQL里面使用 <include refid="sql_id“ /> 引用。
sql元素,也支持参数。
#{} 参数类型处理,使用?占位符。
${} 拼接字符串
级联
mybatis支持三种级联
鉴别器discriminator: 根据条件具体实现类的级联
一对一association
一对多collection
使用级联查询时,select语句的返回结果集,采用的是resultMap属性。
要自定义一个resultMap 集合,根据外键加载具体的类。
select为能更具column值查询具体的POJO类的方法,它是Mapper接口定义的方法
<association property="POJO类成员属性" column="外键" select="Mapper.methodByColumn" />
<collection property="POJO属性" column="外键" select="Mapper.methodByColumn" />
<discriminator javaType="值" column="值" >
<case value="" resultMap =" "/>
<discriminator/>
根据column查询返回的值不同,使用不同的类加载。
N+1问题
延迟加载,全局配置
lazyLoadingEnabled 全局加载,
aggressiveLazyLoading 层级加载,只有在同一层级的表,才会被加载
延迟加载,局部配置
fetchType属性,只有association ,collection拥有
fetchType属性有两个取值,分别是eager,lazy
eager 获得当前的POJO时,立刻加载对应的数据。
局部配置,会忽略全局配置
缓存
一级缓存 sqlSession层次 二级缓存 SqlSessionFactory层次
开启二级缓存,要在映射器
文件开头,加上<cache /> , 注意所以的POJO类都需要实现 序列化接口。
对于一级缓存,是默认开启的,同一个SqlSession对象如果执行两次相同的查询方法,只执行一次SQL语句,因为第二次的查询返回的是缓存。
但是对于不同的SqlSession对象,执行两次相同的查询方法,执行两次SQL语句,因为对于不同的SqlSession对象,缓存是不共享的。
缓存选项
Cache接口
public interface Cache { /** * @return The identifier of this cache */ String getId(); /** * @param key * Can be any object but usually it is a {@link CacheKey} * @param value * The result of a select. */ void putObject(Object key, Object value); /** * @param key * The key * @return The object stored in the cache. */ Object getObject(Object key); /** * As of 3.3.0 this method is only called during a rollback * for any previous value that was missing in the cache. * This lets any blocking cache to release the lock that * may have previously put on the key. * A blocking cache puts a lock when a value is null * and releases it when the value is back again. * This way other threads will wait for the value to be * available instead of hitting the database. * * * @param key * The key * @return Not used */ Object removeObject(Object key); /** * Clears this cache instance. */ void clear(); /** * Optional. This method is not called by the core. * * @return The number of elements stored in the cache (not its capacity). */ int getSize(); /** * Optional. As of 3.2.6 this method is no longer called by the core. * <p> * Any locking needed by the cache must be provided internally by the cache provider. * * @return A ReadWriteLock */ default ReadWriteLock getReadWriteLock() { return null; } }
可以结合redis数据库使用缓存,只有type属性指定自定义的缓存类,如RedisCache类
此外还要其他的属性size , eviction缓存策略 , readOnly 等
<cache type="RedisCache" >
</cache>
select,insert等语句 控制缓存 flushCache 是否刷新缓存,执行语句后,useCache是否使用缓存。
mybatis也支持存储过程 和 游标,当然 标配的 数据库是 oracle 。
动态SQL
if
choose( when otherwise)
trim(where set )
foreach
bind 元素可以用于模糊匹配,使用OGNL表达式自定义一个上下文变量,_parameter 表示传递的参数。
SqlSession的底层原理
四大对象
Executor:负责调度
StatementHandler : 核心,使用数据库的PreparedStatement执行操作。
ParameterHandler
ResultSetHandler
插件
改变底层SqlSession
public interface Interceptor { Object intercept(Invocation invocation) throws Throwable; default Object plugin(Object target) { return Plugin.wrap(target, this); } default void setProperties(Properties properties) { // NOP } }
辅助的类MetaObject类
数据库查询语句加锁,保证数据一致性
在高并发场景下,访问数据库的记录也需要加上锁,这是为了保证数据一致性。
多线程下,多个线程访问或者修改同一行记录,就会造成数据的混乱。
这时候,为了保证数据库 操作的 一致性,在并发场景下的 SQL 语句,需要加上锁。
Select加锁:
读锁:共享锁(Share Lock) SELECT ... LOCK IN SHARE MODE;
写锁:排他锁(eXclusive Lock) SELECT ... FOR UPDATE;
其他的命令
命令:
show open tables;
lock table table_name read ;表上读锁
lock table table_name write ;表上写锁
unlock tables ;