mybatis官方文档解读

官网:https://mybatis.org/mybatis-3/zh/getting-started.html

mybatis是一个jar包,我们引入这个jar包到我们的项目中后,可以通过它方便的操作数据库。要是用mybatis,我们只要把mybatis-x.x.x.jar置于我们的CLASSPATH下即可。

1 简单示例

我们创建一个普通的maven项目,其结构如下所示

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.szj</groupId>
  <artifactId>mybatis-study</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>pom</packaging>

  <name>mybatis-study</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.11</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.24</version>
    </dependency>
  </dependencies>
</project>

MybatisMain.java

public class MybatisMain {
    public static void main(String[] args) {
        try {
            System.out.println("开始从配置文件读取数据库连接串");
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            System.out.println("开始创建SqlSessionFactory");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            System.out.println("打开一个SqlSession");
            SqlSession sqlSession = sqlSessionFactory.openSession();
            System.out.println("开始查询数据库");
            BlogDao blogDao = sqlSession.selectOne("com.szj.mapper.BlogMapper.selectBlog", 1);
            System.out.println("查询结果:"+blogDao.getContext());
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://192.168.3.69:3306/szj"/>
                <property name="username" value="root"/>
                <property name="password" value="Abc123..."/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/BlogMapper.xml"/>
    </mappers>
</configuration>

BlogMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.szj.mapper.BlogMapper">
    <select id="selectBlog" resultType="com.szj.dao.BlogDao">
        select * from blog where id = #{id}
    </select>
</mapper>

另外,我们要创建一个数据库(略)

值得注意的是:虽然我们使用maven来管理我们的项目,但是通过pom文件我们可以看出,我们没有对构建过程做特殊干预(使用 <build> 标签配置构建过程)。因此,项目会使用maven默认给我们提供的构建方案(默认方案可以通过执行 mvn help:effective-pom 命令查看)。因此,我们执行mvn clean install命令,maven将会按照默认方式把我们的项目构建到我们配置的本地仓库里面去。

而我们执行main方法,需要在当前项目的target路径下面生成对应的类和配置文件。

因此,我们需要分三步:

mvn clean //清理target目录
mvn compiler:compile //编译
mvn resources:resources //将资源文件夹拷贝到target目录

对于上面的maven插件不熟悉的可以去了解一下maven的插件机制和maven执行命令的原理。

通过上面命令,我们会把java编译为class文件并拷贝到target路径下。会把resources文件拷贝到target路径下。这样做的目的是供mybatis找到资源文件,加载数据库连接以及mapper映射。

我们先看一下运行效果

开始从配置文件读取数据库连接串
开始创建SqlSessionFactory
打开一个SqlSession
开始查询数据库
查询结果:balabala

通过这里我们可以看到mybatis的运行原理:mybatis中为我们提供了一个工具类Resources,它可以加载classpath下的xml文件读入到InputStream中,然后我们通过这个InputStream得到一个SqlSessionFactory。然后通过这个SqlSessionFactory打开一个SqlSession,然后结合Mapper.xml文件,最终查询到数据库结果。

这也是为什么我们必须把resources文件也编译到target路径下的原因,因为mybatis会在CLASSPATH下查找这些resources资源文件。

还要注意的是:

在实际使用时,我们往往使用单例模式创建一个SqlSessionFactory,然后这个对象会在应用中一直存在。SqlSessionFactory是mybatis的核心,通过SqlSessionFactory可以创建SqlSession。

SqlSession用于打开数据库连接、查询数据。SqlSession应该在使用完成后就关闭,不然这个数据库连接将一直存在,容易发生死锁。

2 SqlSessionFactory的构建

我们可以看一下SqlSessionFactoryBuilder为我们提供的build方法,这些方法分两类,一类是基于xml配置文件的输入流,另一类是基于Java配置类Configuration

2.1 基于xml构建

上面示例中的代码演示的就是基于CLASSPATH下的xml配置加载SqlSessionFactory实例,此外还可以基于文件路径或者file://开头的uri加载,不过不常用

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
System.out.println("开始创建SqlSessionFactory");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

2.2 基于java配置类

DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

3 SqlSession访问数据库的新旧两种方式

3.1 旧方式

SqlSession sqlSession = sqlSessionFactory.openSession();
Blog blog = sqlSession.selectOne("com.szj.mapper.BlogMapper.selectBlog", 1);

3.2 新方式

新方式中,我们定义了一个额外的接口BlogMapper,这个接口用于和同名的BlogMapper.xml配合使用

SqlSession sqlSession = sqlSessionFactory.openSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = blogMapper.selectBlog(1);

其中BlogMapper的定义如下(注意,没有@Repository注解,因为这个注解是spring的东西,我们还没有和spring整合)

public interface BlogMapper {
    Blog selectBlog(int id);
}

对应的BlogMapper.xml

<mapper namespace="com.szj.mapper.BlogMapper">
    <select id="selectBlog" parameterType="int" resultType="com.szj.entity.Blog">
        select * from blog where id = #{id}
    </select>
</mapper>

 其执行结果和旧方式一样。

这种新的调用方式有很多优势,比如不依赖于字符串字面值(比如"com.szj.mapper.BlogMapper.selectBlog")。

3.3 使用注解来映射简单语句

对于映射器类BlogMapper,还有一种更为简洁的方式来完成语句的映射,完全不依赖于xml。即使用注解来配置,例子:

public interface BlogMapper {
  @Select("SELECT * FROM blog WHERE id = #{id}")
  Blog selectBlog(int id);
}

注意,BlogMapper接口上仍然没有@Repository注解

运行结果一样

开始从配置文件读取数据库连接串
开始创建SqlSessionFactory
打开一个SqlSession
开始查询数据库
查询结果:{"context":"this is the main context","id":1,"username":"szj"}

使用注解方式比较简洁直观,但是如果映射的sql语句比较复杂,注解就显得力不从心了,此时还是需要xml映射sql语句

4 xml配置文件

mybatis的配置信息可以配置在xml配置文件中,比如上例中的mybatis-config.xml

其结构如下

  • configuration(配置)
    • properties(属性)
    • settings(设置)
    • typeAliases(类型别名)
    • typeHandlers(类型处理器)
    • objectFactory(对象工厂)
    • plugins(插件)
    • environments(环境配置)
      • environment(环境变量)
        • transactionManager(事务管理器)
        • dataSource(数据源)
    • databaseIdProvider(数据库厂商标识)
    • mappers(映射器)

4.1 <properties>标签

这个标签用于配置需要用到的属性,例如:

<properties resource="org/mybatis/example/jdbc.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

设置好这些属性之后,可以在整个配置文件中使用

    <properties resource="jdbc.properties"/>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

也可以在 SqlSessionFactoryBuilder.build()  方法中传入属性值

 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream, environment, properties); 

如果一个属性在多个地方进行了配置,其读取规则是

  • 首先读取在 properties 元素体内指定的属性。
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
  • 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。

4.2 <settings>标签

通过settings标签配置,我们可以改变mybatis运行时行为。

设置名描述有效值默认值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false
aggressiveLazyLoading 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。 true | false false (在 3.4.1 及之前的版本中默认为 true)
multipleResultSetsEnabled 是否允许单个语句返回多结果集(需要数据库驱动支持)。 true | false true
useColumnLabel 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。 true | false true
useGeneratedKeys 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 true | false False
autoMappingBehavior 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 NONE, PARTIAL, FULL PARTIAL
autoMappingUnknownColumnBehavior 指定发现自动映射目标未知列(或未知属性类型)的行为。
  • NONE: 不做任何反应
  • WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN
  • FAILING: 映射失败 (抛出 SqlSessionException)
NONE, WARNING, FAILING NONE
defaultExecutorType 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。 SIMPLE REUSE BATCH SIMPLE
defaultStatementTimeout 设置超时时间,它决定数据库驱动等待数据库响应的秒数。 任意正整数 未设置 (null)
defaultFetchSize 为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。 任意正整数 未设置 (null)
defaultResultSetType 指定语句默认的滚动策略。(新增于 3.5.2) FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置) 未设置 (null)
safeRowBoundsEnabled 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 true | false False
safeResultHandlerEnabled 是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。 true | false True
mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 true | false False
localCacheScope MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 SESSION | STATEMENT SESSION
jdbcTypeForNull 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。 OTHER
lazyLoadTriggerMethods 指定对象的哪些方法触发一次延迟加载。 用逗号分隔的方法列表。 equals,clone,hashCode,toString
defaultScriptingLanguage 指定动态 SQL 生成使用的默认脚本语言。 一个类型别名或全限定类名。 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler 指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5) 一个类型别名或全限定类名。 org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。 true | false false
returnInstanceForEmptyRow 当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2) true | false false
logPrefix 指定 MyBatis 增加到日志名称的前缀。 任何字符串 未设置
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J(3.5.9 起废弃) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置
proxyFactory 指定 Mybatis 创建可延迟加载对象所用到的代理工具。 CGLIB (3.5.10 起废弃) | JAVASSIST JAVASSIST (MyBatis 3.3 以上)
vfsImpl 指定 VFS 的实现 自定义 VFS 的实现的类全限定名,以逗号分隔。 未设置
useActualParamName 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1) true | false true
configurationFactory 指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3) 一个类型别名或完全限定类名。 未设置
shrinkWhitespacesInSql 从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5) true | false false
defaultSqlProviderType 指定一个拥有 provider 方法的 sql provider 类 (新增于 3.5.6). 这个类适用于指定 sql provider 注解上的type(或 value) 属性(当这些属性在注解中被忽略时)。 (e.g. @SelectProvider) 类型别名或者全限定名 未设置
nullableOnForEach 为 'foreach' 标签的 'nullable' 属性指定默认值。(新增于 3.5.9) true | false false
argNameBasedConstructorAutoMapping 当应用构造器自动映射时,参数名称被用来搜索要映射的列,而不再依赖列的顺序。(新增于 3.5.10) true | false false

