2 MyBatis的核心配置
约 13203 字大约 44 分钟
2025-12-18
学习目标
- 了解 MyBatis 核心对象的作用
- 掌握 MyBatis 核心配置文件及其元素的使用
- 掌握 MyBatis 映射文件及其元素的使用
通过第 1 章的学习,对 MyBatis 框架已经有了初步了解。但是要想熟练地使用 MyBatis 框架进行实际开发,只会简单的配置是不够的,还需要对框架中的核心对象、核心配置文件和映射文件有更深入的理解。
2.1 MyBatis 的核心对象
通过前面的学习可知,MyBatis 的持久化解决方案将用户从原始的 JDBC 访问中解放出来,用户只需定义 SQL 语句,无需关注底层的 JDBC 操作。MyBatis 通过配置文件管理 JDBC 连接,实现数据库的持久化访问。
使用 MyBatis 框架解决持久化问题,主要涉及三个核心对象,分别是:
SqlSessionFactoryBuilderSqlSessionFactorySqlSession
它们在 MyBatis 框架中起着至关重要的作用。
2.1.1 SqlSessionFactoryBuilder
所有的 MyBatis 应用都是以 SqlSessionFactory 对象为中心,而 SqlSessionFactoryBuilder 就是 SqlSessionFactory 的构造者。
SqlSessionFactoryBuilder 通过 build() 方法构建 SqlSessionFactory 对象,它提供了多个重载的 build() 方法,如图 2-1 所示。

图2-1 SqlSessionFactoryBuilder中重载的build()方法
在图 2-1 中,这些重载的 build() 方法按照配置信息的传入方式可以分为三种形式:
第一种形式(字节流传递配置信息):
build(InputStream inputStream, String environment, Properties properties)上述 build() 方法中,参数 inputStream 是字节流,它封装了 XML 文件形式的配置信息;
参数 environment 和参数 properties 为可选参数。
- 其中,参数
environment用于指定将要加载的环境,包括数据源和事务管理器; - 参数
properties用于指定将要加载的 properties 文件。
第二种形式:
build(Reader reader, String environment, Properties properties)由上述 build() 方法可知,第二种形式的 build() 方法参数作用与第一种形式基本一致,唯一的不同是,第一种形式使用 InputStream 字节流封装了 XML 配置信息,而第二种形式使用 Reader 字符流封装了 XML 配置信息。
第三种形式:
build(Configuration config)上述 build() 方法中,Configuration 对象用于封装 MyBatis 项目中的配置信息。
通过以上代码可知,配置信息可以通过 InputStream(字节流)、Reader(字符流)、Configuration(类)三种形式提供给 SqlSessionFactoryBuilder 的 build() 方法。下面通过读取 XML 配置文件的方式来构造 SqlSessionFactory 对象,其关键代码如下:
// import org.apache.ibatis.io.Resources;
// 读取配置文件
InputStream inputStream = Resources.getResourceAsStream("配置文件");
// 根据配置文件构建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSessionFactory 对象是线程安全的,它一旦被创建,在整个应用程序执行期间都会存在。
如果多次创建同一个数据库的 SqlSessionFactory 对象,那么该数据库的资源将很容易被耗尽。
为了解决这个问题,通常一个数据库只创建一个
SqlSessionFactory对象,因此在构建SqlSessionFactory对象时,建议使用单例模式。
2.1.2 SqlSessionFactory
SqlSessionFactory 是 MyBatis 框架中十分重要的对象,用于创建 SqlSession 对象。
所有的 MyBatis 应用都是以 SqlSessionFactory 为对象中心。
在 SqlSessionFactoryBuilder 构建 SqlSessionFactory 对象之后,就可以使用 SqlSessionFactory 对象调用 openSession() 方法创建 SqlSession 对象。SqlSessionFactory 有多个重载的 openSession() 方法,具体如表 2-1 所示。
表2-1 SqISessionFactory的openSession(方法
| 方法名称 | 描述 |
|---|---|
SqlSession openSession() | 开启一个事务,连接对象从活动环境配置的数据源中获取。事务隔离级别将会使用驱动或数据源的默认设置,不会复用预编译语句,也不会批量处理更新。 |
SqlSession openSession(Boolean autoCommit) | 参数autoCommit可设置是否开启事务。若传入true表示事务控制为自动提交;若传入false表示开启事务。 |
SqlSession openSession(Connection connection) | 参数connection可提供自定义连接。 |
SqlSession openSession(TransactionIsolationLevel level) | 参数level可设置事务隔离级别。 |
SqlSession openSession(ExecutorType execType) | 参数execType为执行器的类型,有以下3种: - ExecutorType.SIMPLE:表示为每条语句创建新的预编译语句。 - ExecutorType.REUSE:表示会复用预编译语句。 - ExecutorType.BATCH:表示会批量执行所有更新语句。 |
SqlSession openSession(ExecutorType execType, Boolean autoCommit) | 参数execType为执行器的类型,参数autoCommit设置是否开启事务。 |
SqlSession openSession(ExecutorType execType, Connection connection) | 参数execType为执行器的类型,参数connection可提供自定义连接。 |
注意:
openSession()方法的参数为 boolean 值时,若传入 true 表示关闭事务控制,自动提交;若传入 false 表示开启事务控制。如果不传入参数,默认为 false。
2.1.3 SqlSession
SqlSession 是 MyBatis 框架中另一个重要的对象,它是应用程序与持久层之间执行交互操作的对象,主要作用是执行持久化操作,类似于 JDBC 中的 Connection。SqlSession 对象包含了执行 SQL 操作的方法,由于其底层封装了 JDBC 连接,所以可以直接使用 SqlSession 对象来执行已映射的 SQL 语句。
SqlSession 对象中包含了很多方法,其常用方法如表 2-2 所示。
表2-2 SqlSesson对象中的常用方法
| 方法名称 | 描述 |
|---|---|
<T> T selectOne(String statement) | 查询方法。参数statement是在配置文件中定义的**<select>**元素的id,该方法会返回SQL语句查询结果的一个泛型对象。 |
<T> T selectOne(String statement, Object parameter) | 查询方法。参数statement是在配置文件中定义的<select>元素的id,parameter是查询语句所需的参数。该方法会返回SQL语句查询结果的一个泛型对象。 |
<E> List<E> selectList(String statement) | 查询方法。参数statement是在配置文件中定义的<select>元素的id。该方法会返回SQL语句查询结果的泛型对象的集合。 |
<E> List<E> selectList(String statement, Object parameter) | 查询方法。参数statement是在配置文件中定义的<select>元素的id,parameter是查询语句所需的参数。该方法会返回SQL语句查询结果的泛型对象的集合。 |
<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) | 查询方法。参数statement是在配置文件中定义的<select>元素的id,parameter是查询语句所需的参数,rowBounds是用于分页的参数对象。该方法会返回SQL语句查询结果的泛型对象的集合。 |
void select(String statement, Object parameter, ResultHandler handler) | 查询方法。参数statement是在配置文件中定义的<select>元素的id,parameter是查询语句所需的参数,handler对象用于处理查询语句返回的复杂结果集。该方法通常用于多表查询。 |
int insert(String statement) | 插入方法。参数statement是在配置文件中定义的<insert>元素的id。该方法会返回执行SQL语句所影响的行数。 |
int insert(String statement, Object parameter) | 插入方法。参数statement是在配置文件中定义的<insert>元素的id,parameter是插入语句所需的参数。该方法会返回执行SQL语句所影响的行数。 |
int update(String statement) | 更新方法。参数statement是在配置文件中定义的<update>元素的id。该方法会返回执行SQL语句所影响的行数。 |
int update(String statement, Object parameter) | 更新方法。参数statement是在配置文件中定义的<update>元素的id,parameter是更新语句所需的参数。该方法会返回执行SQL语句所影响的行数。 |
int delete(String statement) | 删除方法。参数statement是在配置文件中定义的<delete>元素的id。该方法会返回执行SQL语句所影响的行数。 |
int delete(String statement, Object parameter) | 删除方法。参数statement是在配置文件中定义的<delete>元素的id,parameter是删除语句所需的参数。该方法会返回执行SQL语句所影响的行数。 |
void commit() | 提交事务的方法。 |
void rollback() | 回滚事务的方法。 |
void close() | 关闭SqlSession对象。 |
<T> T getMapper(Class<T> type) | 该方法会返回Mapper接口的代理对象,该对象关联了SqlSession对象,开发人员可以使用该对象直接调用相应方法操作数据库。参数type是Mapper的接口类型。MyBatis官方推荐通过Mapper对象访问MyBatis。 |
Connection getConnection() | 获取JDBC数据库连接对象的方法。 |
每一个线程都应该有一个自己的 SqlSession 对象,并且该对象不能共享。
**SqlSession****对象是线程不安全的,**因此其使用范围最好在一次请求或一个方法中,绝不能将其放在类的静态字段、对象字段或任何类型的管理范围(如 Servlet 的 HttpSession)中使用。- 使用完
SqlSession对象后要及时关闭,通常将其放在finally块中关闭,示例代码如下:
// 获取 SqlSession 对象
SqlSession sqlSession = null;
try {
// 获取 SqlSession 对象
sqlSession = sqlSessionFactory.openSession();
// 此处执行持久化操作
} finally {
// 关闭 SqlSession 对象
if (sqlSession != null) {
sqlSession.close();
}
}2.2 MyBatis 核心配置文件
学习完 MyBatis 的核心对象之后,接下来学习它的核心配置文件。MyBatis 核心配置文件配置了 MyBatis 的一些全局信息,包括数据库连接信息、MyBatis 运行时所需的各个特性,以及设置和影响 MyBatis 行为的一些属性。
2.2.1 配置文件的主要元素
MyBatis 的核心配置文件包含了很多影响 MyBatis 行为的重要信息。一个项目通常只会有一个核心配置文件,并且核心配置文件编写后也不会轻易改动。MyBatis 核心配置文件的主要元素如图 2-2 所示。
图2-2 MyBatis核心配置文件的主要元素

