Mybatis学习

核心组件

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;
  }

}
View Code

对于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;
  }

}
View Code

可以结合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;

 

select * from mytable where a=9 for update; 加排他锁

其他的命令

命令:

show open tables;

lock table table_name read ;表上读锁

lock table table_name write ;表上写锁

unlock tables ;

 

原文地址:https://www.cnblogs.com/lin7155/p/13751751.html