select
本节内容来自于
本文代码
前言
大部分时候我们都是与查询打交道,从各种角度来查询,如何对复杂查询做映射是我们最经常遇到的事情。
使用
简单的查询
还是回到我们Get-start部分的那个查询
首先xml通过namespace绑定到一个接口上面,然后在sql语句的标签(这里是select)的id绑定到对应的接口上面,然后通过resultType属性来指定对应的返回值类型(其实不指定也行,能自动解析出来)
注意这个占位符#{userId}
,这个占位符的名称就是我们在接口方法上用@Param注解的值
note:为了防止有些情况忘记把函数参数名编译进去导致的绑定失败,还是推荐手动标注一下
这个占位符最后会被转换为预编译语句(prepared statement)中的?
。
现在你已经弄明白1+1了,让我们进入微积分的学习
现在让我们进一步看看select标签的其他属性
属性 | 描述 |
---|---|
| 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
| 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
| 用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。 |
| 期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。 |
| 对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。 |
| 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。 |
| 将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。 |
| 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
| 这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。 |
| 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
| FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。 |
| 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。 |
| 这个设置仅针对嵌套结果 select 语句:如果为 true,则假设结果集以正确顺序(排序后)执行映射,当返回新的主结果行时,将不再发生对以前结果行的引用。 这样可以减少内存消耗。默认值: |
| 这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。 |
resultType
期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
这个没什么好讲的,举个例子,比如说我不要映射为java bean而是hashmap呢?就需要这种形式了
resultMap
这个就是本节的重中之重,如果你之前跑过Get-start部分的代码会发现LoginUser的userId字段查出来是空的,因为默认情况下mybatis会假定你的数据库列名和Java Bean字段名是一致的,因此没有映射成功。为了解决这种问题和其他更复杂的结果集映射问题,我们需要它。
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
字段名不同问题
首先先创建resultMap标签
id就是这个映射的唯一识别码就可以了,然后type为要映射的类的全限定名或者别名,autoMapping建议为true这样可以那些我们没有配置的就使用自动配置
读者可以试试改为false的效果
然后指定select标签的resultMap为之前我们给这个映射起的名字即可
一对多问题之类的复杂映射
思考这样一个Java bean
它代表了一个用户与其关联的分数记录集合,这个sql相信大家都会写
我们先一步一来,先写ScoreRecord的映射
再来写这个复合类的映射
然后我们就可以利用这个映射了
有了这个例子 让我们再来看看resultMap的子标签都是做什么的
constructor
- 用于在实例化类时,注入结果到构造方法中,用于指定实例化时使用哪个构造器,默认为空构造器idArg
- ID 参数;标记出作为 ID 的结果可以帮助提高整体性能arg
- 将被注入到构造方法的一个普通结果
result
– 注入到字段或 JavaBean 属性的普通结果id
– 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能association
- 一个复杂类型的关联;许多结果将包装成这种类型嵌套结果映射 – 关联可以是
resultMap
元素,或是对其它结果映射的引用
collection
– 一个复杂类型的集合嵌套结果映射 – 集合可以是
resultMap
元素,或是对其它结果映射的引用
id和result
这些元素是结果映射的基础。id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。
这两者之间的唯一不同是,id 元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。
两个元素都有一些属性:
属性 | 描述 |
---|---|
| 映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。 |
| 数据库中的列名,或者是列的别名。一般情况下,这和传递给 |
| 一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
| JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。 |
| 我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。 |
支持的 JDBC 类型
为了以后可能的使用场景,MyBatis 通过内置的 jdbcType 枚举类型支持下面的 JDBC 类型。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor
之前我们展示了constructor标签,现在让我们单独拿出看看
假设我们有这样一个类的构造器
当你在处理一个带有多个形参的构造方法时,很容易搞乱 arg 元素的顺序。 从 3.4.3 开始,可以在指定参数名称的前提下,以任意顺序编写 arg 元素。 为了通过名称来引用构造方法参数,你可以添加 @Param
注解,或者使用 '-parameters' 编译选项并启用 useActualParamName
选项(默认开启)来编译项目。下面是一个等价的例子,尽管函数签名中第二和第三个形参的顺序与 constructor 元素中参数声明的顺序不匹配。
association
这种属于一对一中常用,比如说我们之前的例子,一个积分记录对应一个用户
对应映射就很简单
automapping
正如你在前面一节看到的,在简单的场景下,MyBatis 可以为你自动映射查询结果。但如果遇到复杂的场景,你需要构建一个结果映射。 但是在本节中,你将看到,你可以混合使用这两种策略。让我们深入了解一下自动映射是怎样工作的。
当自动映射查询结果时,MyBatis 会获取结果中返回的列名并在 Java 类中查找相同名字的属性(忽略大小写)。 这意味着如果发现了 ID 列和 id 属性,MyBatis 会将列 ID 的值赋给 id 属性。
通常数据库列使用大写字母组成的单词命名,单词间用下划线分隔;而 Java 属性一般遵循驼峰命名法约定。为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase
设置为 true。
即在config的文件中添加一个属性
甚至在提供了结果映射后,自动映射也能工作。在这种情况下,对于每一个结果映射,在 ResultSet 出现的列,如果没有设置手动映射,将被自动映射。在自动映射处理完毕后,再处理手动映射。 在下面的例子中,id 和 userName 列将被自动映射,hashed_password 列将根据配置进行映射。
有三种自动映射等级:
NONE
- 禁用自动映射。仅对手动映射的属性进行映射。PARTIAL
- 对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射FULL
- 自动映射所有属性。
其他一些基础配置
以下的所有配置都得写在一个自己定义的配置xml里面,xml开头得带有如下
这里先列举一些配置标签,之后会详细讲解
根标签:
:核心根标签,所有的配置都得在这个标签下面
引入连接配置文件:
: 引入数据库连接配置文件标签
resource:属性,指定配置文件名
调整设置
:可以改变 Mybatis 运行时行为
起别名:
:为全类名起别名的父标签
:为全类名起别名的子标签
type:指定全类名
alias:指定别名
:为指定包下所有类起别名的子标签,别名就是类名,首字母小写
自带别名:
别名 数据类型 string
java.lang.String
long
java.lang.Lang
int
java.lang.Integer
double
java.lang.Double
boolean
java.lang.Boolean
....
......
配置环境,可以配置多个标签
:配置数据库环境标签,default 属性指定哪个 environment
:配置数据库环境子标签,id 属性是唯一标识,与 default 对应
:事务管理标签,type 属性默认 JDBC 事务
:数据源标签
type 属性:POOLED 使用连接池(MyBatis 内置),UNPOOLED 不使用连接池
:数据库连接信息标签。
name 属性取值:driver,url,username,password
value 属性取值:与 name 对应
引入映射配置文件
:引入映射配置文件标签
:引入映射配置文件子标签
resource:属性指定映射配置文件的名称
url:引用网路路径或者磁盘路径下的 sql 映射文件
class:指定映射配置类
:批量注册
类型处理器(typeHandlers)
MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。
这里摘自官网的一些举例:
类型处理器 | Java 类型 | JDBC 类型 |
---|---|---|
|
| 数据库兼容的 |
|
| 数据库兼容的 |
|
| 数据库兼容的 |
|
| 数据库兼容的 |
|
| 数据库兼容的 |
|
| 数据库兼容的 |
|
| 数据库兼容的 |
|
| 数据库兼容的 |
|
|
|
|
| - |
|
|
|
|
|
|
|
|
|
|
| - |
|
| 数据库兼容的字节流类型 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
在mybatis中对于这些基本类型会给我们自动适配对应的处理器,但对于其他一些我们自己定义的类型可能就不好处理了,我们可以自己定义类型处理器。比如我之前遇到的,我需要在数据库里把varchar
类型查询出来转成我的List<Integer>
类型(当然用resultmap的collection也可以)
对于自己定义自己的类型处理器,需要继承mybatis的BaseTypeHandler<T>
里面有如下方法
![image-20221123111952587](C:/Users/ziyi zheng/Desktop/archive/backend_qingyou/mybatis/assets/1.png)
而我们需要实现如下四个方法,这里的方法用于干啥已经在方法名写的很明白了
![image-20221123112418184](C:/Users/ziyi zheng/Desktop/archive/backend_qingyou/mybatis/assets/2.png)
这里借鉴mybatisplus实现的一些架构写一个demo,有兴趣可以去看看mybatisplus里的AbstractJsonTypeHandler
那我们自己定义的TypeHandler得用在哪里呢?来看看下面的简单例子
springboot配置mybatis
上面列出了那么多,我们先看看如何让springboot识别到我们的配置xml吧。
可以在我们的yml配置文件那么写,springboot就能自动识别到了
同时呢,我们也可以直接在yml里写一些配置,聪明的idea也会给我们提示doge,比如我们之前讲到的typeAlias
对于只需要简单的配置建议直接写yml里,但是如果自己有复杂的配置需求还是任然建议写个配置文件的xml。
classpath补充
先看张图吧,这是我将项目打成jar包之后解压的,这里由于篇幅原因,只讲讲里面的classes目录下的东西,也就是我们自己编写的文件,同时为了方便,读者可以看看自己运行之后产生的target目录里的classes目录
![image-20221129104823003](C:/Users/ziyi zheng/Desktop/archive/backend_qingyou/mybatis/assets/3.png)
classpath其实就是我们的类路径目录,在打包的时候,我们的/src目录以及/resources目录的文件都会被打包到classes下面,这里是不是和我们之前自己编写的代码的目录很像呢?。所以classpath就是告诉我要去哪个classes目录去找,找哪里呢?/config/mybatisConfig.xml
开启日志
你可以选择直接用xml配置
对于现在的springboot的自动配置只需要修改自己的配置文件即可
开启了之后即可看到下面输出的日志
结尾
其实还有很多没有介绍到的,这里只是选取了几个常见且易于理解的例子来讲解,真正工作中要有创造力,通过简单的例子来举一反三
总结一下resultmap的核心,首先寻找实体之间的关系(是一对多,还是一对一),然后思考这一组关系的结果集按什么进行区分(主键区分)
Last updated