完整的配置如下

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="aggressiveLazyLoading" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="safeResultHandlerEnabled" value="true"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
  <setting name="defaultScriptingLanguage" value="org.apache.ibatis.scripting.xmltags.XMLLanguageDriver"/>
  <setting name="defaultEnumTypeHandler" value="org.apache.ibatis.type.EnumTypeHandler"/>
  <setting name="callSettersOnNulls" value="false"/>
  <setting name="returnInstanceForEmptyRow" value="false"/>
  <setting name="logPrefix" value="exampleLogPreFix_"/>
  <setting name="logImpl" value="SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING"/>
  <setting name="proxyFactory" value="CGLIB | JAVASSIST"/>
  <setting name="vfsImpl" value="org.mybatis.example.YourselfVfsImpl"/>
  <setting name="useActualParamName" value="true"/>
  <setting name="configurationFactory" value="org.mybatis.example.ConfigurationFactory"/>
</settings>

4.3 <typeAliases>

用于降低冗余的类全限定名的书写。类型别名仅用于mybatis的xml配置文件。

举例:

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

或者,另一种形式,指定一个包名,这个包下的所有类型都会具有类型别名,比如,比如 domain.blog.Author 的别名为 author

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>

另外一种指定类型别名的方式,使用注解

@Alias("author")
public class Author {
    ...
}

