- insert、update、delete元素
- 主键生成方法
- selectKey
- 参数(Parameters)传递
- {参数名};取出参数值
- {属性名}:取出传入pojo的属性值
- {key}:取出map中对应的值
- 参数处理
- { prperty, javaType = int, jdbcType = NUMERIC }
- { height, javaType = double, jdbcType = NUMBERIC, numericScale = 2 }
- select元素
- 自动映射
- reseultMap
- id & result
- association
- association-嵌套结果集
- association-分段查询
- association-分段查询&延迟加载
- Collection-集合类型&嵌套结果集
- Collection-分布查询&延迟加载
- 扩展-多列值封装map传递
- 参数取值(#{} & ${})
- 鉴别器
- 分步查询
映射文件指导着MyBatis如何进行数据库增删改查,有着非常重要的意义
- cache —— 命名空间的二级缓存设置
- cache-ref —— 其他命名空间缓存配置的引用。
- resultMap —— 自定义结果集映射。
- parameterMap —— 已废弃!老式风格的参数映射
- sql —— 抽取可重用的语句块
- insert —— 映射插入语句
- update —— 映射更新语句
- delete —— 映射删除语句
- select —— 映射查询语句
https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#
insert、update、delete元素
参数 | 意义 |
---|---|
id | 命名空间中的唯一标识符 |
parameterType | 要将传入语句的参数的完全限定类名或别名。这个属性是可选的,因为MyBatis可以通过TypeHandler推断出具体传入语句的参数类型,默认值为unset |
flushCache | 将其设置为true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句)。 |
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)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列。也可以是逗号分隔的属性名称列表。 |
databaseId | 如果配置了databaseIdPrvoider,MyBatis会加载所有的不带databaseId或匹配当前databaseId的语句;如果带或者不带的语句都有,则不带的会被忽略。 |
主键生成方法
若数据库支持自动生成主键的字段(比如MySQL和SQL Server),则可以设置
useGeneratedKeys="true"
,然后再把keyProperty
设置到目标属性属性上。<insert id="insertCustomer" databaseId="mysql" userGeneratedKeys="true" keyProperty="id">
insert into coutomers2 (last_name, email, age) values (#{lastName}, #{email}, #{age})
</insert>
而对于不支持自增主键的数据库(例如Oracle),则可以使用selectKey子元素:selectKey元素将会首先运行,id会被设置,然后插入语句会被调用。
<insert id="insertCustomer" databaseId="oracle" parameterType="customer"> <selectKey order="BEFORE" keyProperty="id" resultType="_int"> select crm_seq.nextval from dual </selectKey> insert into customer2 (id, last_name, email, age) values (#{id}, #{lastName}, #{email}, #{age}) </insert>
selectKey
属性 | 意义 |
---|---|
keyProperty | selectKey语句结果应该被设置的目标属性 |
keyColumn | 匹配属性的返回结果集中的列名称 |
resultType | 结果的类型,MyBatis通常可以推算出来,但是为了更加确定写上也不会有什么问题。MyBatis允许任何简单类型用作主键的类型,包括字符串。 |
order | 可以被设置为BEFORE或AFTER。如果设置为BEFORE,name他会首先选择主键,设置keyProperty然后执行插入语句。如果设置为AFTER,name限制性插入语句,然以是selectKey元素 |
statementType | 与前面相同,MyBatis支持STATEMENT,PREPARED和CALLABLE语句的映射类型,分别代表PreparedStatement和CallableStatement类型 |
参数(Parameters)传递
单个参数
可以接受基本类型,对象类型,集合类型的值。这种情况下MyBatis可以直接使用这个参数,不需要经过任何处理。
{参数名};取出参数值
多个参数
任意多个参数,都会被MyBatis重新包装成一个Map传入。Map的key是param1,param2,0,1.。。,值就是参数的值。
多个参数会被封装成一个map,
key:param1...paramN,或者参数的索引也可以
value:传入的参数值
#{}就是从map中获取指定的key的值;
异常:
org.apache.ibatis.binding.BindingException:
Parameter 'id' not found.
Available parameters are [arg1, arg0, param1, param2]
操作:
方法:public Employee getEmpByIdAndLastName(Integer id, String lastName);
取值:#{id}, #{lastName}
命名参数
为参数使用@Param起一个名字,MyBatis就会将这些参数封装进map中,key就是我们自己指定的名字。
明确指定封装参数时,map的key;@Param("id")
多个参数会被封装成一个map,
key:使用@Param注解指定的值
value:参数值
#{指定的key}取出对应的参数值
POJO
当这些参数属于我们业务POJO时,我们直接传递POJO。
{属性名}:取出传入pojo的属性值
Map
我们也可以封装多个参数为map,直接传递
{key}:取出map中对应的值
参数处理
参数也可以制定一个特殊的数据类型
{ prperty, javaType = int, jdbcType = NUMERIC }
{ height, javaType = double, jdbcType = NUMBERIC, numericScale = 2 }
- javaType 通常可以从参数对象中去确定。
- 如果null被当做值来传递,对于所有可能为空的列,jdbcType需要被设置。
- 对于数值类型,还可以设置小数点后保留的位数。
- mode属性允许IN,OUT或INOUT参数。如果参数为OUT或INOUT,参数对象属性的正式值就会被改变,就像在获取输出参数时所期望的那样。
参数位置支持的属性
javaType、jdbcType、mode、numericScale、resultMap、typeHandler、jdbcTypeName
实际上通常被设置的是
可能为空的列名制定jdbcType
#{key}
获取参数的值,预编译到SQL中。安全。
${key}
获取参数的值,并接到SQL中,有SQL注入问题。ORDER BY ${name}
select元素
- Select元素来定义查询操作。
- Id:唯一标识符
- 用来引用这条语句,需要和接口的方法名一致
- parameterType:参数类型。
- 可以不传,MyBatis会根据TypeHandler自动推断
- resultType:返回值类型。
- 别名或者全类名,如果返回的是结合,定义集合中元素的类型。不能喝resultMap同时使用。| | 参数 | 意义 | | —- | —- | | parameterType | 将会传入这条语句的参数类完全限定名或别名。这个属性是可选的,因为MyBatis可以提供过TypeHandler推断出具体传入语句的参数,默认值为unset | | resultType | 从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合,那应该是是集合可以包含的类型,而不能是集合本身。改属性和resultMap,不能同时使用。 | | resultMap | 外部resultMap的命名引用。和resultType属性不能同时使用。 | | flushCache | 将其设置为true,任何时候只要被调用,都会导致本地缓存和二级缓存,默认值:false | | useCache | 将其设置为true,将会导致本条语句的结果被二级缓存,默认值:对select元素为true | | timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回结果的秒数。默认值为unset(依赖驱动) | | fetchSize | 影响驱动程序每次批量返回的结果行数,默认值为unset(依赖驱动)。 | | statementType | STATEMENT,PREPARED或CALLABLE的一个。这会让MyBatis分别使用Statement,PreparedStatement或CallableStatement,默认值:PREPARED, | | resultSetType | FOREWARD_ONLY,SCROLL_SENSITIVE或SCROLL_INSENSITIVE中的一个,默认值为unset(依赖驱动) | | databaseId | 如果配置了databaseIdProvider,MyBatis会加载所有的不带databaseId或匹配当前databaseId的语句;如果带或者不带语句都有,则不带的会被忽略。 | | resultOrdered | 这个设置仅对嵌套结果select语句适用:如果为true,就假设包含了嵌套结果集或是分组,这样当返回一个主结果行,就不会发生有对前面结果集引用的情况。这就是的在获取嵌套的结果集的时候不至于导致内存不够用。默认值为:false | | resultSets | 这个设置仅对结果集的情况适用,他讲列出语句执行后返回的结果集并每隔结果集给一个名称,名称是逗号分割的。 |
自动映射
- 全局setting设置
- autoMappingBehavior默认是PARTIAL,开启自动映射的功能。唯一的要求是列名和javaBean属性名一致。
- 如果autoMappingBehavior设置为null则会取消自动映射。
- 数据库字段命名规范,POJO属性符合驼峰命名法,如A_COLUMN —> AColumn,我们可以开启自动驼峰命名规则映射功能,mapUnderscoreToCamelCase = true
- 自定义resultMap,实现高级结果集映射。
reseultMap
- constructor
- 类在实例化时,用来注入结果到构造方法中
- idArg —— ID 参数:标记结果作为ID可以帮助提高整体效能。
- id —— 一个ID结果;标记结果作为ID可以帮助提高整体效能。
- result —— 注入到字段或JavaBean属性的普通结果
- association —— 一个复杂的类型关联;许多结果将包成这种类型
- 嵌入结果映射——结果映射自身的关联,或者参考一个
- collection —— 复杂类型的集
- 嵌入结果映射 —— 结果映射自身的集,或者参考一个
- discriminator —— 使用结果值来决定使用哪个结果映射。
- case——基于某些值的结果映射
- 嵌入结果映射——这种情形结果也映射它本身,因此可以包含很多相同的元素,或者他可以参照一个外部的结果映射。
- case——基于某些值的结果映射
id & result
id和result映射一个单独列的值到简单数据类型(字符串、整形、双精度浮点数、日期等)的属性或字段。
元素 | 参数 |
---|---|
property | 映射到列结果的字段或属性。例如:”username” 或 “address.street.number”。 |
column | 数据表的列名。通常和resultSet.getString(columnName)的返回值一致。 |
javaType | 一个Java类的完全限定名,或一个类型别名。如果映射到一个JavaBean,MyBatis通常可以断定类型 |
jdbcType | JDBC类型是仅仅需要对插入,更新和删除操作可能为空的列进行处理。 |
typeHandler | 类型处理器,使用这个属性,可以覆盖默认的类型处理器。这个属性值是类的完全限定类名或者是一个类型处理器的实现,或者是类型别名 |
association
- 复杂对象映射
- POJO中的属性可能会是一个对象
我们可以使用联合查询。并以吉利啊属性的方式封装对象。
<resultMap type="com.zh.bean.lock" id="myLock"> <id column="id" property="id"/> <result column="lockName" property="lockName"/> <result column="key_id" property="key.id"/> <result column="keyName" property="key.keyName"/> </resultMap>
使用association标签定义对象的封装规则。
association-嵌套结果集
<resultMap type="com.zh.bean.Lock" id="myLock2">
<id column="id" property="id"/>
<result column="lockName" property="lockName"/>
<association property="key" javaType="com.zh.bean.Key">
<id column="key_id" property="id"/>
<result column="keyName" property="keyName"/>
</association>
</resultMap>
association-分段查询
<resultMap type="com.zh.bean.Lock" id="myLock3">
<id column="id" property="id"/>
<result column="lockName" property="lockName"/>
<association property="key" select="com.zh.dao.KeyMapper.getKeyById" column="key_id"> </association>
</resultMap>
- select:调用目标的方法查询当前属性的值
- column:将指定列的值传入目标方法
association-分段查询&延迟加载
开启延迟加载和属性按需加载
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
旧版本的MyBatis需要额外的支持包。
- asm-3.3.1.jar
- cglib-2.2.2.jar
Collection-集合类型&嵌套结果集
<select id="getDeptById" resultMap="MyDept">
select d.id d_id, d.dept_name d_deptName, e.id e_id, e.last_name e_lastName, e.email email, e.gender e_gender, e.dept_id e_deptId from department d left join employee e on e.'dept_id' = d.'id' where d.'id' = #{id}
</select>
<resultMap type="com.zh.bean.Department" id="MyDept">
<id column="d_id" property="id"/>
<result column="d_department" property="deptName"/>
<collection property="emps" ofType="com.zh.bean.Employee" columnPrefix="e_">
<id column="id" property="id"/>
<result column="lastName" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</collection>
</resultMap>
Collection-分布查询&延迟加载
<resultMap type="com.zh.bean.Department" id="MyDeptStep">
<id cloumn="id" property="id"/>
<result column="dept_name" property="deptName"/>
<collection property="emps" select="com.zh.dao.Employee.getEmpsByDeptId" column="id">
</collection>
</resultMap>
扩展-多列值封装map传递
- 分布查询的时候通过column指定,将对应的列的数据传递过去,我们有时需要传递多列数据。
使用
{key1=column1, key2=column2...}
的形式<resultMap type="com.zh.bean.Department" id="MyDeptStep"> <id column="id" property="id"/> <result column="dept_name" property="deptName"/> <collection property="emps" select="com.zh.dao.EmployeeMapper.getEmpsByDeptId" column="{deptId=id}"> </collection> </resultMap>
association或者collect标签的fetchType=eager/lazy可以覆盖全局的延迟加载策略。指定立即加载(eager)或者延迟加载(lazy)
参数取值(#{} & ${})
#{}:可以获取map中的值或者pojo对象属性的值;
${}:可以获取map中的值或者pojo对象属性的值。
select * from tbl_employee where id=${id} and last_name=#{lastName}
Preparing: select * tbl_employee where id=2 and last_name=?
区别:
#{}:它是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入
${}:取出的值直接拼装在sql语句中;会有安全问题
大多情况下,我们取参数的值都应该取使用#{};
原生jdbc不支持占位符的地方就可以使用${}进行取值
比如分表、排序:按照年份分表拆分
select * from ${year}_salary where xxx;
select * from tbl_employee order by ${f_name} ${order}
#{}:更丰富的用法:
可以规定参数的规则:
javaType、jdbcType、mode(存储过程)、numericScale(数字精度)、resultMap、typeHandler、jdbcTypeName、expression(未来准备支持的)
jdbcType通常需要在某种特定的条件下被设置:
在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错);
JdbcType OTHER:无效的类型;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,oracle不能正确处理。
由于全局配置中:jdbcTypeForNull=OTHER;oracle不支持;两种办法都行
1. #{email,jdbc=NULL};
2. jdbcTypeForNull=NULL
鉴别器
<discriminator javaType=""></discriminator>
鉴别器:myBatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
封装employee:
如果查出的是女生,就把部门信息查询出来,否则不查询
如果是男生,把last_name这一列的值,赋值给email;
<resultMap id="MyEmpDis" type="com.zh.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!--
column:指定判断的列名
javaType:列值对应的java类型
-->
<discriminator javaType="string" column="gender">
<!--女生 resultType:指定封装的结果类型:不能缺少。resultMap和resultType二选其一-->
<case value="0" resultType="com.zh.mybatis.bean.Employee">
<association property="department"
select="com.zh.mybatis.dao.DepartmentMapper.getDeptById"
column="d_id">
</association>
</case>
<!--男生 -->
<case value="1" resultType="com.zh.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="last_name" property="email"/>
<result column="gender" property="gender"/>
</case>
</discriminator>
</resultMap>
分步查询
<!--
使用association进行分步查询
1. 先按照员工id查出员工信息
2. 根据查询员工信息中的d_id值去部门表查出部门信息
3. 部门设置到员工中;
-->
<resultMap id="MyEmpByStep" type="com.zh.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="last_Name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!--
association定义相关对象的封装规则
select :表明当前属性是调用select指定的方法查出的结果
column:指定将哪一列的值传递给这个方法
流程:使用select 指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
-->
<association property="department"
select="com.zh.mybatis.dao.DepartmentMapper.getDeptById"
column="d_id">
</association>
</resultMap>