1.1SQL映射文件
SQL映射是MyBatis框架最具特色的部分,功能强大且使用简单。MyBatis框架的主要思想是讲SQL语句从程序代码中分离出来,对JDBC访问数据库的代码进行封装,从程序中消除了所有的SQL参数设置及处理结果集的JDBC代码,从而大幅减少数据访问层的编码量。
SQL映射文件中的几个顶级元素介绍如下:
①mapper:SQL映射文件的根元素。只有一个属性namespace,用于区分不同的mapper,必须全局唯一。
②cache:为给定命名空间配置缓存
③cache-ref:引用其他命名空间中的缓存配置。
④resultMap:用来描述查询结果集中的字段和java实体类属性的对应关系
⑤sql:定义可重用的SQL语句快,可以在其他语句映射中引用,提高编写和维护SQL语句的效率
⑥insert:映射inset语句
⑦update:映射update语句
⑧delete:映射delete语句
⑨select:映射select语句
SQL映射文件的开发需要注意以下规则:
①习惯上,SQL映射文件与该Mapper接口同名(实体类名+Mapper),并放置在同一包路径下。
②以要映射的Mapper接口的完全限定名(即包含包名的完整名称)作为namespace属性的值。
③接口中的方法名与映射文件中SQL语句映射的ID一一对应。MyBatis框架通过namespace+ID确定和接口方法绑定的SQL语句。
④在不同的SQL映射文件中,子元素的ID可以相同。
1.2MyBatis框架的条件查询
1.2.1实现单一条件查询
(1)在接口中添加查询方法:
/**
* 根据用户的真实姓名模糊匹配查询用户,参数realName为需要传递给SQL语句的查询条件
*/
public List<SysUser> getUserByRealName(String realName);
(2)在xml文件中添加SQL语句映射:
<select id="getUsersByRealName" resultType="sysuser" parameterType="string">
select * from t_sys_user where realName like concat('%',#{param},'%')
</select>
(3)在测试类中添加测试方法:
......//省略创建SqlSession的代码
List<SysUser> userList = sqlSession.getMapper(接口类名.class).getUserByRealName("李");
......//省略输出查询结果的代码
1.2.2实现多条件查询
有三种实现方法
1.将查询条件封装成java对象作为入参
2.将查询条件封装成Map对象作为入参
3.使用@Param注解时间多参数入参
在映射的SQL语句中使用默认命名表意不直观,且调整顺序容易出错,所以推荐使用@Param注解为参数命名。例如@Param(“realName”)String realName,相当于改参数的可以命名为realName,便于在映射的SQL中使用。
1.2.3MyBatis框架的结果映射
使用mybatis,有两个属性标签<resultType>
、<resultMap>
可以提供结果映射
虽然resultType
属性在大部分情况下都够用,但是在一些特殊情况下无能为力,比如属性名和列名不一致,为一些连接的复杂语句编写映射代码。
遇到这些情况,我们要使用<resultMap>
标签,一份 <resultMap>
能够代替实现同等功能的数千行代码。
1.2.3.1使用resultMap元素自定义结果映射
MyBatis框架使用resultMap元素来自定义结果映射。
resultMap 元素是 MyBatis 中最重要最强大的元素。
resultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
属性名和列名不一致
这是开发过程中常见的情境,JavaBean 属性使用驼峰命名,数据库列名单词之间加入下划线。
public class User {
private int id;
private String username;
private String hashedPassword;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getHashedPassword() {
return hashedPassword;
}
public void setHashedPassword(String hashedPassword) {
this.hashedPassword = hashedPassword;
}
}
<select id="selectUsers" resultType="User">
select
user_id,user_name,hashed_password
from some_table
where id = #{id}
</select>
为了解决上述问题,我们只需要在 <resultMap>
中做一下简单的配置,然后在引用它的语句中设置 <resultMap>
属性就行了
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>
<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>
注意:这里去掉了<reslutType>
属性,用<resultMap>
代替,二者只能选择其中的一个。
高级结果映射
MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。
我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。
如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,可惜没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。
一对一映射
比如,我们如何映射下面这个语句?
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio,
A.favourite_section as author_favourite_section
from Blog B
left outer join Author A on B.author_id = A.id
where B.id = #{id}
</select>
这是典型的一对一的关联关系情况,我们通过<association>
配置就可以解决这个问题。
<resultMap id="detailedBlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<result property="title" column="blog_title"/>
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
<result property="favouriteSection" column="author_favourite_section"/>
</association>
</resultMap>
接下来,我们一步一步来看这些元素的意义。
reslutMap 的属性
id:当前命名空间中的一个唯一标识,用于标识一个结果映射。
type:类的完全限定名, 或者一个类型别名。
id和result
id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。
这两者之间的唯一不同是,id 元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。
两者的一些属性:
property:映射到列结果的字段或属性。
column:数据库中的列名,或者是列的别名。
javaType:一个 Java 类的全限定名,或一个类型别名。通常不会配置,mybatis 能够根据参数信息自动识别,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
jdbcType:JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。
多对多映射
首先来看对应的 SQL 语句:
<select id="selectBlog" resultMap="blogResult">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
P.id as post_id,
P.subject as post_subject,
P.body as post_body,
from Blog B
left outer join Post P on B.id = P.blog_id
where B.id = #{id}
</select>
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<result property="title" column="blog_title"/>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<result property="body" column="post_body"/>
</collection>
</resultMap>
javaBean 中我们这样表示集合:
private List<Post> posts;
大部分都和我们上面学习过的关联元素非常相似,这里只是新增了一个 ofType
属性。
这个属性非常重要,它用来将 JavaBean(或字段)属性的类型和集合存储的类型区分开来。 所以你可以按照下面这样来阅读映射:
<collection property="posts" javaType="ArrayList" ofType="Post"/>
读作: “posts 是一个存储 Post 的 ArrayList 集合”。
在一般情况下,MyBatis 可以推断 javaType 属性,因此并不需要填写。所以很多时候你可以简略成:
<collection property="posts" ofType="Post"/>
配合少许的实践,你会很快了解全部的用法。