此外,mybatis为java内置的常见类型设定了类型别名,如下

别名映射的类型
_byte byte
_char (since 3.5.10) char
_character (since 3.5.10) char
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
char (since 3.5.10) Character
character (since 3.5.10) Character
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
biginteger BigInteger
object Object
date[] Date[]
decimal[] BigDecimal[]
bigdecimal[] BigDecimal[]
biginteger[] BigInteger[]
object[] Object[]
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

4.4 <typeHandlers>

类型处理器

从数据库查询出来的数据有各种各样的类型,怎么把它们转换成java中的类型呢?这里就用到了类型处理器。mybatis为我们准备了默认的类型处理器

我们可以定义自己的类型处理器,替代默认的类型处理器,不过一般不需要。

类型处理器Java 类型JDBC 类型
BooleanTypeHandler java.lang.Booleanboolean 数据库兼容的 BOOLEAN
ByteTypeHandler java.lang.Bytebyte 数据库兼容的 NUMERIC 或 BYTE
ShortTypeHandler java.lang.Shortshort 数据库兼容的 NUMERIC 或 SMALLINT
IntegerTypeHandler java.lang.Integerint 数据库兼容的 NUMERIC 或 INTEGER
LongTypeHandler java.lang.Longlong 数据库兼容的 NUMERIC 或 BIGINT
FloatTypeHandler java.lang.Floatfloat 数据库兼容的 NUMERIC 或 FLOAT
DoubleTypeHandler java.lang.Doubledouble 数据库兼容的 NUMERIC 或 DOUBLE
BigDecimalTypeHandler java.math.BigDecimal 数据库兼容的 NUMERIC 或 DECIMAL
StringTypeHandler java.lang.String CHARVARCHAR
ClobReaderTypeHandler java.io.Reader -
ClobTypeHandler java.lang.String CLOBLONGVARCHAR
NStringTypeHandler java.lang.String NVARCHARNCHAR
NClobTypeHandler java.lang.String NCLOB
BlobInputStreamTypeHandler java.io.InputStream -
ByteArrayTypeHandler byte[] 数据库兼容的字节流类型
BlobTypeHandler byte[] BLOBLONGVARBINARY
DateTypeHandler java.util.Date TIMESTAMP
DateOnlyTypeHandler java.util.Date DATE
TimeOnlyTypeHandler java.util.Date TIME
SqlTimestampTypeHandler java.sql.Timestamp TIMESTAMP
SqlDateTypeHandler java.sql.Date DATE
SqlTimeTypeHandler java.sql.Time TIME
ObjectTypeHandler Any OTHER 或未指定类型
EnumTypeHandler Enumeration Type VARCHAR 或任何兼容的字符串类型,用来存储枚举的名称(而不是索引序数值)
EnumOrdinalTypeHandler Enumeration Type 任何兼容的 NUMERIC 或 DOUBLE 类型,用来存储枚举的序数值(而不是名称)。
SqlxmlTypeHandler java.lang.String SQLXML
InstantTypeHandler java.time.Instant TIMESTAMP
LocalDateTimeTypeHandler java.time.LocalDateTime TIMESTAMP
LocalDateTypeHandler java.time.LocalDate DATE
LocalTimeTypeHandler java.time.LocalTime TIME
OffsetDateTimeTypeHandler java.time.OffsetDateTime TIMESTAMP
OffsetTimeTypeHandler java.time.OffsetTime TIME
ZonedDateTimeTypeHandler java.time.ZonedDateTime TIMESTAMP
YearTypeHandler java.time.Year INTEGER
MonthTypeHandler java.time.Month INTEGER
YearMonthTypeHandler java.time.YearMonth VARCHAR 或 LONGVARCHAR
JapaneseDateTypeHandler java.time.chrono.JapaneseDate DATE

