1.9 复杂查询环境(一对多,多对一)
1.9.1 多对一
创建学生实体类和老师实体类
//这里使用了lombok注解,减少了代码量
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
private String classname;
private Teacher teacher;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher {
private int id;
private String name;
}
关于lombok的简单介绍:
Lombok插件
1. 概念
一款java插件,能帮助开发人员省略冗余的实体类中的构造方法,set get方法,重写方法等,但不支持构造方法的重载,可以手动添加,但一般不介意使用此插件
2. 插件使用
先在设置中下载lombok插件,再在pom.xml文件中导入lombok的依赖
3. 相关注解类型
@Data //无参构造方法,get和set方法,equals方法,重写方法,hashCode方法
@AllArgsConstructor //全参构造方法
@NoArgsConstructor //无参构造方法
回到测试程序:
多对一查询一般有两种方式:
第一种方式:子查询
<!--查询学生信息-->
<select id="findAllStudent" resultMap="stuWithTea">
select * from student;
</select>
<!--进行结果集映射-->
<resultMap id="stuWithTea" type="student">
<association property="teacher" column="tid" javaType="Teacher" select="findTeacher"/>
</resultMap>
<!--查询老师信息-->
<select id="findTeacher" resultType="teacher">
select * from teacher where id=#{tid};
</select>
第二种方式:联表查询
<!--联表查询的sql语句-->
<select id="findAllStudent" resultMap="stuWithTea">
select s.id sid,s.name sname,s.classname sclsname,t.name tname,t.id tid
from student s,teacher t where s.tid=t.id;
</select>
<!--进行结果集的一一映射,字段名对应属性名-->
<resultMap id="stuWithTea" type="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="classname" column="sclsname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
<result property="id" column="tid"/>
</association>
</resultMap>
1.9.2 一对多,(和多对一类似)
Student和Teacher实体类中添加属性
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
private String classname;
private Teacher teacher;
private int tid;
//重载toString方法
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", classname='" + classname +
'}';
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher {
private int id;
private String name;
private List<Student> list;
}
<!--方式一:子查询-->
<select id="findTeacherById" resultMap="teaWithStu">
select * from teacher where id=#{id};
</select>
<resultMap id="teaWithStu" type="teacher">
<collection property="list" javaType="ArrayList" ofType="Student" column="id" select="findStu"/>
</resultMap>
<select id="findStu" resultType="student">
select * from student where tid=#{id};
</select>
<!--联表查询-->
<select id="findTeacherById" resultMap="teaWithStu" parameterType="int">
select t.id tid,t.name tname,s.id sid,s.name sname,s.classname sclsname
from teacher t,student s where s.tid=t.id and t.id=#{id};
</select>
<resultMap id="teaWithStu" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="list" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="classname" column="sclsname"/>
</collection>
</resultMap>
1.9.3 联表查询和子查询的比较
联表查询的sql语句较难写,但结果集的映射较容易,而子查询与联表查询则相反,实际开发中可根据自己的理解选择查询方式
1.9.4 几个特殊词
-
关联:assocation【多对一】
-
集合:collection 【一对多】
-
JavaType:用来指定实体类中属性的类型
-
ofType:用来指定映射到集合中的实体类类型(泛型)
2.0 动态SQL
根据不同的查询条件,生成不同的sql语句
通常创建一个新的实体类后在主配置文件中进行类型别名配置,防止报错且难以找出错误
2.0.1 IF语句
<select id="findBlogWith" parameterType="map" resultType="blog">
select * from blog
<where>
<if test="title != null">
title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</where>
</select>
/**
* 测试不同条件下得到的查询结果
*/
@Test
public void findBlogWith(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("title","java学习");
map.put("author","战狼");
List<Blog> blogs = mapper.findBlogWith(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
2.0.2 常用标签
-
where
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除
-
set
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号
<update id="updateBlogById" parameterType="blog">
update blog
<set>
<if test="title != null">
title=#{title},
</if>
<if test="author != null">
author=#{author},
</if>
<if test="views != null">
views=#{views}
</if>
</set>
</update>
-
choose(when,otherwise)
有时不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句
<select id="findBlogWith" parameterType="map" resultType="blog">
select * from blog
<!--where标签-->
<where>
<choose>
<when test="title != null">
title=#{title}
</when>
<when test="author != null">
and author=#{author}
</when>
<otherwise>
and ....
</otherwise>
</choose>
</where>
</select>
2.0.3 SQL片段
增加sql代码的复用性,简化代码量,可将重复用到的sql语句抽取出来成为一个sql片段,使用直接调用
例如如下sql语句是经常用到的,可以进行一个抽取,只需要添加一个sql标签即可,id属性值可任选,方便调用
<sql id="title-author">
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</sql>
然后在需要调用的地方添加include标签即可,refid属性值为sql片段的id值
<select id="findBlogWith" parameterType="map" resultType="blog">
select * from blog
<where>
<include refid="title-author"></include>
</where>
</select>
sql片段适用于简单且单表的语句,若语句过于复杂将难以实现复用,且最好不包含where标签
2.0.4 foreach标签
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)
例如如下需求:查询id为1,2,3的博客信息
select * from blog where (id=1 or id=2 or id=3);
在接口中定义方法
/**
*查询指定范围中的博客信息
* @param map
* @return
*/
List<Blog> findBlogForeach(Map map);
在对应的mapper.xml文件中写sql语句,其中open标签是开始遍历时拼接的字符串,而close标签为结束遍历时拼接的字符串,separator标签为遍历对象之间所需要拼接的字符串,collection标签为传入的集合属性名,item标签为每一次遍历生成的对象
<select id="findBlogForeach" parameterType="map" resultType="blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="(" close=")" separator="or">
id=#{id}<!--对应item属性的值-->
</foreach>
</where>;
</select>
进行测试
/**
* 测试查询指定范围的博客信息方法
*/
@Test
public void findBlogForeachTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
map.put("ids",ids);
List<Blog> blogs = mapper.findBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
下一篇介绍mybatis的缓存!