MyBatis框架入门
MyBatis是一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。MyBatis可以通过简单的XML或注解来配置和映射原始类型、接口和Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录
基本使用
向SQL语句传参
mybatis日志输出配置
mybatis配置文件设计标签和顶层结构如下:
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
我们可以在mybatis的配置文件使用settings标签设置,输出运行过程SQL日志,通过查看日志,我们可以判定#{}和${}的输出效果
#{}形式
Mybatis会将SQL语句中的#{}
转换为问号占位符
${}形式
${}
形式传参,底层Mybatis做的是字符串拼接操作。通常不会采用${}
的方式传值。一个特定的适用场景是:通过Java程序动态生成数据库表,表名部分需要Java程序通过参数传入;而JDBC对于表名部分是不能使用问号占位符的,此时只能使用${}
|
数据输入
Mybatis总体机制概括
单个简单类型参数
Mapper接口中抽象方法的声明
Employee selectEmployee(Integer empId); |
SQL语句
<select id="selectEmployee" resultType="mybatis.entity.Employee"> |
单个简单类型参数,在#{}中可以随意命名,但是没有必要。通常还是使用和接口方法参数同名
实体类类型参数
Mapper接口中抽象方法的声明
int insertEmployee(Employee employee); |
SQL语句
<insert id="insertEmployee"> |
对应关系
Mybatis会根据#{}中传入的数据,加工成getXxx()方法,通过反射在实体类对象中调用这个方法,从而获取到对应的数据。填充到#{}解析后的问号占位符这个位置
零散的简单类型数据
零散的多个简单类型参数,如果没有特殊处理,那么Mybatis无法识别自定义名称,需要在Mapper接口中抽象方法的声明
int updateEmployee(; Integer empId, Double empSalary) |
SQL语句
<update id="updateEmployee"> |
对应关系
Map类型参数
Mapper接口中抽象方法的声明
int updateEmployeeByMap(Map<String, Object> paramMap); |
SQL语句
<update id="updateEmployeeByMap"> |
junit测试
private SqlSession session; |
对应关系:#{}中写Map中的key
使用场景:有很多零散的参数需要传递,但是没有对应的实体类类型可以使用。使用@Param注解一个一个传入又太麻烦了。所以都封装到Map中。
数据输出
数据输出总体上有两种形式:
- 增删改操作返回的受影响行数:直接使用 int 或 long 类型接收即可
- 查询操作的查询结果
我们需要做的是,指定查询的输出数据类型即可
单个简单类型
Mapper接口中的抽象方法
int selectEmpCount(); |
SQL语句
<select id="selectEmpCount" resultType="int"> |
Mybatis 内部给常用的数据类型设定了很多别名。 以 int 类型为例,可以写的名称有:int、integer、Integer、java.lang.Integer、Int、INT、INTEGER 等等。
细节解释:
select标签,通过resultType指定查询返回值类型
resultType = 全限定符 | 别名 | 如果是返回集合类型,写范型类型即可
别名问题:https://mybatis.org/mybatis-3/zh/configuration.html#typeAliases
类型别名可为Java类型设置一个缩写名字。 它仅用于XML配置,意在降低冗余的全限定类名书写
<typeAliases> |
当这样配置时,Blog
可以用在任何使用domain.blog.Blog的地方,也可以指定一个包名,MyBatis会在包名下面搜索需要的Java Bean,比如:
<typeAliases> <package name="domain.blog"/> </typeAliases> |
每一个在包 domain.blog
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author
的别名为 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 |
object[] | Object[] |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
返回实体类对象Mapper
接口的抽象方法
Employee selectEmployee(Integer empId); |
SQL语句
<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 --> |
通过给数据库表字段加别名,让查询结果的每一列都和Java实体类中属性对应起来
增加全局配置自动识别对应关系,Mybatis全局配置文件中,做了下面的配置,select语句中可以不给字段设置别名
<!-- 在全局范围内对Mybatis进行配置 --> |
返回主键值
- 自增长类型主键
Mapper接口中的抽象方法
int insertEmployee(Employee employee); |
SQL语句
<!-- int insertEmployee(Employee employee); --> |
junit测试
|
注意
Mybatis是将自增主键的值设置到实体类对象中,而不是以Mapper接口方法返回值的形式返回。
2. 非自增长类型主键
而对于不支持自增型主键的数据库(例如 Oracle)或者字符串类型主键,则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用!
使用 selectKey
帮助插入UUID作为字符串类型主键示例:
<insert id="insertUser" parameterType="User"> |
上例中定义了一个insertUser
的插入语句来将 User
对象插入到 user
表中。我们使用selectKey
来查询 UUID 并设置到 id
字段中。
通过 keyProperty
属性来指定查询到的 UUID 赋值给对象中的 id
属性,而 resultType
属性指定了 UUID 的类型为 java.lang.String
需要注意的是,我们将 selectKey
放在了插入语句的前面,这是因为 MySQL 在 insert
语句中只支持一个 select
子句,而 selectKey
中查询 UUID 的语句就是一个 select
子句,因此我们需要将其放在前面。
最后,在将User
对象插入到 user
表中时,我们直接使用对象中的 id
属性来插入主键值。
使用这种方式,可以方便地插入 UUID 作为字符串类型主键。
实体类属性和数据库字段对应关系
- 别名对应
将字段的别名设置成和实体类属性一致。
<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 --> |
关于实体类属性的约定:
getXxx()方法、setXxx()方法把方法名中的get或set去掉,首字母小写。
2. 全局配置自动识别驼峰式命名规则
在Mybatis全局配置文件加入如下配置:
<!-- 使用settings对Mybatis全局进行设置 --> |
SQL语句中可以不使用别名
<!-- Employee selectEmployee(Integer empId); --> |
- 使用resultMap
使用resultMap标签定义对应关系,再在后面的SQL语句中引用这个对应关系
<!-- 专门声明一个resultMap设定column到property之间的对应关系 --> |
mapperXML标签总结
MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):
insert
– 映射插入语句。update
– 映射更新语句。delete
– 映射删除语句。select
– 映射查询语句。
select标签:
MyBatis 在查询和结果映射做了相当多的改进。一个简单查询的 select 元素是非常简单:
<select id="selectPerson" |
这个语句名为 selectPerson,接受一个 int(或 Integer)类型的参数,并返回一个 HashMap 类型的对象,其中的键是列名,值便是结果行中的对应值。
注意参数符号:#{id} ${key}
MyBatis 创建一个预处理语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中,就像这样:
// 近似的 JDBC 代码,非 MyBatis 代码... |
select 元素允许你配置很多属性来配置每条语句的行为细节:
属性 | 描述 |
---|---|
id |
在命名空间中唯一的标识符,可以被用来引用这条语句。 |
resultType |
期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。 |
resultMap |
对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。 |
timeout |
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
statementType |
可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
insert, update 和 delete标签:
数据变更语句 insert,update 和 delete 的实现非常接近:
<insert |
属性 | 描述 |
---|---|
id |
在命名空间中唯一的标识符,可以被用来引用这条语句。 |
timeout |
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
statementType |
可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys |
(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。 |
keyProperty |
(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset )。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
keyColumn |
(仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
MyBatis多表映射
对一映射
- 需求说明
根据ID查询订单,以及订单关联的用户的信息!
2. OrderMapper接口
public interface OrderMapper { |
- OrderMapper.xml配置文件
<!-- 创建resultMap实现“对一”关联关系映射 --> |
对应关系可以参考下图:
4. Mybatis全局注册Mapper文件
<!-- 注册Mapper配置文件:告诉Mybatis我们的Mapper配置文件的位置 --> |
- junit测试程序
|
- 关键词
在“对一”关联关系中,我们的配置比较多,但是关键词就只有:association和javaType
对多映射
- 需求说明
查询客户和客户关联的订单信息!
2. CustomerMapper接口
public interface CustomerMapper { |
- CustomerMapper.xml文件
<!-- 配置resultMap实现从Customer到OrderList的“对多”关联关系 --> |
对应关系可以参考下图:
4. Mybatis全局注册Mapper文件
<!-- 注册Mapper配置文件:告诉Mybatis我们的Mapper配置文件的位置 --> |
- junit测试程序
|
- 关键词
在“对多”关联关系中,同样有很多配置,但是提炼出来最关键的就是:“collection”和“ofType”
多表映射优化
setting属性 | 属性含义 | 可选值 | 默认值 |
---|---|---|---|
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
我们可以将autoMappingBehavior设置为full,进行多表resultMap映射的时候,可以省略符合列和属性命名映射规则(列名=属性名,或者开启驼峰映射也可以自定映射)的result标签!
修改mybati-sconfig.xml:
<!--开启resultMap自动映射 --> |
修改teacherMapper.xml
<resultMap id="teacherMap" type="teacher"> |
多表映射总结
关联关系 | 配置项关键词 | 所在配置文件和具体位置 |
---|---|---|
对一 | association标签/javaType属性/property属性 | Mapper配置文件中的resultMap标签内 |
对多 | collection标签/ofType属性/property属性 | Mapper配置文件中的resultMap标签内 |
MyBatis动态语句
if和where标签
使用动态 SQL 最常见情景是根据条件包含 where / if 子句的一部分。比如:
<!-- List<Employee> selectEmployeeByCondition(Employee employee); --> |
set标签
<!-- void updateEmployeeDynamic(Employee employee) --> |
choose/when/otherwise标签
在多个分支条件中,仅执行一个。
- 从上到下依次执行条件判断
- 遇到的第一个满足条件的分支会被采纳
- 被采纳分支后面的分支都将不被考虑
- 如果所有的when分支都不满足,那么就执行otherwise分支
<!-- List<Employee> selectEmployeeByConditionByChoose(Employee employee) --> |
foreach标签
基本用法
用批量插入举例
<!-- |
批量更新时需要注意
上面批量插入的例子本质上是一条SQL语句,而实现批量更新则需要多条SQL语句拼起来,用分号分开。也就是一次性发送多条SQL语句让数据库执行。此时需要在数据库连接信息的URL地址中设置:
dev.url=jdbc:mysql:///mybatis-example?allowMultiQueries=true |
对应的foreach标签如下:
<!-- int updateEmployeeBatch(@Param("empList") List<Employee> empList) --> |
关于foreach标签的collection属性
如果没有给接口中List类型的参数使用@Param注解指定一个具体的名字,那么在collection属性中默认可以使用collection或list来引用这个list集合。这一点可以通过异常信息看出来:
Parameter 'empList' not found. Available parameters are [arg0, collection, list] |
在实际开发中,为了避免隐晦的表达造成一定的误会,建议使用@Param注解明确声明变量的名称,然后在foreach标签的collection属性中按照@Param注解指定的名称来引用传入的参数。
sql片段
抽取重复的SQL片段
<!-- 使用sql标签抽取重复出现的SQL片段 --> |
引用已抽取的SQL片段
<!-- 使用include标签引用声明的SQL片段 --> |
Mapper批量映射优化
- 需求
Mapper配置文件很多时,在全局配置文件中一个一个注册太麻烦,希望有一个办法能够一劳永逸。
2. 配置方式
Mybatis允许在指定 Mapper 映射文件时,只指定其所在的包:
<mappers> |
此时这个包下的所有 Mapper 配置文件将被自动加载、注册,比较方便。
3. 资源创建要求
- Mapper 接口和 Mapper 配置文件名称一致
- Mapper 接口:EmployeeMapper.java
- Mapper 配置文件:EmployeeMapper.xml
- Mapper 配置文件放在 Mapper 接口所在的包内
- 可以将mapperxml文件放在mapper接口所在的包!
- 可以在sources下创建mapper接口包一致的文件夹结构存放mapper.xml文件
MyBatis总结
核心点 | 掌握目标 |
---|---|
mybatis基础 | 使用流程, 参数输入,#{} ${},参数输出 |
mybatis多表 | 实体类设计,resultMap多表结果映射 |
mybatis动态语句 | Mybatis动态语句概念, where , if , foreach标签 |
mybatis扩展 | Mapper批量处理,分页插件,逆向工程 |