4.5 <objectFactory>

对象工厂

我们在结果映射配置文件xxxMapper.xml中指定了查询结果存储在哪个结果类里面,比如

<select id="selectUsers" resultMap="userResultMap">
  select user_id, user_name, hashed_password
  from some_table
  where id = #{id}
</select>

<resultMap id="userResultMap" type="User">
  <id property="id" column="user_id" />
  <result property="username" column="user_name"/>
  <result property="password" column="hashed_password"/>
</resultMap>

查询数据库的结果将存储在List<User>对象中,对象工厂就是实例化List和User的。

mybatis为我们准备了默认的对象工厂DefaultObjectFactory

默认对象工厂的工作原理可以看如下的代码演示

ObjectFactory objectFactory = new DefaultObjectFactory();
List<String> list = objectFactory.create(List.class);
Robot robot = objectFactory.create(Robot.class);

默认的对象工厂DefaultObjectFactory,一般情况下我们不需要覆盖,如果基于业务需要,自定义对象工厂,可以在mybatis xml配置文件中指定,比如

<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
  <property name="someProperty" value="100"/>
</objectFactory>

4.6 <plugins>

插件

mybatis允许你在映射sql语句执行过程中的某些点进行拦截,做一些特殊的事情。允许拦截的方法调用如下

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

比如,如果我们想在update执行前后做一些处理,可以定义一个插件

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class, //需要拦截的实例
  method = "update", //需要拦截的实例中的特定方法
  args = {MappedStatement.class,Object.class})}) //args为方法的参数,可以看Executor.update源码看到这两个参数定义
public class ExamplePlugin implements Interceptor {
  private Properties properties = new Properties();

  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    // 在执行目标方法之前,我们可以做一些事情,写在这
    Object returnObject = invocation.proceed(); //执行目标方法
    // 在执行目标方法之后,我们可以做一些事情,写在这
    return returnObject;
  }

  @Override
  public void setProperties(Properties properties) {
    this.properties = properties;
  }
}

然后我们在mybatis xml配置文件中配置这个插件

<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>
4.6.1 原理简析

mybatis中有一个Configuration对象,这个对象非常重要,其相关介绍见:https://www.cnblogs.com/zhenjingcool/p/15781978.html

mybatis的SessionFactory实例化过程中,会根据xml配置文件实例化Configuration对象。

