第二章 MyBatis的核心配置
2.1 MyBatis的核心对象
SqlSessionFactoryBuilder:用于创建 SqlSessionFactory 对象的构建器。
SqlSessionFactory:SqlSession 对象的工厂,创建 SqlSession 对象。
SqlSession:与数据库交互的会话,用于执行 SQL 命令、管理事务等。
Executor:SqlSession 执行 SQL 命令的组件,具体负责维护一级缓存、二级缓存、执行器类型等。
MappedStatement:存储 SQL 映射信息的对象,包括 SQL 语句、输入参数、输出参数等。
Configuration:MyBatis 的全局配置对象,包括数据源、类型转换器、插件等。
TypeHandler:MyBatis 提供的类型转换器,用于将 Java 类型转换为数据库类型。
本章介绍前三个
2.1.1 SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 是 MyBatis 构建 SqlSessionFactory 的工厂类,其作用是读取 MyBatis 的配置文件并生成 SqlSessionFactory 对象,从而创建 SqlSession 对象。SqlSession 是 MyBatis 与数据库交互的核心对象。
mybatis通过配置的方式与数据库交流,不用写在程序代码中,是程序员专注于业务的实现,且更符合面向对象的程序设计模式。这个配置,那就要写配置文件,而SqlSessionFactoryBuilder的作用就是读配置文件。
SqlSessionFactoryBuilder通过build()方法创建SqlSessionFactory对象。
SqlSessionFactoryBuilder的build()方法有三个重载:
1.build(Reader reader, String environment, Properties properties)
通过传入一个Reader对象,读取mybatis的配置文件(mybatis-config.xml),并通过environment指定环境,properties指定属性,创建SqlSessionFactory对象。
2.build(InputStream inputStream, String environment, Properties properties)
通过传入一个InputStream对象,读取mybatis的配置文件(mybatis-config.xml),并通过environment指定环境,properties指定属性,创建SqlSessionFactory对象。
3.build(Configuration config)
通过传入一个Configuration对象,直接创建SqlSessionFactory对象。
SqlSessionFactory是线程安全的,因为它的创建过程是线程安全的,并且一旦创建完成,就不会被改变。SqlSessionFactory实例应该在应用程序启动时创建,并在整个应用程序生命周期中共享。因此,SqlSessionFactory通常被设计为一个单例模式,以确保在整个应用程序中只有一个实例。每个线程应该使用自己的SqlSession实例,SqlSession实例本身不是线程安全的,因此应该在每个线程中创建和使用它。
2.1.2 SqlSessionFactory
SqlSessionFactory是MyBatis的核心接口之一,其作用是创建SqlSession对象。SqlSession是用于与数据库进行交互的重要对象,可以执行SQL语句、管理事务等。
SqlSessionFactory的主要功能是读取MyBatis的配置文件,包括数据库连接信息、映射文件路径等,然后根据这些配置信息创建并返回SqlSession对象。
SqlSessionFactory是线程安全的,它一旦创建后通常在应用程序生命周期内一直存在,因此建议在应用程序启动时就创建SqlSessionFactory对象,并在整个应用程序中共享它。
SqlSessionFactory使用openSession方法创建Sqlsession对象。
SqlSessionFactory的重载的openSession方法有多个,其中比较常用的有两个,分别是:
1.openSession():无参数的方法,返回一个非自动提交的SqlSession对象。
2.openSession(boolean autoCommit):参数为autoCommit,表示是否自动提交事务,返回一个与autoCommit参数相匹配的SqlSession对象。
SqlSessionFactory的具体实现流程大致如下:
读取MyBatis的配置文件,包括数据库连接信息、映射文件路径等。
根据配置文件的信息创建DataSource数据源对象,以便在SqlSession中获取连接。
根据配置文件的信息创建Configuration对象,该对象包括Mapper映射信息、插件、类型转换器等。
将Configuration对象传递给SqlSessionFactoryBuilder,使用SqlSessionFactoryBuilder的build方法创建SqlSessionFactory对象。
使用SqlSessionFactory的openSession方法创建SqlSession对象。
疑问:
为什么SqlSessionFactory是单例的,为什么还需要创建SqlSessionFactoryBuilder呢,为什么不直接创建一个SqlSessionFactory对象?
尽管 SqlSessionFactory 是单例的,但是它的创建过程是比较复杂的,需要配置文件的解析、数据库连接的建立等过程。而 SqlSessionFactoryBuilder 的作用就是负责解析配置文件、构建 SqlSessionFactory,将复杂的创建过程与使用代码分离,降低了耦合性,方便代码的维护和修改。另外, SqlSessionFactoryBuilder 也可以重复使用,用于多次创建 SqlSessionFactory 对象。
为什么要重复创建SqlSessionFactory 对象,它不是单例的吗?
虽然 SqlSessionFactory是单例的,但是在多个线程并发访问的情况下,如果只使用一个 SqlSessionFactory,就会有线程安全问题,可能会导致连接池过度消耗或者连接池被锁定等问题。因此,每个线程需要一个自己的 SqlSession,而每个 SqlSession需要一个 SqlSessionFactory来创建它。因此,为了保证多线程下的线程安全,每个线程需要使用自己的 SqlSessionFactory对象来创建 SqlSession,所以需要重复创建 SqlSessionFactory对象。
SqlSessionFactory和SqlSessionFactoryBuilder都要读取配置文件吗,SqlSessionFactoryBuilder不是已经读了吗?
SqlSessionFactoryBuilder 在创建 SqlSessionFactory 时需要读取配置文件,而 SqlSessionFactory 在创建 SqlSession 时也需要读取配置文件。SqlSessionFactoryBuilder 读取的配置文件通常是 mybatis-config.xml,而 SqlSessionFactory 读取的配置文件通常是 mapper.xml 和其他相关的配置文件。因此,虽然 SqlSessionFactory 是单例的,但是每次创建 SqlSession 时都需要重新读取配置文件。
SqlSessionFactoryBuilder 创建过程没有读mapper.xml 吗?mybatis-config.xml中不是写了mappers标签吗?
SqlSessionFactoryBuilder 创建过程中是不会读取 mapper.xml 的,因为 SqlSessionFactoryBuilder 主要的作用是将配置文件(包括 mybatis-config.xml 和映射文件)解析为一个完整的 SqlSessionFactory 对象。具体的解析过程会在调用 SqlSessionFactoryBuilder 的 build 方法时进行,而在这个过程中,SqlSessionFactoryBuilder 会使用 XMLConfigBuilder 来解析 mybatis-config.xml 配置文件,并使用 XMLMapperBuilder 来解析映射文件(即 Mapper.xml 文件)。
在 mybatis-config.xml 中的 mappers 标签是用来配置映射文件位置的,它告诉 MyBatis 哪些映射文件需要被解析。实际上,SqlSessionFactoryBuilder 会根据 mappers 标签配置的映射文件路径来读取对应的映射文件,然后通过 XMLMapperBuilder 将映射文件解析为对应的 Mapper 接口。
最后得到Mapper 接口的代理实现类,在使用SqlSession.getmapper(Mapper.class) 时的被调用。
基本流程:
1.SqlSessionFactoryBuilder读取mybatis-config.xml文件。
2.使用SqlSessionFactoryBuilder.build()方法时,SqlSessionFactoryBuilder根据mybatis-config.xml创建SqlSessionFactory。
3.读取到mappers标签时,遍历每个mapper标签,mapper标签里有mapperxml的文件路径,根据这个路径找到mapperxml文件。(如果是mapper接口的路径,则找同名的xml文件,因为接口中没有namespace)
4.根据mapperxml中的namespace可以找到一个接口类,这个接口中写着各种方法与xml中一一对应。
5.根据此二文件就能自动生成一个实现了mapper接口的对象(代理对象),这个对象里的方法有着在xml中配置的sql语句,调用这个代理对象的方法就可以对数据库进行操作。
openSession所有重载方法:
方法名 | 描述 | 使用示例 |
openSession() | 创建一个新的SqlSession对象,使用默认的执行器,事务将被自动提交。 | SqlSession sqlSession = sqlSessionFactory.openSession(); |
openSession(boolean autoCommit) | 创建一个新的SqlSession对象,指定事务是否自动提交。 | SqlSession sqlSession = sqlSessionFactory.openSession(true); |
openSession(Connection connection) | 创建一个新的SqlSession对象,使用指定的JDBC连接。 | Connection connection = dataSource.getConnection();SqlSession sqlSession = sqlSessionFactory.openSession(connection); |
openSession(TransactionIsolationLevel level) | 创建一个新的SqlSession对象,使用默认的执行器和事务隔离级别。 | SqlSession sqlSession = sqlSessionFactory.openSession(TransactionIsolationLevel.REPEATABLE_READ); |
openSession(ExecutorType execType) | 创建一个新的SqlSession对象,使用指定的执行器。 | SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); |
openSession(ExecutorType execType, boolean autoCommit) | 创建一个新的SqlSession对象,使用指定的执行器和事务是否自动提交。 | SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.REUSE, true); |
openSession(ExecutorType execType, TransactionIsolationLevel level) | 创建一个新的SqlSession对象,使用指定的执行器和事务隔离级别。 | SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.SIMPLE, TransactionIsolationLevel.READ_UNCOMMITTED); |
openSession(ExecutorType execType, Connection connection) | 创建一个新的SqlSession对象,使用指定的执行器和JDBC连接。 | Connection connection = dataSource.getConnection();SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.REUSE, connection); |
2.1.3 SqlSession
SqlSession是MyBatis中用于执行SQL语句的主要对象之一,它提供了执行SQL语句的方法,以及一些对数据库进行操作的方法。
以下是SqlSession的一些常用方法:
方法签名 | 描述 |
void close() | 关闭SqlSession,释放资源 |
void commit() | 提交事务 |
int delete(String statement, Object parameter) | 删除操作,其中statement为SQL语句的唯一标识符,parameter为传递给SQL语句的参数 |
<T> T getMapper(Class<T> type) | 获取指定类型的Mapper接口的实例对象 |
<T> T selectOne(String statement, Object parameter) | 查询操作,返回单个结果,其中statement为SQL语句的唯一标识符,parameter为传递给SQL语句的参数 |
<E> List<E> selectList(String statement, Object parameter) | 查询操作,返回结果集,其中statement为SQL语句的唯一标识符,parameter为传递给SQL语句的参数 |
void rollback() | 回滚事务 |
getMapper方法就是使用接口获得上面说到的实例对象。
使用SqlSession的一般流程如下:
通过SqlSessionFactory创建SqlSession对象
使用SqlSession执行数据库操作,例如执行SQL语句、获取Mapper接口实例等
关闭SqlSession,释放资源
以下是使用SqlSession执行数据库操作的示例代码:
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
// 执行查询操作
User user = sqlSession.selectOne("com.example.mapper.UserMapper.selectById", 1);
// 获取UserMapper接口的实例对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 执行插入操作
User newUser = new User();
newUser.setName("Alice");
newUser.setAge(20);
userMapper.insert(newUser);
// 提交事务
sqlSession.commit();
} catch (Exception e) {
// 回滚事务
sqlSession.rollback();
} finally {
// 关闭SqlSession,释放资源
sqlSession.close();
}
其中,"com.example.mapper.UserMapper.selectById"是UserMapper.xml中定义的SQL语句的唯一标识符。在这里,我们使用了两种方式执行SQL语句:一种是使用SqlSession的selectOne方法,另一种是获取UserMapper接口的实例对象后,调用insert方法执行插入操作。需要注意的是,每次执行完SQL语句后,需要手动提交事务或回滚事务。同时,也需要手动关闭SqlSession释放资源。
每个线程都应该有自己的SqlSession对象,因为SqlSession是非线程安全的。如果在多个线程之间共享同一个SqlSession对象,可能会导致数据不一致或者并发问题。使用完SqlSession要及时关闭。使用完SqlSession要及时关闭是因为SqlSession中存在着数据库连接等资源,如果不及时关闭会导致这些资源无法释放,从而导致资源的浪费和数据库连接池的耗尽。
2.2 MyBatis核心配置文件
MyBatis核心配置文件是mybatis-config.xml,它用于配置MyBatis框架的全局属性和设置。它可以包含以下信息:
数据库连接池信息,如数据源的类型、JDBC驱动、连接URL、用户名和密码等。
MyBatis的全局属性设置,如默认的执行器类型、缓存的类型、延迟加载的配置等。
插件,用于自定义MyBatis的扩展。
TypeHandler的配置,用于Java类型和数据库类型之间的转换。
映射器文件(Mapper文件)的配置,指定Mapper文件的位置和加载方式。
其他配置,如日志实现类、SQL语句的拦截器等。
MyBatis核心配置文件的作用是为MyBatis框架提供全局的属性配置和设置,使得MyBatis可以在不同的环境中运行,并可以根据不同的需求进行自定义配置。
2.2.1 配置文件的主要元素
<!-- MyBatis 核心配置文件 -->
<configuration>
<!-- 配置 MyBatis 运行环境的属性,包括数据源和全局配置 -->
<properties>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
</properties>
<!-- 配置 MyBatis 的类型别名,可以为 Java 类型设置一个别名,方便在 Mapper 映射文件中使用 -->
<typeAliases>
<typeAlias type="com.example.User" alias="User"/>
</typeAliases>
<!-- 配置 MyBatis 的插件,可以拦截 SQL 执行过程中的某些操作,如参数处理、结果集处理等 -->
<plugins>
<plugin interceptor="com.example.MyPlugin">
<property name="param1" value="value1"/>
<property name="param2" value="value2"/>
</plugin>
</plugins>
<!-- 配置 MyBatis 的对象工厂,用于创建映射器所需的实例对象 -->
<objectFactory type="com.example.MyObjectFactory">
<property name="param1" value="value1"/>
<property name="param2" value="value2"/>
</objectFactory>
<!-- 配置 MyBatis 的类型处理器,用于处理 Java 对象与数据库字段之间的类型转换 -->
<typeHandlers>
<typeHandler javaType="java.util.Date" jdbcType="TIMESTAMP" handler="com.example.MyTypeHandler"/>
</typeHandlers>
<!-- 配置 MyBatis 的环境,可以配置多个环境,如开发环境、测试环境、生产环境等 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
<property name="url" value="${url}"/>
</dataSource>
</environment>
</environments>
<!-- 配置 MyBatis 的数据库厂商标识,可以为不同的数据库厂商配置不同的 SQL 方言 -->
<databaseIdProvider type="DB_VENDOR">
<property name="Oracle" value="oracle"/>
<property name="MySQL" value="mysql"/>
</databaseIdProvider>
<!-- 配置 MyBatis 的映射器,用于指定 Mapper 接口和映射器文件 -->
<mappers>
<mapper resource="com/example/UserMapper.xml"/>
<mapper class="com.example.OrderMapper"/>
</mappers>
</configuration>
configuration:MyBatis 核心配置文件
properties:用于配置属性,可以在整个配置文件中使用,使用 ${property} 格式来引用属性值。
settings:用于配置全局设置,比如是否开启缓存、是否使用懒加载等等。
typeAliases:用于配置别名,使得类型可以使用短名称进行引用。
typeHandlers:用于配置类型处理器,将 Java 类型转换为 JDBC 类型或从 JDBC 类型转换为 Java 类型。
objectFactory:用于配置对象工厂,用于创建结果对象。
plugins:用于配置插件,用于在执行器、语句处理器等地方拦截方法调用。
environments:用于配置数据库环境,包括事务管理器和数据源等。
transactionManager:用于配置事务管理器,用于管理事务。
dataSource:用于配置数据源,包括连接池、连接参数等。
databaseIdProvider:用于配置数据库厂商标识,用于支持多数据库类型。
mappers:用于配置映射器,即将 SQL 语句映射到 Java 方法的接口。
2.2.2 <properties>元素
<properties>元素用于定义属性配置,可以在MyBatis配置文件中引用这些属性。
常用的场景是将数据库连接的信息(例如数据库URL,用户名和密码)定义在外部文件中,然后使用<properties>将这些信息加载到MyBatis配置文件中,从而实现配置与代码的分离。
以下是一个使用<properties>的例子:
<!-- 定义 properties -->
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
<!-- 引用 properties 中定义的属性 -->
<dataSource type="POOLED">
<property name="driver" value="${db.driverClassName}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
在上述例子中, <properties>元素中通过 resource属性引用了一个名为 db.properties的外部属性文件,同时也定义了 username和 password两个属性。在 <dataSource>元素中,通过 ${}占位符的方式引用了 <properties>中定义的属性,从而实现了数据库连接信息的配置。
2.2.3 <settings>元素
settings 元素用于配置 MyBatis 全局性设置。下面是一个包含了所有可选设置的示例:
<settings>
<setting name="cacheEnabled" value="true" />
<setting name="lazyLoadingEnabled" 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="mapUnderscoreToCamelCase" value="false" />
<setting name="localCacheScope" value="SESSION" />
<setting name="jdbcTypeForNull" value="OTHER" />
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" />
</settings>
配置参数 | 作用 | 默认值 |
cacheEnabled | 是否开启缓存 | true |
lazyLoadingEnabled | 是否开启懒加载 | false |
aggressiveLazyLoading | 是否开启积极的懒加载 | false |
multipleResultSetsEnabled | 是否允许单个语句返回多结果集 | true |
useColumnLabel | 是否使用列标签代替列名 | true |
useGeneratedKeys | 是否使用JDBC自动生成主键 | false |
autoMappingBehavior | 自动映射行为 | PARTIAL |
defaultExecutorType | 默认执行器类型 | SIMPLE |
defaultStatementTimeout | 默认语句超时时间(秒) | null |
defaultFetchSize | 默认的每次返回的记录数 | null |
safeRowBoundsEnabled | 是否开启安全的行限制 | false |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射 | false |
localCacheScope | 本地缓存作用域 | SESSION |
jdbcTypeForNull | 在预处理语句中,指定JDBC类型为空值的默认类型 | OTHER |
lazyLoadTriggerMethods | 指定触发懒加载的方法列表 | equals,clone,hashCode,toString |
defaultScriptingLanguage | 默认的脚本语言类型 | org.apache.ibatis.scripting.xmltags.XMLLanguageDriver |
callSettersOnNulls | 是否对空值调用setter方法 | false |
logPrefix | 日志前缀 | null |
logImpl | 日志实现类 | org.apache.ibatis.logging.stdout.StdOutImpl |
2.2.4 <typeAliases>元素
typeAliases 标签用于为常见的 Java 类型设置别名,以简化映射文件中的类型引用。例如,将 java.util.HashMap 的别名设置为 HashMap,那么在映射文件中使用 HashMap 作为参数类型或返回类型时,就不需要使用完整的类名了。
以下是 typeAliases 的一个例子:
<typeAliases>
<typeAlias alias="HashMap" type="java.util.HashMap"/>
<typeAlias alias="Long" type="java.lang.Long"/>
<typeAlias alias="User" type="com.example.User"/>
</typeAliases>
上面的例子中,设置了三个别名:
HashMap 别名对应 java.util.HashMap 类型。
Long 别名对应 java.lang.Long 类型。
User 别名对应 com.example.User 类型。
默认情况下,MyBatis 会自动为许多常见的 Java 类型设置别名,例如:
别名 | 对应类型 |
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | java.lang.Integer |
_double | double |
_float | float |
_boolean | boolean |
string | java.lang.String |
byte | byte[] |
date | java.util.Date |
decimal | java.math.BigDecimal |
blob | byte[] |
clob | java.lang.String |
object | java.lang.Object |
可以通过在配置文件中设置 usePrimitiveTypes 属性来决定是否使用原始类型(例如 int)的别名,默认值为 false。
2.2.5 <environments>元素
environments 标签用于配置 MyBatis 运行的环境,包括数据库连接池等相关信息。一个 MyBatis 配置文件可以配置多个环境,但只能有一个默认环境。
environments 标签下包含一个或多个 environment 子标签,每个 environment 子标签代表一个环境配置。一个 environment 子标签必须包含一个 dataSource 子标签,代表数据源相关配置。
以下是一个简单的 environments 标签的例子:
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
在上述例子中,配置了一个名为 development 的环境,该环境使用 JDBC 事务管理器,并配置了一个 Pooled 数据源,其中包含了数据库连接的相关信息。同时将该环境设置为默认环境。
注意,transactionManager 和 dataSource 标签中的 type 属性指定了对应的实现类,MyBatis 内置了多种实现类可供选择。在本例中,transactionManager 和 dataSource 标签的 type 属性分别指定为 JDBC 和 POOLED,分别代表了 MyBatis 内置的 JdbcTransactionFactory 和 PooledDataSourceFactory 实现类。
以下是数据源类型及其用途以及需要配置的属性:
数据源类型 | 用途 | 属性 |
UNPOOLED | 适用于小型应用 | driver、url、username、password |
POOLED | 适用于多个线程或多个连接的中型应用 | driver、url、username、password、poolMaximumActiveConnections、poolMaximumIdleConnections、poolMaximumCheckoutTime、poolPingQuery、poolPingEnabled |
JNDI | 适用于应用服务器或类似环境 | dataSource、java.naming.factory.initial、java.naming.provider.url、java.naming.security.authentication、java.naming.security.principal、java.naming.security.credentials |
其中,UNPOOLED 类型的数据源不使用连接池,每次请求都会打开新的数据库连接;POOLED 类型的数据源使用连接池管理数据库连接,从而提高应用性能;JNDI 类型的数据源适用于基于 J2EE 的环境。
对于 POOLED 类型的数据源,需要配置的属性包括:
driver:数据库驱动类名。
url:数据库连接地址。
username:登录数据库的用户名。
password:登录数据库的密码。
poolMaximumActiveConnections:池中最多可同时连接的活动连接数,默认值为 10。
poolMaximumIdleConnections:池中最多可保留的空闲连接数,默认值为 5。
poolMaximumCheckoutTime:从连接池获取连接的超时时间(以毫秒为单位),默认值为 20000 毫秒。
poolPingQuery:连接池用于检测连接是否可用的 SQL 查询语句,默认值为 "NO PING QUERY SET"。
poolPingEnabled:是否启用连接池的连接检测机制,默认值为 false。
对于 JNDI 类型的数据源,需要配置的属性包括:
dataSource:数据源 JNDI 名称。
java.naming.factory.initial:用于创建命名上下文的工厂的完全限定类名。
java.naming.provider.url:用于标识命名服务提供者的 URL。
java.naming.security.authentication:JNDI 访问时使用的身份验证类型。
java.naming.security.principal:进行 JNDI 访问时使用的用户名。
java.naming.security.credentials:进行 JNDI 访问时使用的密码。
2.2.6 <mappers>
mappers标签是MyBatis配置文件中的一个元素,用于配置Mapper接口对应的XML文件。
在mappers标签中,可以配置多个mapper元素,每个mapper元素可以用于指定一个Mapper接口对应的XML文件。例如:
<mappers>
<mapper resource="com/example/mapper/UserMapper.xml"/>
<mapper class="com.example.mapper.OrderMapper"/>
</mappers>
上述示例中,mappers标签中配置了两个mapper元素,第一个mapper指定了一个Mapper接口对应的XML文件,第二个mapper指定了一个Mapper接口对应的Java接口。
当MyBatis初始化时,会根据mappers标签中的配置,将Mapper接口与对应的XML文件关联起来,并生成Mapper接口的代理实现。
配置mappers标签可以让MyBatis自动扫描并加载Mapper接口,从而方便地进行SQL映射。
MyBatis的mappers元素引入映射文件的方式有四种,分别是:
1.使用类路径
<mappers>
<mapper resource="com/example/mapper/ExampleMapper.xml"/>
</mappers>
2.使用本地文件路径
<mappers>
<mapper url="file:///var/mappers/ExampleMapper.xml"/>
</mappers>
3.使用接口类
<mappers>
<mapper class="com.example.mapper.ExampleMapper"/>
</mappers>
4.使用包名
<mappers>
<package name="com.example.mapper"/>
</mappers>
以上四种方式分别通过resource、url、class、package来指定映射文件的位置和对应的接口类或包名。其中,使用接口类和使用包名两种方式都需要遵循MyBatis的命名规范,即接口类和映射文件名字一样,只是后缀不同(.java和.xml)。
2.3 MyBatis映射文件
MyBatis映射文件(Mapper XML文件)是将数据库表和Java类之间的映射关系定义在XML文件中的一种方式。它包含了SQL语句以及SQL语句与Java方法的映射关系,是MyBatis框架的核心组成部分之一。
2.3.1 MyBatis映射文件中的常用元素
MyBatis映射文件中的所有元素
元素 | 说明 |
cache | 定义缓存的配置 |
cache-ref | 引用其它命名空间下的缓存 |
resultMap | 结果集映射 |
result | 单个属性映射 |
association | 多个属性映射 |
collection | 多个属性映射 |
discriminator | 鉴别器,用于复杂结果集映射 |
case | 鉴别器中的条件分支 |
if | 动态 SQL 中的条件语句 |
choose | 动态 SQL 中的条件语句 |
when | choose 中的条件分支 |
otherwise | choose 中的条件分支 |
trim | 动态 SQL 中的条件语句,常用于处理 SET 和 WHERE 关键字的逗号问题 |
where | 动态 SQL 中的条件语句,常用于处理 WHERE 关键字的逗号问题 |
set | 动态 SQL 中的条件语句,常用于处理 SET 关键字的逗号问题 |
foreach | 动态 SQL 中的遍历语句,用于遍历集合等数据类型 |
bind | 动态 SQL 中的赋值语句 |
sql | SQL 片段的定义 |
insert | 插入数据操作 |
select | 查询数据操作 |
update | 更新数据操作 |
delete | 删除数据操作 |
sqlProvider | 基于 Provider 的 SQL 片段定义 |
selectKey | 插入数据时,获取生成的主键 |
include | 引用其它 XML 中定义的 SQL 片段 |
注意:该表格中列出的元素并不是全部,而是常用的元素。具体可以查看 MyBatis 官方文档。
2.3.2 <select>元素
select元素用于定义SQL查询操作,并将其映射到一个Java方法中,是MyBatis映射文件中最常用的元素之一。它包含了一条SQL语句及其相关配置,定义了从数据库中查询数据的操作。以下是一个select元素的示例:
<select id="selectUserById" parameterType="int" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
上述代码定义了一个查询操作,使用了id为selectUserById的标识符,并将查询参数类型设为int,查询结果映射为User对象。SQL查询语句为SELECT * FROM users WHERE id = #{id},其中#{id}为查询参数占位符。
下表列出了select元素的所有属性及其说明:
属性名 | 是否必须 | 默认值 | 说明 |
id | 是 | 无 | 用于标识SQL语句的唯一标识符,可以被用于Mapper接口的方法中 |
parameterType | 否 | 没有默认值 | SQL语句中使用的参数的Java类型,可以是一个Java类的全限定名、一个别名或一个TypeHandler实现类的全限定名 |
resultType | 否 | 没有默认值 | SQL查询结果集映射成的Java类型,可以是一个Java类的全限定名、一个别名或一个TypeHandler实现类的全限定名 |
resultMap | 否 | 没有默认值 | SQL查询结果集映射规则的唯一标识符,用于指定一个ResultMap元素的id属性 |
flushCache | 否 | 没有默认值 | 是否将本条SQL语句的查询结果缓存清空,默认为false |
useCache | 否 | 没有默认值 | 是否使用二级缓存,默认为true |
timeout | 否 | 没有默认值 | 查询超时时间,单位为毫秒 |
fetchSize | 否 | 没有默认值 | 一次查询返回的结果集大小 |
statementType | 否 | STATEMENT | 执行SQL语句的方式,可选值有STATEMENT、PREPARED和CALLABLE |
resultSetType | 否 | UNSPECIFIED | 返回结果集类型,可选值有FORWARD_ONLY、SCROLL_SENSITIVE、SCROLL_INSENSITIVE和UNSPECIFIED |
databaseId | 否 | 没有默认值 | 数据库厂商标识符,用于支持不同的数据库厂商的特性 |
其中,id属性是必须的,其余属性均为可选。
2.3.3 <insert>元素
<insert> 元素用于定义插入操作的 SQL 语句,可以通过参数映射指定插入参数。
以下是一个示例:
<insert id="insertUser" parameterType="com.example.User">
insert into user (username, password, email)
values (#{username}, #{password}, #{email})
</insert>
上述示例中,定义了一个 insertUser 的插入操作,将 User 对象的属性 username、password 和 email 分别插入到 user 表中的对应列。
以下是 <insert> 元素的所有属性:
属性名 | 描述 |
id | SQL 语句的唯一标识符,可以被用来引用这个语句。 |
parameterType | 插入语句中参数的 Java 类型。 |
statementType | 指定 Statement 的类型。可选值:STATEMENT、PREPARED 或 CALLABLE。默认为 PREPARED。 |
flushCache | 是否清空缓存。默认为 false。 |
useCache | 是否使用缓存。默认为 true。 |
timeout | SQL 执行的超时时间,以秒为单位。默认为未设置(null)。 |
fetchSize | 指定返回的结果集数量。默认为未设置(null)。 |
parameterMap | 引用外部 parameterMap 的名称。 |
resultType | 插入操作的返回类型,指定映射的 Java 类型。 |
resultMap | 引用外部 resultMap 的名称,用于将结果集映射到 Java 对象。 |
useGeneratedKeys | 是否使用自动生成的键。默认为 false。 |
keyColumn | 自动生成键列的列名。 |
keyProperty | 自动生成键列对应的属性名。 |
databaseId | 数据库厂商标识符。 |
sqlSource | SQL 语句的来源。 |
statementProvider | 指定 StatementProvider 的类名。 |
keyGenerator | 指定 KeyGenerator 的类名。 |
其中,比较常用的是 id、parameterType、resultType、resultMap、useGeneratedKeys 等。
通常,执行插人操作后需要获取插人成功的数据生成的主键值,不同类型数据库获取主键值的方式不同下面分别对支持主键自动增长的数据库获取主键值和不支持主键自动增长的数据库获取主键值的方式进行介绍。
1.针对支持主键自动增长的数据库获取主键值,可以在插入语句执行后,通过selectKey元素进行查询,以获得自动生成的主键值。selectKey元素必须位于插入语句之前,它可以是插入语句的一部分,也可以是单独的语句。例如,对于MySQL数据库,可以使用以下方式获取插入成功后自动生成的主键值:
<insert id="insertUser" parameterType="User">
<selectKey keyProperty="id" order="AFTER" resultType="int">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO users (username, password) VALUES (#{username}, #{password})
</insert>
在这个例子中,keyProperty属性指定了插入成功后生成的自增主键的属性名,resultType属性指定了返回值类型,order属性用于指定selectKey的执行顺序。
2.对于不支持主键自动增长的数据库,可以使用selectKey元素或select元素来获取主键值。一种常见的方法是在插入语句后,紧跟着执行一个查询语句,以获取刚刚插入的记录的主键值。例如,对于Oracle数据库,可以使用以下方式获取主键值:
<insert id="insertUser" parameterType="User">
INSERT INTO users (id, username, password) VALUES (user_seq.nextval, #{username}, #{password})
<selectKey keyProperty="id" order="AFTER" resultType="int">
SELECT user_seq.currval FROM dual
</selectKey>
</insert>
在这个例子中,首先执行插入语句,然后通过selectKey元素执行一个查询语句,以获取刚刚插入的记录的主键值。注意,在Oracle数据库中,必须使用user_seq.currval来获取最后插入的序列值。
以下是selectKey元素的所有属性:
属性 | 描述 |
keyProperty | 指定将生成的键值设置到哪个属性中。 |
resultType | 指定返回结果类型。 |
order | 指定selectKey的执行顺序。取值为BEFORE或AFTER。如果设置为BEFORE,则会在插入语句之前执行查询语句;如果设置为AFTER,则会在插入语句之后执行查询语句。 |
statementType | 指定执行的SQL语句类型。取值为STATEMENT、PREPARED或CALLABLE。 |
keyColumn | 指定生成的键值对应的数据库列名。 |
useGeneratedKeys | 指定是否使用自动生成的键值。取值为true或false。如果设置为true,则会使用数据库自动生成的键值;如果设置为false,则会使用selectKey元素中的查询语句获取键值。 |
2.3.4 <update>元素
update 元素用于执行更新操作,可以通过 set 子元素设置要更新的列及其值,通过 where 子元素指定更新记录的条件。
以下是一个 update 元素的示例:
<update id="updateUser" parameterType="User">
UPDATE users SET username=#{username}, password=#{password}
WHERE id=#{id}
</update>
属性 | 描述 |
id | 元素的唯一标识符 |
parameterType | 元素接受的参数类型 |
statementType | 执行的 SQL 语句类型 |
timeout | 超时时间,以秒为单位 |
flushCache | 是否清空缓存 |
useCache | 是否使用缓存 |
update 元素没有子元素,其内容即为一条 SQL 更新语句。可以使用 MyBatis 的动态 SQL 功能,根据实际情况动态生成更新语句。
2.3.5 <delete>元素
delete元素用于执行删除操作,它的实例可以如下所示:
<delete id="deleteUserById" parameterType="int">
DELETE FROM user WHERE id = #{id}
</delete>
上述实例中,delete元素表示执行一个删除操作,它的id属性值为"deleteUserById",表示这个元素的唯一标识符,parameterType属性值为"int",表示输入参数的类型为整数。元素的内容为执行的SQL语句,使用#{}语法表示动态输入的参数。
在实际应用中,可以通过动态构建SQL语句来完成更加复杂的删除操作。例如,可以使用where元素来动态构建WHERE子句,实现根据不同条件删除数据的功能:
<delete id="deleteUser" parameterType="map">
DELETE FROM user
<where>
<if test="id != null">
AND id = #{id}
</if>
<if test="name != null">
AND name = #{name}
</if>
</where>
</delete>
上述实例中,delete元素的id属性值为"deleteUser",parameterType属性值为"map",表示输入参数的类型为Map类型。元素的内容中使用了<where>元素来动态构建WHERE子句,使用<if>元素来根据条件动态添加SQL语句。例如,当输入参数Map中包含id属性时,会添加"AND id = #{id}"语句到SQL中,实现根据id删除数据的功能。当输入参数Map中包含name属性时,会添加"AND name = #{name}"语句到SQL中,实现根据name删除数据的功能。
以下是delete元素的所有属性:
属性 | 描述 |
id | 语句的唯一标识符,使用 namespace 加上语句的 id 组成。 |
parameterType | 指定传入语句的参数类型,可以是简单类型、POJO、Map 或其他复杂类型。 |
flushCache | 是否刷新缓存。取值为 true 或 false,默认为 false。 |
statementType | 执行语句的类型,取值为 STATEMENT、PREPARED 或 CALLABLE。默认为 PREPARED。 |
timeout | 执行超时时间,单位为秒。 |
useCache | 是否使用缓存,默认为 true。 |
databaseId | 数据库厂商标识,MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句。多个数据库厂商可以配置多个不同的语句,这些语句带有不同的 databaseId 值。 |
lang | 指定在动态语言下,如何处理 SQL 语句中的占位符,默认为 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver。 |
resultOrdered | 是否对查询结果进行排序,默认为 false。 |
resultSetType | 结果集类型,默认为 FORWARD_ONLY。其他可选项为 SCROLL_INSENSITIVE 和 SCROLL_SENSITIVE。 |
fetchSize | 每次查询时返回的行数。 |
statementName | 执行语句的名称,用于引用缓存中的语句。 |
keyColumn | 指定生成的键值对应的数据库列名。 |
keyProperty | 指定将生成的键值设置到哪个属性中。 |
useGeneratedKeys | 指定是否使用自动生成的键值。取值为 true 或 false。如果设置为 true,则会使用数据库自动生成的键值。 |
2.3.6 <sql>
sql元素用于定义可重用的SQL片段,可以在映射文件中的任何地方引用。它的语法格式如下:
<sql id="sql片段名称">
SQL语句
</sql>
在这个例子中,我们定义了一个名为userColumns的SQL片段,它包含了user表中的user_id、user_name、user_age和user_address四个列。
当需要在某个查询语句或其他语句中使用这个SQL片段时,只需要使用如下方式进行引用:
<select id="getUserList" resultType="User">
SELECT
<include refid="userColumns" />
FROM
user
</select>
下面是sql元素的所有属性:
属性 | 描述 |
id | SQL片段的名称,必须指定 |
parameterType | SQL片段中使用的参数类型,与select、insert、update、delete元素中的parameterType属性相同 |
statementType | 指定执行的SQL语句类型,取值为STATEMENT、PREPARED或CALLABLE |
resultType | SQL片段返回结果的类型 |
flushCache | 指定是否刷新缓存 |
databaseId | 用于指定在哪种数据库环境下应该被使用的ID |
在MyBatis的<include>元素中,<property>元素可以用于传递参数给被包含的 SQL 片段。它的作用类似于 Java 中的方法参数。当 SQL 片段中需要使用外部传递进来的参数时,可以通过 <property> 元素将参数传递给 SQL 片段。在 SQL 片段中可以使用 ${propertyName} 的方式引用参数值。
下面是一个包含<property>元素的<include>元素的例子:
<sql id="selectUsers">
SELECT * FROM users
WHERE
<include refid="whereClause"/>
ORDER BY id
</sql>
<sql id="whereClause">
<where>
<if test="name != null">
AND name = #{name}
</if>
<if test="email != null">
AND email = #{email}
</if>
</where>
</sql>
<select id="getUsers" resultType="User">
<include refid="selectUsers">
<property name="name" value="foo"/>
<property name="email" value="foo@example.com"/>
</include>
</select>
在上面的例子中,<include>元素包含了一个SQL片段<sql id="selectUsers">,并且使用了一个名为<property>的子元素,将name和email参数传递给<sql id="whereClause">中的SQL片段。在<sql id="whereClause">中,参数的值被引用为${name}和${email}。这样,通过<include>元素和<property>元素的结合使用,可以更加灵活地拼接 SQL 语句。
2.3.7 <resultMap>元素
resultMap元素用于映射查询结果集中的列到Java对象的属性上。通过resultMap元素,可以自定义映射规则,使得查询结果可以被正确地映射到Java对象上。
下面是一个resultMap元素的例子:
<resultMap id="userMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name" />
<result property="email" column="user_email" />
</resultMap>
下表列出了MyBatis中resultMap元素的常用属性:
属性名 | 描述 |
type | 指定映射结果的类型,可以是一个JavaBean或Map。该属性必须设置。 |
id | 指定结果映射的唯一标识符。 |
extends | 指定继承的其他结果映射。可以继承其他结果映射的配置,并可以在当前结果映射中进行覆盖或扩展。 |
autoMapping | 指定是否开启自动映射。取值为true或false。如果设置为true,则MyBatis会尝试自动将查询结果映射到JavaBean中;如果设置为false,则必须手动映射每个查询结果。 |
discriminator | 定义一个鉴别器用来判断结果集的类型,根据不同类型调用对应的映射规则。 |
result | 映射结果的每一列。包含属性名、列名和Java类型等信息。 |
association | 复杂类型的映射规则。可以通过关联查询来完成复杂对象的映射。 |
collection | 集合类型的映射规则。可以通过关联查询来完成集合对象的映射。 |
其中,type、id、result属性是必须设置的。其他属性可以根据需要进行配置。