从图 2-2 中可以看到,<configuration> 元素是整个 XML 配置文件的根元素,相当于 MyBatis 各元素的管理员。<configuration> 有很多子元素,MyBatis 的核心配置就是通过这些子元素完成的。需要注意的是,在核心配置文件中,**<configuration>**的子元素必须按照图 2-2 中由上到下的顺序进行配置,否则 MyBatis 在解析 XML 配置文件的时候会报错。
mybatis-config.dtd 元素顺序 :
<!ELEMENT configuration (properties?,settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>2.2.2 <properties> 元素
<properties> 是一个配置属性的元素,该元素的作用是读取外部文件的配置信息。假设现在有一个配置文件 db.properties,该文件配置了数据库的连接信息,具体内容如下:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root如果想获取数据库的连接信息,可以在 MyBatis 的核心配置文件 mybatis-config.xml 中使用 <properties> 元素先引入 db.properties 文件:
<properties resource="db.properties"/>引入 db.properties 文件后,如果希望动态获取其中的数据库连接信息,可以使用 <dataSource> 元素配置:
<dataSource type="POOLED">
<!-- 数据库驱动 -->
<property name="driver" value="${jdbc.driver}" />
<!-- 连接数据库的 url -->
<property name="url" value="${jdbc.url}" />
<!-- 连接数据库的用户名 -->
<property name="username" value="${jdbc.username}" />
<!-- 连接数据库的密码 -->
<property name="password" value="${jdbc.password}" />
</dataSource>上述代码中,<dataSource> 元素中连接数据库的四个属性(driver、url、username 和 password)值将会由 db.properties 文件中对应的值来动态替换。
这样一来,<properties> 元素就可以通过 db.properties 文件实现动态参数配置。
ref:https://mybatis.org/mybatis-3/zh_CN/configuration.html#properties
2.2.3 <settings> 元素
<settings> 元素主要用于改变 MyBatis 运行时的行为,例如开启二级缓存、开启延迟加载等。
<settings> 元素中的常见配置参数如表 2-3 所示。
表2-3**<setings>**元素中的常见配置参数
| 配置参数 | 描述 | 有效值 | 默认值 |
|---|---|---|---|
cacheEnabled | 用于配置是否开启缓存 | true | false |
lazyLoadingEnabled | **延迟加载的全局开关。**开启时,所有关联对象都会延迟加载。特定关联关系中可以通过设置fetchType属性来覆盖该项的开关状态 | true | false |
aggressiveLazyLoading | 关联对象属性的延迟加载开关。当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性会按需加载 | true | false |
multipleResultSetsEnabled | 是否允许单一语句返回多结果集(需要兼容驱动) | true | false |
useColumnLabel | 使用列标签代替列名。不同的驱动在这方面有不同的表现,具体可参考驱动文档或通过测试两种模式来观察所用驱动的行为 | true | false |
useGeneratedKeys | 允许JDBC支持自动生成主键,需要驱动兼容。如果设置为true,则这个设置强制使用自动生成主键,尽管一些驱动不兼容但仍可正常工作 | true | false |
autoMappingBehavior | 指定MyBatis应如何自动映射列到字段或属性。NONE表示取消自动映射;PARTIAL只会自动映射没有定义嵌套结果集映射的结果集;FULL会自动映射任意复杂的结果集(无论是否嵌套) | NONE | PARTIAL |
defaultExecutorType | 配置默认的执行器。SIMPLE执行器是普通的执行器;REUSE执行器会复用预处理语句(prepared statements);BATCH执行器将复用语句并执行批量更新 | SIMPLE | REUSE |
defaultStatementTimeout | 配置超时时间,它决定驱动等待数据库响应的秒数 | 任何正整数 | 无 |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(CamelCase)映射 | true | false |
jdbcTypeForNull | 当没有为参数提供特定的JDBC类型时,为空值指定JDBC类型。某些驱动需要指定列的JDBC类型,多数情况直接用一般类型即可,例如NULL、VARCHAR或OTHER | NULL | VARCHAR |
表 2-3 中介绍了 <settings> 元素中的常见配置参数,这些配置参数在配置文件中的使用方式如下:
<settings>
<!-- 是否开启缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 是否开启延迟加载,如果开启,所有关联对象都会延迟加载 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 是否开启关联对象属性的延迟加载,如果开启,对任意延迟属性的调用都会使带有延迟加载属性的对象完整加载。否则每种属性都按需加载 -->
<setting name="aggressiveLazyLoading" value="true" />
</settings>表 2-3 中介绍的配置参数在大多数情况下都不需要开发人员去配置,通常只在需要时配置少数几项即可。了解这些可设置的参数值及其含义即可。
ref:https://mybatis.org/mybatis-3/zh_CN/configuration.html#settings
2.2.4 <typeAliases> 元素
核心配置文件若要引用一个 POJO 实体类,需要输入 POJO 实体类的全限定类名,而 POJO 实体类的全限定类名较长,如果直接输入容易拼写错误。这时,可以使用 **<typeAliases>** 元素为核心配置文件中的 POJO 实体类设置一个简短的别名,通过 MyBatis 的核心配置文件与映射文件相关联,减少全限定类名的冗余,以简化操作。例如,POJO 实体类 User 的全限定类名是 com.itheima.pojo.User,未设置别名之前,映射文件的 select 语句块若要引用 POJO 类 User,必须使用其全限定类名:
<select id="findById" parameterType="int" resultType="com.itheima.pojo.User">
select * from users where uid = #{id}
</select>在核心配置文件 mybatis-config.xml 中,使用 <typeAliases> 元素为 com.itheima.pojo.User 实体类定义别名:
<typeAliases>
<typeAlias alias="User" type="com.itheima.pojo.User"/>
</typeAliases>上述代码为全限定类名 com.itheima.pojo.User 定义了一个别名 User,定义了别名之后,映射文件中只需使用别名 User 就可以引用 POJO 实体类 com.itheima.pojo.User:
<select id="findById" parameterType="int" resultType="User">
select * from users where uid = #{id}
</select>如果有多个全限定类需要设置别名,有两种方式可以完成设置:
- 在
<typeAliases>元素下,使用多个<typeAlias>元素为每个全限定类逐个配置别名:
<typeAliases>
<typeAlias alias="User" type="com.itheima.pojo.User"/>
<typeAlias alias="Student" type="com.itheima.pojo.Student"/>
<typeAlias alias="Employee" type="com.itheima.pojo.Employee"/>
<typeAlias alias="Animal" type="com.itheima.pojo.Animal"/>
</typeAliases>- 通过自动扫描包的形式自定义别名:
<typeAliases>
<package name="com.itheima.pojo"/>
</typeAliases>按照上述代码配置后,MyBatis 会自动扫描 **<package>** 元素的 **name** 属性指定的包 **com.itheima.pojo**,并自动将该包下的所有实体类以首字母小写的类名作为别名。例如,它会自动给 **com.itheima.pojo.User** 设置别名 **user**。
除了可以使用 <typeAliases> 元素为实体类自定义别名外,MyBatis 框架还为许多常见的 Java 类型(如数值、字符串、日期和集合等)提供了相应的默认别名。MyBatis 默认的常见 Java 类型的别名如表 2-4 所示。
表2-4 MyBatis默认的常见Java类型的别名
| 别名 | 映射的类型 |
|---|---|
| _byte | byte |
| _long | long |
| _short | short |
| _int | int |
| _integer | int |
| _double | double |
| _float | float |
| _boolean | boolean |
| string | String |
| byte | Byte |
| long | Long |
| short | Short |
| int | Integer |
| integer | Integer |
| double | Double |
| float | Float |
| boolean | Boolean |
| date | Date |
| decimal | BigDecimal |
| bigdecimal | BigDecimal |
| object | Object |
| map | Map |
| hashmap | HashMap |
| list | List |
| arraylist | ArrayList |
| collection | Collection |
| iterator | Iterator |
表 2-4 所列举的别名可以在 MyBatis 中直接使用,但由于别名不区分大小写,所以在使用时要注意重复定义的覆盖问题。
ref:https://mybatis.org/mybatis-3/zh_CN/configuration.html#typeAliases
2.2.5 <environments> 元素
MyBatis 可以配置多套运行环境,如开发环境、测试环境、生产环境等,可以灵活选择不同的配置,从而将 SQL 映射到不同运行环境的数据库中。不同的运行环境可以通过 <environments> 元素来配置,但不管增加几套运行环境,都必须明确选择出当前要用的唯一的一个运行环境。
MyBatis 的运行环境信息包括事务管理器和数据源。在 MyBatis 的核心配置文件中,MyBatis 通过 <environment> 元素定义一个运行环境。<environment> 元素有两个子元素:<transactionManager> 元素和 <dataSource> 元素。
<transactionManager>元素用于配置运行环境的事务管理器;<dataSource>元素用于配置运行环境的数据源信息。
使用 <environments> 元素进行环境配置的示例代码如下:
<environments default="development">
<environment id="development">
<!-- 设置使用 JDBC 事务管理 -->
<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>在上述示例代码中,<environments> 元素是环境配置的根元素,它包含一个 default 属性,该属性用于指定默认环境的 id。<environment> 是 <environments> 元素的子元素,一个 <environments> 元素下可以有多个 <environment> 子元素,<environment> 元素的 id 属性用于设置所定义环境的 id 值。
在 <environment> 元素内,使用 <transactionManager> 元素配置事务管理,它的 type 属性用于指定事务管理的方式,即使用哪种事务管理器;<dataSource> 元素用于配置数据源,它的 type 属性用于指定所使用的数据源。
在 MyBatis 中,<transactionManager> 元素可以配置两种类型的事务管理器,分别是 **JDBC** 和 **MANAGED**。这两个事务管理器的含义如下:
- JDBC:此配置直接使用 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务的作用域。
- MANAGED:此配置不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。默认情况下,它会关闭连接,但有些容器并不希望这样,为此可以将
<transactionManager>元素的 closeConnection 属性设置为 false 来阻止它默认的关闭行为。
注意:
如果项目中使用的是 Spring + MyBatis,则没有必要在 MyBatis 中配置事务管理器,因为在实际开发中项目会使用 Spring 自带的管理器来实现事务管理。
对于数据源的配置,MyBatis 框架提供了 UNPOOLED、POOLED 和 JNDI 三种数据源类型,具体如下:
**UNPOOLED******表示数据源为无连接池类型。配置此数据源类型后,程序在每次被请求时会打开和关闭数据库连接。UNPOOLED 适用于对性能要求不高的简单应用程序。UNPOOLED 类型的数据源需要配置五种属性,如表 2-5 所示。
表2-5 UNPOOLED数据源需要配置的属性
| 属性 | 说明 |
|---|---|
| driver | JDBC驱动的Java类的完全限定名(并不是JDBC驱动中可能包含的数据源类) |
| url | 数据库的URL地址 |
| username | 登录数据库的用户名 |
| password | 登录数据库的密码 |
| defaultTransactionIsolationLevel | 默认的连接事务隔离级别 |
**POOLED******POOLED 表示数据源为连接池类型。POOLED 数据源利用“池”的概念将 JDBC 连接对象组织起来,节省了创建新的连接对象时需要初始化和认证的时间。POOLED 数据源使并发 Web 应用可以快速响应请求,是当前比较流行的数据源配置类型。配置 POOLED 数据源类型时,除了可以使用表 2-5 中的五种属性外,还可以配置更多的属性,如表 2-6 所示。
表2-6 POOLED数据源可额外配置的属性
| 属性 | 说明 |
|---|---|
| poolMaximumActiveConnections | 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值为10 |
| poolMaximumIdleConnections | 任意时间可能存在的空闲连接数 |
| poolMaximumCheckoutTime | 在被强制返回前,池中连接被检出(checked out)时间,默认值为20000毫秒,即20秒 |
| poolTimeToWait | 如果获取连接花费的时间较长,它会给连接池打印状态日志并重新尝试获取一个连接(避免在错误配置的情况下一直处于无提示的失败),默认值为20000毫秒,即20秒 |
| poolPingEnabled | 是否启用侦测查询,若开启,则必须使用一个可执行的SQL语句(最好是一个非常快的SQL)设置poolPingQuery属性,默认值为false |
| poolPingQuery | 发送到数据库的侦测查询,用于检验连接是否处在正常工作秩序中,默认是"NO PING QUERY SET" |
| poolPingConnectionsNotUsedFor | 配置poolPingQuery的使用频度,该属性的值可以被设置成匹配具体的数据库连接超时时间,从而避免不必要的侦测,默认值为0(表示所有连接每一时刻都被侦测,只有poolPingEnabled的属性值为true时适用) |
**JNDI******JNDI 表示数据源可以在 EJB 或应用服务器等容器中使用。配置 JNDI 数据源时,只需要配置两个属性,如表 2-7 所示。
表2-7 JNDI数据源需要配置的属性
| 属性 | 说明 |
|---|---|
| initial_context | 该属性主要用于在InitialContext中寻找上下文(即initialContext.lookup(initial_context))。该属性为可选属性,在忽略时,data_source属性会直接从InitialContext中寻找 |
| data_source | 该属性表示引用数据源对象位置的上下文路径。如果提供了initial_context配置,那么程序会在其返回的上下文中进行查找;如果没有提供,则直接在InitialContext中查找 |
2.2.6 <mappers> 元素
MyBatis 的核心配置文件中,<mappers> 元素用于引入 MyBatis 映射文件。映射文件包含了 POJO 对象和数据表之间的映射信息,MyBatis 通过核心配置文件中的 <mappers> 元素找到映射文件并解析其中的映射信息。通过 <mappers> 元素引入映射文件的方法有四种,具体如下:
- 使用类路径引入
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>- 使用本地文件路径引入
<mappers>
<mapper url="file:///D:/com/itheima/mapper/UserMapper.xml"/>
</mappers>- 使用接口类引入
<mappers>
<mapper class="com.itheima.mapper.UserMapper"/>
</mappers>- 使用包名引入
<mappers>
<package name="com.itheima.mapper"/>
</mappers>2.3 MyBatis 映射文件
MyBatis 的真正强大之处在于可以配置 SQL 映射语句。相比于 JDBC,使用 MyBatis 可使 SQL 映射配置的代码量大大减少,并且 MyBatis 专注于 SQL 语句,对于开发人员来说,进行项目开发时,使用 MyBatis 的 SQL 映射配置可最大限度地进行 SQL 调优,以保证性能。MyBatis 通过许多特定的元素将 SQL 映射语句配置在映射文件中。
2.3.1 MyBatis 映射文件中的常用元素
MyBatis 映射文件中,<mapper> 元素是映射文件的根元素,其他元素都是它的子元素。MyBatis 映射文件中的常用元素如表 2-8 所示。
表2-8 MyBatis映射文件中的常用元素
| 元素 | 说明 |
|---|---|
<mapper> | 映射文件的根元素,该元素只有一个namespace(命名空间)属性,namespace属性作用如下: (1)用于区分不同的mapper,全局唯一 (2)绑定DAO接口,即面向接口编程。当namespace绑定某一接口之后,可以不用写该接口的实现类,MyBatis会通过接口的全限定类名查找到对应的mapper配置来执行SQL语句,因此namespace的命名必须跟接口同名 |
<cache> | 配置给定命名空间的缓存 |
<cache-ref> | 从其他命名空间引用缓存配置 |
<select> | 用于映射查询语句 |
<insert> | 用于映射插入语句 |
<update> | 用于映射更新语句 |
<delete> | 用于映射删除语句 |
<sql> | 可以重用的SQL块,也可以被其他语句使用 |
<resultMap> | 描述数据库结果集和对象的对应关系 |
注意:
在不同的映射文件中,<mapper>元素的子元素的 id 可以是相同的,MyBatis 通过<mapper>元素的 namespace 属性值和子元素的 id 区分不同的 Mapper.xml 文件。接口中的方法与映射文件中 SQL 语句 id 应一一对应。
2.3.2 <select> 元素
<select> 元素用于映射查询语句,它可以从数据库中查询数据并返回:
<select id="findById" parameterType="int" resultType="io.weew12.github.pojo.User">
select *
from heima_ssm_book.ch1_users
where uid = #{id}
</select>上述语句中的 <select> 元素 id 属性的值为 findById;parameterType 属性的值为 int,表示接收一个 int(或 Integer)类型的参数;resultType 的值为 User 类的全限定类名,表示返回一个 User 类型的对象;参数符号 #{参数名} 表示创建一个预处理语句参数,在 JDBC 中,这个预处理语句参数由 “?” 来标识。
<select> 元素中,除了上述示例代码中的几个属性外,还有其他一些可以配置的属性,具体如表 2-9 所示。
表2-9**<select>**元素的常用属性
| 属性 | 说明 |
|---|---|
id | 表示命名空间中**<select>**元素的唯一标识,通过该标识可以调用这条查询语句。如果id属性的值在当前命名空间不唯一,MyBatis会抛出异常 |
parameterType | 用于指定SQL语句所需参数类的全限定类名或者别名。它是一个可选属性,因为MyBatis可以通过TypeHandler推断出具体传入语句的参数。其默认值是unset(依赖于驱动) |
resultType | 用于指定执行这条SQL语句返回的类的全限定类名或别名。如果属性值是集合,则需要指定集合可以包含的类型(如HashMap) |
resultMap | 表示外部**resultMap**的命名引用。**resultMap**和**resultType**不能同时使用 |
flushCache | SQL语句被调用之后,flushCache用于指定是否需要MyBatis清空本地缓存和二级缓存。flushCache值为布尔类型(true/false),默认值为false。如果设置为**true**,则任何时候只要SQL语句被调用,都会清空本地缓存和二级缓存 |
useCache | 用于控制二级缓存的开启和关闭。其值为布尔类型(true |
timeout | 用于设置超时时间,单位为秒,超时将抛出异常 |
fetchSize | 获取记录的总条数设定,默认值是unset(依赖于驱动) |
statementType | 用于设置MyBatis预处理类,其值有3个:STATEMENT、PREPARED(默认值)或CALLABLE,分别对应JDBC中的Statement、PreparedStatement和CallableStatement |
resultSetType | 表示结果集的类型,其值可设置为DEFAULT、FORWARD_ONLY、SCROLL_SENSITIVE或SCROLL_INSENSITIVE |
2.3.3 <insert> 元素
<insert> 元素用于映射插入语句,在执行完 <insert> 元素中定义的 SQL 语句后,会返回插入记录的数量:
<!-- 插入操作 -->
<insert id="addUser" parameterType="com.itheima.pojo.User">
insert into users (uid, uname, uage) values (#{uid}, #{uname}, #{uage})
</insert>上述语句中的 <insert> 元素 id 属性的值为 addUser;parameterType 属性的值为 User 类的全限定类名,表示接收一个 User 类型的参数;SQL 语句中的 #{uid}、#{uname} 和 #{uage} 表示通过占位符的形式接收参数 uid、uname 和 uage。
<insert> 元素除了上述示例代码中的几个属性外,还有其他一些可以配置的属性,具体如表 2-10 所示。
表2-10**<inser>**元素的其他常用属性
| 属性 | 说明 |
|---|---|
id | 表示命名空间中的唯一标识,通过该标识可以调用这条语句。如果id属性的值在当前命名空间不唯一,MyBatis会抛出异常 |
parameterType | 用于指定SQL语句所需参数类的全限定类名或者别名。它是一个可选属性,因为MyBatis可以通过TypeHandler推断出具体传入语句的参数。其默认值是unset(依赖于驱动) |
flushCache | SQL语句被调用之后,flushCache用于指定是否需要MyBatis清空本地缓存和二级缓存。其值为布尔类型(true |
timeout | 用于设置超时时间,单位为秒,超时将抛出异常 |
statementType | 用于设置MyBatis预处理类,它有3个值:STATEMENT、PREPARED(默认值)或CALLABLE,分别对应JDBC中的Statement、PreparedStatement和CallableStatement |
keyProperty | 将插入或更新操作的返回值赋值给POJO类的某个属性。如果需要设置联合主键,在多个值之间用逗号隔开 |
keyColumn | 该属性用于设置第几列是主键,当主键列不是表中的第一列时需要设置。需要设置联合主键时,在多个值之间用逗号隔开 |
useGeneratedKeys | 该属性会使MyBatis调用JDBC的**getGeneratedKeys()**方法来获取由数据库内部生产的主键,如MySQL和SQLServer等自动递增的字段,其默认值为**false** |
通常,执行插入操作后需要获取插入成功的数据生成的主键值,不同类型数据库获取主键值的方式不同,下面分别对支持主键自动增长的数据库获取主键值和不支持主键自动增长的数据库获取主键值的方式进行介绍。
- 使用支持主键自动增长的数据库获取主键值
如果使用的数据库支持主键自动增长(如 MySQL 和 SQL Server),那么可以通过 keyProperty 属性指定 POJO 类的某个属性接收主键返回值(通常会设置到 id 属性上),然后将 useGeneratedKeys 的属性值设置为 true。示例代码如下:
<!-- 数据库支持主键自动增长,插入数据,并返回插入成功的数据生成的主键值 -->
<insert id="addUser" parameterType="com.itheima.pojo.User" keyProperty="uid" useGeneratedKeys="true">
insert into users (uname, uage) values (#{uname}, #{uage})
</insert>- 使用不支持主键自动增长的数据库获取主键值
如果使用的数据库不支持主键自动增长(如 Oracle),或者支持增长的数据库取消了主键自增的规则,可以使用 MyBatis 提供的 <selectKey> 元素来自定义主键。<selectKey> 元素在使用时可以设置以下几种属性:
<selectKey
keyProperty="id"
resultType="Integer"
order="BEFORE"
statementType="PREPARED">
<!-- 自定义主键生成逻辑 -->
</selectKey>在上述 <selectKey> 元素的几个属性中,keyProperty、resultType 和 statementType 的作用与前面讲解的相同。order 属性可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先执行 <selectKey> 元素中的配置来设置主键,然后执行插入语句;如果设置为 AFTER,那么它先执行插入语句,然后再执行 <selectKey> 元素中的配置内容。
使用 MyBatis 提供的 <selectKey> 元素来自定义生成主键的具体配置示例如下:
<!-- 数据库不支持主键自动增长,插入数据,并返回插入成功的数据生成的主键值 -->
<insert id="addUser" parameterType="com.itheima.pojo.User">
<selectKey keyProperty="uid" resultType="Integer" order="BEFORE">
select if(max(uid) is null, 1, max(uid) + 1) as newId from users
</selectKey>
insert into users (uid, uname, uage) values (#{uid}, #{uname}, #{uage})
</insert>执行上述示例代码时,<selectKey> 元素会先运行,通过自定义的语句来设置数据表中的主键,然后再调用插入语句。在设置数据表中的主键时,如果 users 表中没有记录,则将 uid 设置为 1,否则将 uid 的最大值加 1,作为新的主键。
2.3.4 <update> 元素
<update> 元素用于映射更新语句,它可以更新数据库中的数据。在执行完 <update> 元素中定义的 SQL 语句后,会返回更新的记录数量:
<!-- 更新操作 -->
<update id="updateUser" parameterType="com.itheima.pojo.User">
update users set uname= #{uname}, uage = #{uage} where uid = #{uid}
</update>在上述语句中,<update> 元素 id 属性的值为 updateUser;parameterType 属性的值为 User 类的全限定类名,表示接收一个 User 类型的参数;SQL 语句中的 #{uid}、#{uname} 和 #{uage} 表示通过占位符的形式接收参数 uid、uname 和 uage。
<update> 元素属性与 <insert> 元素属性一致。
2.3.5 <delete> 元素
<delete> 元素用于映射删除语句,在执行完 <delete> 元素中的 SQL 语句之后,会返回删除的记录数量:
<!-- 删除操作 -->
<delete id="deleteUser" parameterType="Integer">
delete from users where uid = #{uid}
</delete><delete> 元素除了上述示例代码中的几个属性外,还有其他一些可以配置的属性,具体如表 2-11 所示。
表2-11**<delete>**元素的常用属性
| 属性 | 说明 |
|---|---|
id | 表示命名空间中的唯一标识,通过该属性可以调用这条语句。如果id属性的值在当前命名空间不唯一,MyBatis会抛出异常 |
parameterType | 用于指定SQL语句所需参数类的全限定名或者别名。它是一个可选属性,因为MyBatis可以通过TypeHandler推断出具体传入语句的参数。其默认值是unset(依赖于驱动) |
flushCache | SQL语句被调用之后,flushCache指定是否需要MyBatis清空本地缓存和二级缓存。其值为布尔类型(true |
timeout | 用于设置超时时间,单位为秒。超时将抛出异常 |
statementType | 用于设置MyBatis预处理类,它有3个值:STATEMENT、PREPARED(默认值)和CALLABLE,分别对应JDBC中的Statement、PreparedStatement和CallableStatement |
由表 2-11 可知,<delete> 元素的属性与 <insert> 元素的属性大致相同,但 <delete> 元素比 <insert> 元素少了三个与键值相关的属性,即 keyProperty、keyColumn 和 useGeneratedKeys。
2.3.6 <sql> 元素
在一个映射文件中,通常需要定义多条 SQL 语句,这些 SQL 语句的组成可能有一部分是相同的(如多条 select 语句中都查询相同的 id、username 字段),如果每一个 SQL 语句都重写一遍相同的部分,势必会增加代码量,导致映射文件过于臃肿。针对以上问题,可以在映射文件中使用 MyBatis 所提供的 <sql> 元素,将这些 SQL 语句中相同的组成部分抽取出来,然后在需要的地方引用。
<sql> 元素的作用是定义可重用的 SQL 代码片段,它可以被包含在其他语句中。**<sql>**元素可以被静态地(在加载参数时)参数化,**<sql>****元素不同的属性值随包含的对象不同而发生变化。**例如,定义一个包含 uid、uname 和 uage 字段的代码片段,具体如下:
<sql id="userColumns">${alias}.uid, ${alias}.uname, ${alias}.uage</sql>这一代码片段可以包含在其他语句中使用,如 select 查询语句、update 更新语句等。下面将上述代码片段包含在 select 查询语句中,具体如下:
<select id="findUserById" parameterType="Integer" resultType="com.itheima.pojo.User">
select
<include refid="userColumns">
<property name="alias" value="u" />
</include>
from users u
where uid = #{uid}
</select>在上述代码中,使用 <include> 元素的 refid 属性引用了自定义的代码片段,refid 属性的值为自定义代码片段的 id。使用 <include> 元素将 <sql> 元素参数化,<sql> 元素中的属性值随包含的对象而发生变化。
上面示例只是一个简单的引用查询,在实际开发中,可以更灵活地定义 SQL 片段。下面实现一个根据客户 id 查询客户信息的 SQL 片段,示例代码如下:
<!-- 定义要查询的表 -->
<sql id="someInclude">
from <include refid="${include_target}" />
</sql>
<!-- 定义查询列 -->
<sql id="userColumns">
uid, uname, uage
</sql>
<!-- 根据客户 id 查询客户信息 -->
<select id="findUserById" parameterType="Integer" resultType="com.itheima.pojo.User">
select
<include refid="userColumns"/>
<include refid="someInclude">
<property name="include_target" value="users" />
</include>
where uid = #{uid}
</select>上述代码所有的代码片段在程序运行时都会由 MyBatis 组合成 SQL 语句来执行需要的操作。
2.3.7 <resultMap> 元素
<resultMap> 元素表示结果映射集,是 MyBatis 中最重要也是功能最强大的元素。<resultMap> 元素的主要作用是定义映射规则、更新级联和定义类型转换器等。
默认情况下,MyBatis 程序在运行时会自动将查询到的数据与需要返回的对象的属性进行匹配赋值(数据表中的列名与对象的属性名称完全一致才能匹配成功并赋值)。然而实际开发时,数据表中的列和需要返回的对象的属性可能不会完全一致,这种情况下 MyBatis 不会自动赋值,这时就需要使用 **<resultMap>** 元素进行结果集映射。
下面通过一个具体的案例演示使用 <resultMap> 元素进行结果集映射,具体步骤如下:
(1)在名称为 heima_ssm_book 的数据库中,创建一个 t_student 表,并插入几条测试数据,具体代码如下:
use heima_ssm_book;
create table t_student
(
sid int primary key auto_increment,
sname varchar(50),
sage int
);
insert into t_student(sname, sage)
values ('lucy', 25);
insert into t_student(sname, sage)
values ('lily', 20);
insert into t_student(sname, sage)
values ('jim', 20);(2)在项目 src/main/java 目录下创建实体类 Student,用于封装学生信息。在类中定义 id、name 和 age 属性,以及属性的 getter/setter 方法和 toString() 方法。
package io.weew12.github.pojo;
public class Student {
// 学生id
private Integer id;
// 姓名
private String name;
// 年龄
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}(3)
在项目下创建接口定义文件io.weew12.github.mapper.StudentMapper:
package io.weew12.github.mapper;
import io.weew12.github.pojo.Student;
import java.util.List;
public interface StudentMapper {
List<Student> findAllStudent();
}在项目下创建映射文件src/main/resources/mapper/StudentMapper.xml,并在映射文件中编写映射查询语句。StudentMapper.xml 映射文件主要用于实现 SQL 语句和 Java 对象之间的映射,使 SQL 语句查询出来的关系型数据能够被封装成 Java 对象。
<?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="io.weew12.github.mapper.StudentMapper">
<resultMap id="studentMap" type="io.weew12.github.pojo.Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="age" column="sage"/>
</resultMap>
<select id="findAllStudent" resultMap="studentMap">
select *
from heima_ssm_book.t_student;
</select>
</mapper>在文件中,使用 <resultMap> 元素进行结果集的映射。其中,<id> 子元素和 <result> 子元素的 property 属性的值与 Student 类的属性名一一对应,column 属性的值与数据表 t_student 的列名一一对应。<select> 元素的 resultMap属性的值为 studentMap,表示引用 id 值为 studentMap 的元素。
创建
(4)在核心配置文件 mybatis-config.xml 中,引入 StudentMapper.xml,将 StudentMapper.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>
<!-- 加载类路径下的属性文件-->
<properties resource="db.properties"/>
<!-- 环境配置-->
<environments default="development">
<environment id="development">
<!-- 事务管理器-->
<transactionManager type="JDBC"/>
<!-- 数据源配置-->
<dataSource type="POOLED">
<!-- 数据库连接相关配置 使用db.properties中的内容-->
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
</environments>
<!-- mapping 文件路径配置-->
<mappers>
<mapper resource="io/github/weew12/mapper/StudentMapper.xml"/>
</mappers>
</configuration>(5)在项目的 src/test/java 目录下的 Test 包中创建测试类 MyBatisTest,在测试类中编写测试方法 findAllStudentTest(),用于测试 <resultMap> 元素实现查询结果的映射。
package io.weew12.github;
import io.weew12.github.pojo.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.Reader;
import java.util.List;
public class MyBatisTest {
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
@Before
public void init() {
String resources = "mybatis-config.xml";
Reader reader;
try {
reader = Resources.getResourceAsReader(resources);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
sqlSession = sqlSessionFactory.openSession();
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void findAllStudentTest() {
List<Student> list = sqlSession.selectList("io.weew12.github.mapper.StudentMapper.findAllStudent");
for (Student student : list) {
System.out.println(student);
}
}
@After
public void destroy() {
sqlSession.commit();
sqlSession.close();
}
}在文件中,编写了一个 init() 方法,并使用 @Before 注解标注。JUnit 中的 @Before 注解用于初始化,每个测试方法都要执行一次 init() 方法。在 init() 方法中,先根据 MyBatis 的核心配置文件构建 SqlSessionFactory 对象,再通过 SqlSessionFactory 对象创建 SqlSession 对象。
编写了一个测试方法 findAllStudentTest(),在该方法中,调用 SqlSession 对象的 selectList() 方法执行查询操作。selectList() 方法的参数是映射文件StudentMapper.xml 中定义的 <select> 元素 id 的全限定类名,这里为 io.weew12.github.mapper.StudentMapper.findAllStudent,表示将执行这个 id 所表示的元素中的 SQL 语句。
代码定义了一个 destroy() 方法,用于释放资源,并使用 @After 注解标注,使用 @After 注解标注的方法在每个方法执行完后都要执行一次。需要注意的是,在测试类 MyBatisTest 中,每一个用 @Test 注解标注的方法称为测试方法,它们的调用顺序为 @Before → @Test → @After。
运行 MyBatisTest 测试类,控制台的输出结果如图 2-3 所示。对图 2-3 中的运行结果进行分析可知,虽然 t_student 数据表的列名与 Student 对象的属性名完全不一样,但查询出的数据还是被正确地封装到了 Student 对象中,这表明 <resultMap> 元素实现了查询结果的映射。

学一招:使用工具类创建 SqlSession 对象
在上述案例中,由于每个方法执行时都需要读取配置文件,并根据配置文件的信息构建 SqlSessionFactory 对象、创建 SqlSession 对象、释放资源,这产生了大量的重复代码。为了简化开发,可以将读取配置文件和释放资源的代码封装到一个工具类中,然后通过工具类创建 SqlSession 对象。
package io.weew12.github.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.Reader;
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
// 使用 MyBatis 提供的 Resources 类加载 MyBatis 的配置文件
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
// 构建 SqlSessionFactory 对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取 SqlSession 对象的静态方法
*
* @return SqlSessionFactory
*/
public static SqlSession getSession() {
return sqlSessionFactory.openSession();
}
}这样,在使用时就只创建了一个 SqlSessionFactory 对象,并且可以通过工具类的 getSession() 方法来获取 SqlSession 对象。
2.4 案例:员工管理系统
本章对 MyBatis 的核心配置进行了详细讲解,包括 MyBatis 的核心对象、MyBatis 的核心配置文件和 MyBatis 映射文件中的元素。现有一员工表,如表 2-12 所示。

本案例要求根据表 2-12 在数据库中创建一个 employee 表,并利用本章所学知识完成一个员工管理系统,该系统需要实现以下几个功能:
- 根据 id 查询员工信息。
- 新增员工信息。
- 根据 id 修改员工信息。
- 根据 id 删除员工信息。
数据表:
use heima_ssm_book;
CREATE TABLE ch2_employee
(
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '员工编号',
name VARCHAR(50) NOT NULL COMMENT '员工名称',
age INT NOT NULL COMMENT '员工年龄',
position VARCHAR(50) NOT NULL COMMENT '员工职位'
);
INSERT INTO ch2_employee (id, name, age, position)
VALUES (null, '张三', 20, '员工'),
(null, '李四', 18, '员工'),
(null, '王五', 35, '经理');config:
<?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>
<!-- 加载类路径下的属性文件-->
<properties resource="db.properties"/>
<!-- 类型别名设置-->
<typeAliases>
<typeAlias type="io.weew12.github.pojo.Employee" alias="emp"/>
</typeAliases>
<!-- 环境配置-->
<environments default="development">
<environment id="development">
<!-- 事务管理器-->
<transactionManager type="JDBC"/>
<!-- 数据源配置-->
<dataSource type="POOLED">
<!-- 数据库连接相关配置 使用db.properties中的内容-->
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
</environments>
<!-- mapping 文件路径配置-->
<mappers>
<!-- <mapper resource="io/weew12/github/mapper/StudentMapper.xml"/> -->
<mapper resource="io/weew12/github/mapper/EmployeeMapper.xml"/>
</mappers>
</configuration>pojo 类:
package io.weew12.github.pojo;
public class Employee {
// 员工编号
private Integer eid;
// 员工名称
private String ename;
// 员工年龄
private Integer eage;
// 员工职位
private String eposition;
public Integer getEid() {
return eid;
}
public void setEid(Integer eid) {
this.eid = eid;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Integer getEage() {
return eage;
}
public void setEage(Integer eage) {
this.eage = eage;
}
public String getEposition() {
return eposition;
}
public void setEposition(String eposition) {
this.eposition = eposition;
}
@Override
public String toString() {
return "EmployeeMapper{" +
"eid=" + eid +
", ename='" + ename + '\'' +
", eage=" + eage +
", eposition='" + eposition + '\'' +
'}';
}
}接口类:
package io.weew12.github.mapper;
import io.weew12.github.pojo.Employee;
public interface EmployeeMapper {
/**
* 1. 根据 id 查询员工信息。
*/
Employee findById(Integer eid);
/**
* 2. 新增员工信息。
*/
int insert(Employee employee);
/**
* 3. 根据 id 修改员工信息。
*/
int updateById(Integer eid, Employee employee);
/**
* 4. 根据 id 删除员工信息。
*/
int deleteById(Integer eid);
}mapper:
<?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="io.weew12.github.mapper.EmployeeMapper">
<resultMap id="employeeMap" type="emp">
<id property="eid" column="id"/>
<result property="ename" column="name"/>
<result property="eage" column="age"/>
<result property="eposition" column="position"/>
</resultMap>
<select id="findById" parameterType="int" resultMap="employeeMap">
select *
from heima_ssm_book.ch2_employee
where id = #{eid};
</select>
<insert id="insert" parameterType="emp">
insert into heima_ssm_book.ch2_employee(name, age, position)
VALUES (#{ename}, #{eage}, #{eposition})
</insert>
<update id="updateById" parameterType="map">
update heima_ssm_book.ch2_employee
set name=#{arg1.ename},
age=#{arg1.eage},
position=#{arg1.eposition}
where id = #{arg0};
</update>
<delete id="deleteById" parameterType="int">
delete
from heima_ssm_book.ch2_employee
where id = #{eid};
</delete>
</mapper>测试类:
package io.weew12.github;
import io.weew12.github.mapper.EmployeeMapper;
import io.weew12.github.pojo.Employee;
import io.weew12.github.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class EmployeeTest {
private SqlSession sqlSession;
@Before
public void init() {
sqlSession = MybatisUtils.getSession();
}
/**
* 1. 根据 id 查询员工信息。
*/
@Test
public void findByIdTest() {
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = employeeMapper.findById(2);
System.out.println(employee);
}
/**
* 2. 新增员工信息。
*/
@Test
public void insertTest() {
Employee employee = new Employee();
employee.setEname("weew12");
employee.setEage(26);
employee.setEposition("老板");
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
int insert = employeeMapper.insert(employee);
System.out.println("插入" + insert + "条数据");
// !!
sqlSession.commit();
}
/**
* 3. 根据 id 修改员工信息。
*/
@Test
public void updateByIdTest() {
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = employeeMapper.findById(1);
System.out.println("修改前:" + employee);
employee.setEage(55);
int i = employeeMapper.updateById(1, employee);
System.out.println("更新" + i + "条数据");
employee = employeeMapper.findById(1);
System.out.println("修改后:" + employee);
// !!
sqlSession.commit();
}
/**
* 4. 根据 id 删除员工信息。
*/
@Test
public void deleteByIdTest() {
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
int i = employeeMapper.deleteById(3);
System.out.println("删除" + i + "条数据");
// !!
sqlSession.commit();
}
@After
public void destroy() {
sqlSession.close();
}
}结果:

2.5 小结
本章主要对 MyBatis 的核心配置进行了详细讲解。首先,讲解了 MyBatis 中的三个重要核心对象 SqlSessionFactoryBuilder、SqlSessionFactory 和 SqlSession;然后,介绍了核心配置文件中的元素及其使用;最后,对映射文件中的几个主要元素进行了详细讲解。通过学习本章的内容,能够了解 MyBatis 中三个核心对象的作用,熟悉核心配置文件中常用元素的使用,并掌握映射文件中常用元素的使用。
2.6 weew12 补充
MyBatis的两种不同使用方式——原理和区别
两种使用方式的对比
方式1:使用Mapper接口(推荐)
<!-- namespace指向Mapper接口 -->
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectById" resultType="User">...</select>
</mapper>// 通过Mapper接口调用
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectById(1L);方式2:直接使用SqlSession
<!-- namespace可以是任意字符串,通常用POJO类名 -->
<mapper namespace="com.example.pojo.User">
<select id="selectById" resultType="User">...</select>
</mapper>// 直接通过SqlSession调用,需要完整的statement路径
User user = sqlSession.selectOne("com.example.pojo.User.selectById", 1L);原理分析
MyBatis的SQL语句注册机制
MyBatis在启动时会扫描所有mapper.xml文件,将每个SQL语句注册为MappedStatement对象:
// MyBatis内部的大致逻辑
Map<String, MappedStatement> mappedStatements = new HashMap<>();
// 注册SQL语句的key是:namespace + "." + id
String statementId = namespace + "." + id;
mappedStatements.put(statementId, mappedStatement);两种方式的底层调用
- Mapper接口方式(实际是方式2的封装):
// getMapper()底层还是调用SqlSession的方法
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 实际上转换为:sqlSession.selectOne("com.example.mapper.UserMapper.selectById", 1L)- 直接SqlSession方式:
// 直接使用完整的statement ID
sqlSession.selectOne("com.example.pojo.User.selectById", 1L)为什么两种方式都可以?
核心原因:Statement ID的唯一性
MyBatis只要求namespace.id在整个系统中唯一,不关心namespace具体是什么。
<!-- 这三种写法在MyBatis看来都是合法的 -->
<mapper namespace="com.example.pojo.User">
<mapper namespace="user">
<mapper namespace="任意字符串">两种方式的优缺点
直接SqlSession方式
优点:
- 简单直接,适合快速demo和小项目
- 不需要定义Mapper接口
- 灵活,namespace可以随意命名
缺点:
- 类型不安全:容易写错字符串
- 难以重构:重命名方法需要手动修改所有调用处
- IDE不支持:没有代码提示和自动补全
- 不利于维护
Mapper接口方式
优点:
- 类型安全:编译期检查
- IDE支持:代码提示、重构支持
- 易于测试和维护
- 面向接口编程,符合Java最佳实践
缺点:
- 需要多定义一个接口
- 学习曲线稍高
【思考题】
- 请简述
**<mappers>**元素引入映射文件的四种方式。
在 MyBatis 中,<mappers> 元素用于配置 SQL 映射文件或类。通过 <mappers> 元素可以将 XML 映射器文件或注解的接口类注册到 MyBatis 的配置中。以下是四种常见的引入映射文件的方式:
- 使用全限定资源路径:可以通过指定映射文件的完整路径来加载映射文件。例如:
<mappers>
<mapper resource="com/example/mapping/UserMapper.xml"/>
</mappers>这种方法适用于当你的映射文件位于项目的类路径下时。
- 使用类路径下的相对路径:如果映射文件放在了某个包内,可以直接给出相对于类路径的路径。比如:
<mappers>
<mapper resource="mapping/UserMapper.xml"/>
</mappers>注意这里不需要写出完整的包名,但需要保证该路径相对于类路径是正确的。
- 使用接口类:直接指定实现了
Mapper接口的 Java 类,MyBatis 将自动查找与该接口同名的 XML 文件(默认情况下)或者利用注解形式的映射。
<mappers>
<mapper class="com.example.mapping.UserMapper"/>
</mappers>此处假定存在一个名为 UserMapper 的接口,并且可能有一个对应的 XML 配置文件或是这个接口上使用了 @Select, @Insert 等 MyBatis 注解。
- 使用包扫描:如果你有多个 mapper 接口分布在同一个包及其子包下,可以使用
<package>标签来一次性引入整个包里的所有 mapper。
<mappers>
<package name="com.example.mapping"/>
</mappers>使用这种方式时,MyBatis 会自动扫描指定包以及其子包中的所有接口,并尝试加载对应的 XML 文件或解析注解。
- 请简述 MyBatis 映射文件中的常用元素及其作用。
MyBatis 的映射文件主要由一系列的 XML 标签组成,这些标签定义了如何从数据库中读取数据、更新数据等操作。下面是一些常用的元素及其功能描述:
<select>:用于定义查询语句。它允许你执行 SQL 查询并将结果集映射到 Java 对象上。例如:
<select id="selectAllUsers" resultType="User">
SELECT * FROM users
</select><insert>:用于定义插入新记录的操作。这通常涉及到向数据库表中添加新的行。
<insert id="insertUser">
INSERT INTO users (name, email) VALUES (#{name}, #{email})
</insert><update>:用来修改现有记录的信息。通过此标签你可以更新数据库中的某条或多条记录。
<update id="updateUser">
UPDATE users SET name = #{name} WHERE id = #{id}
</update><delete>:用于删除记录。此命令可以从表中移除一条或多条符合条件的数据。
<delete id="deleteUser">
DELETE FROM users WHERE id = #{id}
</delete><resultMap>:这是一个非常强大的特性,它允许开发者自定义结果集与 Java 对象之间的映射关系。这对于处理复杂的表结构特别有用。
<resultMap id="userResultMap" type="User">
<id column="id" property="id"/>
<result column="username" property="name"/>
<!-- 更多字段映射... -->
</resultMap>