和sql映射语句执行相关的类Executor、ParameterHandler、ResultSetHandler、StatementHandler的实例化就在这个Configuration实例中

public Executor newExecutor(Transaction transaction)
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql)
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql)
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)

经过一些执行过程(参见https://www.cnblogs.com/likeguang/p/16336742.html),最终会执行到我们的interceptor.intercept方法中来,最终执行到我们自己的逻辑中来。

这就是插件的执行原理。

4.7 <environments>

环境配置。通过环境配置,我们可以配置多种环境以适用不同的情况,比如开发环境和测试环境、不同的环境使用不同的数据库连接。

同时,我们可以摄者defalut="xxx"来指定默认环境。

我们可以指定transactionManager事务管理器类型(比如type="JDBC")

我们可以指定数据源配置(比如type="POOLED")

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
</environments>

在创建SqlSessionFactory时

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);

我们可以指定environment参数,也可以不指定(从xml配置中加载)

4.7.1 事务管理器(transactionManager)

在mybatis中有两种类型的事务管理器(type=JDBC|MANAGED)

JDBC-这个配置直接使用了JDBC的提交和回滚功能,它依赖从数据源获得的连接来管理事务的作用域。默认情况下,在关闭连接时启用自动提交

MANAGED-这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。默认情况下,它会关闭连接。

在spring+mybatis中,没有必要设置事务管理器,设置了也不会生效,spring会使用自带的事务管理器来覆盖mybatis自己的配置。

4.7.2 数据源(DataSource)

有三种内建的数据源类型(type="UNPOOLED|POOLED|JNDI")

UNPOOLED-不使用缓存,每次请求时都会打开和关闭连接

POOLED-把连接放入缓存池中,避免了创建新的连接实例时所需要的初始化和认证时间。注意:这里还不涉及mybatis的一级缓存和二级缓存

JNDI-这种目前不重要,略

5 xml映射文件

XxxMapper.xml

6 动态sql

 

7 mybatis-spring

官网:http://mybatis.org/spring/zh/index.html

mybatis-spring是一个jar包,我们通过这个jar包整合mybatis和spring。也是mybatis官方开发的。

我们知道,mybatis中最重要的一个类是SqlSessionFactory,使用SqlSessionFactoryBuilder创建SqlSessionFactory。对应的在spring中,spring使用SqlSessionFactoryBean创建SqlSessionFactory。

要注入SqlSessionFactory,只需要如下配置

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
</bean>

或使用注解

@Configuration
public class MyBatisConfig {
  @Bean
  public SqlSessionFactory sqlSessionFactory() throws Exception {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(dataSource());
    return factoryBean.getObject();
  }
}

那么如果注入xxxMapper映射器类呢?spring为我们准备了MapperFactoryBean,使用它我们注入映射器

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

对应的java配置

@Configuration
public class MyBatisConfig {
  @Bean
  public UserMapper userMapper() throws Exception {
    SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory());
    return sqlSessionTemplate.getMapper(UserMapper.class);
  }
}

那么如何来访问数据呢?如下

public class FooServiceImpl implements FooService {

  private final UserMapper userMapper;

  public FooServiceImpl(UserMapper userMapper) {
    this.userMapper = userMapper;
  }

  public User doSomeBusinessStuff(String userId) {
    return this.userMapper.getUser(userId);
  }
}

7.1 SqlSessionFactoryBean

在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建

要创建这个工厂bean,只需要将下面配置放到spring的xml配置文件中

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
</bean>

等效的java代码为

@Configuration
public class MyBatisConfig {
  @Bean
  public SqlSessionFactory sqlSessionFactory() {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(dataSource());
    return factoryBean.getObject();
  }
}

在spring-mybatis中,mybatis自有的配置被替换,也就是说mybatis的<environments>、<DataSource>、<transactionManager>都会被忽略。mybatis-spring会创建它自有的mybatis环境配置。

在mybatis-spring中使用参数mapperLocations指定映射器类位置

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="mapperLocations" value="classpath*:sample/config/mappers/**/*.xml" />
</bean>

7.2 事务

在mybatis-spring中,使用spring的事务管理器DataSourceTransactionManager来实现事务的管理。而不是使用mybatis自身的事务管理功能。

开启spring的事务处理功能

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <constructor-arg ref="dataSource" />
</bean>
@Configuration
public class DataSourceConfig {
  @Bean
  public DataSourceTransactionManager transactionManager() {
    return new DataSourceTransactionManager(dataSource());
  }
}

