MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):
cache
– 该命名空间的缓存配置。cache-ref
– 引用其它命名空间的缓存配置。resultMap
– 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。sql
– 可被其它语句引用的可重用语句块。insert
– 映射插入语句。update
– 映射更新语句。delete
– 映射删除语句。select
– 映射查询语句。
查看dtd文件也能了解到包含这些标签:
<!ELEMENT mapper (cache-ref | cache | resultMap* | parameterMap* | sql* | insert* | update* | delete* | select* )+>
下面的例子我们对象都使用了别名,在全局配置文件配置就可以了
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 使用驼峰命名法转换字段 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<!-- 扫描的包,自己定义 -->
<package name="com.example.springboot_mybatis.model"></package>
</typeAliases>
</configuration>
输入参数
传递简单类型
//查询用户
User findById(Integer id);
<select id="selectUsers" resultType="User" parameterType="integer">
select id, username, password
from users
where id = #{id}
</select>
传递pojo
使用#{对象属性}的方式,假设包含了一个部门对象属性dept,也可以用#{dept.xxx}来获取包含对象的属性值
//添加用户
int save(User user);
<insert id="insertUser" parameterType="User">
insert into users (id, username, password)
values (#{id}, #{username}, #{password})
</insert>
HashMap参数
HashMap
作为参数的时候,我们显示执行javaType
来确保正确的类型处理器(TypeHandler
)被使用
提示 JDBC 要求,如果一个列允许使用 null 值,并且会使用值为 null 的参数,就必须要指定 JDBC 类型(jdbcType)。
要更进一步地自定义类型处理方式,可以指定一个特殊的类型处理器类(或别名)
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
数值类型:还可以设置 numericScale
指定小数点后保留的位数。
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}
最后,mode 属性允许你指定 IN
,OUT
或 INOUT
参数。如果参数的 mode
为 OUT
或 INOUT
,将会修改参数对象的属性值,以便作为输出参数返回。 如果 mode
为 OUT
(或 INOUT
),而且 jdbcType
为 CURSOR
(也就是 Oracle 的 REFCURSOR),你必须指定一个 resultMap
引用来将结果集 ResultMap
映射到参数的类型上。要注意这里的 javaType
属性是可选的,如果留空并且 jdbcType 是 CURSOR
,它会被自动地被设为 ResultMap
。
#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}
MyBatis 也支持很多高级的数据类型,比如结构体(structs),但是当使用 out 参数时,你必须显式设置类型的名称。比如(再次提示,在实际中要像这样不能换行):
#{middleInitial, mode=OUT, jdbcType=STRUCT, jdbcTypeName=MY_TYPE, resultMap=departmentResultMap}
尽管上面这些选项很强大,但大多时候,你只须简单指定属性名,顶多要为可能为空的列指定 jdbcType
,其他的事情交给 MyBatis 自己去推断就行了。
简单使用: #{key}
List<User> findListByHashMap(HashMap<String, Object> map)
<select id="findListByHashMap" resultType="com.pojo.User">
select * from user where username = #{username} and status = #{status}
</select>
集合和数组
一般我们使用<foreach>
标签去遍历
/**
* 批量插入
* @param userList
* @return
*/
int batchSave(@Param("userList") List userList);
<insert id="batchSave" parameterType="list">
INSERT INTO t_user(username,email,addr) VALUES
<foreach item="item" collection="userList" separator=",">
(#{item.username}, #{item.email}, #{item.addr})
</foreach>
</insert>
如果我们想取集合的第一条的话:比如我们取user集合的第一条的user对象的id去查询:#{list[0].id}
/**
* 查询用户
* @param users
* @return
*/
User findById(List<User> users);
<select id="findById" parameterType="integer" resultType="com.pojo.User" resultMap="userMap">
select id, username, email, addr from t_user where id = #{list[0].id} limit 1
</select>
类似这种形式的话是有一定规律的:
-
Collection - collection[索引值]
-
LIst - list[索引值]
-
数组 - array[索引值]
-
…
public static Object wrapToMapIfCollection(Object object, String actualParamName) {
if (object instanceof Collection) {
ParamMap<Object> map = new ParamMap<>();
map.put("collection", object);
if (object instanceof List) {
map.put("list", object);
}
Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
return map;
} else if (object != null && object.getClass().isArray()) {
ParamMap<Object> map = new ParamMap<>();
map.put("array", object);
Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
return map;
}
return object;
}
输出参数
输出简单类型
我们获取用户表的总记录数
UserMapper
:
/**
* 获取总记录
* @return 总记录
*/
int getTotal();
UserMapper.xml
:
<select id="getTotal" resultType="java.lang.Integer">
select count(1) from t_user
</select>
输出pojo
UserMapper
:
/**
* 获取总记录
* @return 总记录
*/
int getTotal();
UserMapper.xml
:
<select id="getTotal" resultType="java.lang.Integer">
select count(1) from t_user
</select>
输出pojo列表
比如我们根据用户名去模糊查询用户
UserMapper
:
/**
* 根据用户名模糊查询
* @param username 用户名
* @return list
*/
List<User> getListByName(@Param("username") String username);
UserMapper.xml
:
<select id="getListByName" resultType="User">
select * from t_user t where instr(t.username, #{username}) > 0
</select>
输出Map集合
还是上面的例子,但是我们只需要查询id和用户名这2个字段
UserMapper
:
/**
* 根据用户名模糊查询
* @param username 用户名
* @return list
*/
List<Map<String, Object>> getListByName(@Param("username") String username);
UserMapper.xml
:
<select id="getListByName" resultType="map">
select t.id, t.username from t_user t where instr(t.username, #{username}) > 0
</select>
测试:
@Test
public void getList(){
List<Map<String, Object>> users = userMapper.getListByName("x");
users.stream().forEach(System.out::println);
}
//map的key对应数据库中的列名
{id=15, username=xx}
{id=16, username=xx}
{id=17, username=xx}
{id=18, username=xx}
{id=19, username=xx}
结果映射
resultMap初识
之前的查询语句大部分数据库列名都和实体保持一致, 但是如果不一致呢,经常我实体属性和数据表属性都是按照驼峰命名的,比如实体类叫,叫creTime,数据库命名cre_time那么就需要我们手动的建立一一映射的关系了。
方法有两种:
一种是:使用as
起别名
<select id="getList" resultType="User">
select
user_id as id,
user_name as userName
from t_user
where user_id = #{id}
</select>
第二种是: 使用resultMap
: select标签通过resultMap
属性指定我们要使用的映射map
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="userName" column="user_name"/>
</resultMap>
<select id="findById" parameterType="integer" resultMap="userResultMap">
select * from t_user where id = #{id}
</select>
- id用来唯一标识这个resultMap, type指的是映射的实体类
- id标签一般用来代表主键映射,result标签其他列映射
- property: 对象的属性名, column:数据库列名