spring中,在不显式指定事务操作的情况下,事务是自动提交的。

在显式使用PlatformTransactionManager指定事务操作的情况下,需要手动提交和回滚事务,比如

public class UserService {
  private final PlatformTransactionManager transactionManager;
  public UserService(PlatformTransactionManager transactionManager) {
    this.transactionManager = transactionManager;
  }
  public void createUser() {
    TransactionStatus txStatus =
        transactionManager.getTransaction(new DefaultTransactionDefinition());
    try {
      userMapper.insertUser(user);
    } catch (Exception e) {
      transactionManager.rollback(txStatus);
      throw e;
    }
    transactionManager.commit(txStatus);
  }
}

我们也可以使用TransactionTemplate来操作事务,此时可以省略commit和rollback方法的调用

public class UserService {
  private final PlatformTransactionManager transactionManager;
  public UserService(PlatformTransactionManager transactionManager) {
    this.transactionManager = transactionManager;
  }
  public void createUser() {
    TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
    transactionTemplate.execute(txStatus -> {
      userMapper.insertUser(user);
      return null;
    });
  }
}

注意,上述代码使用的是映射器,但是换成SqlSession也可以工作

7.3 使用SqlSession

在mybatis中,我们使用SqlSession来进行数据库的访问。

在mybatis-spring中,我们使用SqlSessionTemplate作为SqlSession的一个实现。它可以无缝替代mybatis中使用SqlSession的场景。

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

对应的java配置为

@Configuration
public class MyBatisConfig {
  @Bean
  public SqlSessionTemplate sqlSession() throws Exception {
    return new SqlSessionTemplate(sqlSessionFactory());
  }
}

现在我们就可以使用这个SqlSessionTemplate对象了

public class UserDaoImpl implements UserDao {
  private SqlSession sqlSession;
  public void setSqlSession(SqlSession sqlSession) {
    this.sqlSession = sqlSession;
  }
  public User getUser(String userId) {
    return sqlSession.selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
  }
}

7.4  注入映射器

我们知道,mybatis访问数据库有两种方式,一种是通过类似SqlSession.selectOne()访问。另一种是通过映射器访问,比如userMapper.queryUser()

使用映射器访问数据库,首先需要注入映射器到spring中,有两种方式注入映射器

7.4.1 注册映射器

使用MapperFactoryBean注册映射器

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
7.4.2 发现映射器

另一种比较方便的注入映射器的方式是使用如下方式

  • 使用 <mybatis:scan/> 元素
  • 使用 @MapperScan 注解

我们以@MapperScan为例

@Configuration
@MapperScan("org.mybatis.spring.sample.mapper")
public class AppConfig {
  // ...
}

8 和springboot集成

官网:http://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/zh/index.html

MyBatis-Spring-Boot-StarterMyBatis-SpringSpring BootJava
3.0 3.0 3.0 17 或更高
2.3 2.1 2.5 - 2.7 8 或更高
2.2 2.0(2.0.6 以上可开启所有特性) 2.5 - 2.7 8 或更高
2.1 2.0(2.0.6 以上可开启所有特性) 2.1 - 2.4 8 或更高
2.0 (EOL) 2.0 2.0 或 2.1 8 或更高
1.3 (EOL) 1.3 1.5 6 或更高
1.2 (EOL) 1.3 1.4 6 或更高
1.1 (EOL) 1.3 1.3 6 或更高
1.0 (EOL) 1.2 1.3 6 或更高

springboot中使用如下依赖进行mybatis集成

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.1</version>
</dependency>

mybatis-spring-boot-starter将会

  • 自动探测存在的 DataSource
  • 将使用 SqlSessionFactoryBean 创建并注册一个 SqlSessionFactory 的实例,并将探测到的 DataSource 作为数据源
  • 将创建并注册一个从 SqlSessionFactory 中得到的 SqlSessionTemplate 的实例
  • 自动扫描你的 mapper,将它们与 SqlSessionTemplate 相关联,并将它们注册到Spring 的环境(context)中去,这样它们就可以被注入到你的 bean 中

至此,mybatis和springboot集成介绍完毕,非常简单,基本上就是通过引入自动配置jar包进行自动配置。

 

posted @ 2023-02-28 23:28  zhenjingcool  阅读(1141)  评论(0编辑  收藏  举报