文章目录
10、多对一处理(day02_01mybatis)
10.1、测试环境搭建
-
创建老师、学生实体类
/** * @author ajacker * 学生实体类 */ public class Student { private int id; private String name; private Teacher teacher; /** * 省略getter setter toString */ }
/** * @author ajacker * 老师实体类 */ public class Teacher { private int id; private String name; /** * 省略getter setter toString */ }
-
创建对应的
IStudentDao.java
、ITeacherdao.java
接口 -
配置核心配置文件:
<?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> <!--属性--> <properties resource="jdbcConfig.properties"/> <!--设置--> <settings> <setting name="logImpl" value="LOG4J"/> </settings> <!--类型命名--> <typeAliases> <typeAlias type="com.ajacker.domain.Student" alias="student"/> <typeAlias type="com.ajacker.domain.Teacher" alias="teacher"/> </typeAliases> <!--配置环境--> <environments default="mysql"> <!--配置mysql环境--> <environment id="mysql"> <!--事务管理器--> <transactionManager type="jdbc"/> <!--数据源--> <dataSource type="pooled"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <!--映射器--> <mappers> <mapper class="com.ajacker.dao.IStudentDao"/> <mapper class="com.ajacker.dao.ITeacherDao"/> </mappers> </configuration>
-
添加
Mapper
文件:IStudentDao.xml
、ITeacherDao.xml
-
创建数据库表和外键
Student表
create table student ( id int auto_increment, name varchar(45) null, tid int not null, primary key (id, tid), constraint fktid foreign key (tid) references teacher (id) ); create index fktid_idx on student (tid);
Teacher表
create table teacher ( id int auto_increment primary key, name varchar(45) null );
10.2、按照嵌套查询处理
我们有以下的查询需求,查询所有学生和他对应的老师实体,这时我们需要处理的字段不是简单的类型,我们需要对它进行新的处理。
-
创建接口方法:
/** * 查询所有的学生和对应的老师 * @return */ List<Student> getStudent();
-
在mapper中添加配置:
<select id="getStudent" resultMap="StudentTeacher"> select * from student </select> <resultMap id="StudentTeacher" type="student"> <result property="id" column="id"/> <result property="name" column="name"/> <!--单独处理复杂属性--> <association property="teacher" column="tid" javaType="teacher" select="getTeacher"> </association> </resultMap> <select id="getTeacher" resultType="teacher"> select * from teacher where id=#{tid} </select>
这时我们相当于以Student
表中查询出来的tid
列去调用getTeacher
,再由getTeacher
返回实体类封装到Student
的teacher
字段,这是一个子查询。
-
我们来编写测试类运行一下:
public class MyBatisTest { private InputStream in; private SqlSession sqlSession; @Before public void init() throws IOException { //1.读取配置文件 in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.创建SqlSessionFactory工厂 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(in); //3.使用工厂生产SqlSession对象 sqlSession = factory.openSession(); } @After public void destroy() throws IOException { //提交事务 sqlSession.commit(); //释放资源 sqlSession.close(); in.close(); } /** * 测试查询所有 */ @Test public void testFindAll() throws Exception { IStudentDao studentDao = sqlSession.getMapper(IStudentDao.class); List<Student> students = studentDao.getStudent(); students.forEach(System.out::println); } }
运行结果为:
Student{id=1, name='小王', teacher=Teacher{id=2, name='王老师'}} Student{id=2, name='小名', teacher=Teacher{id=2, name='王老师'}} Student{id=3, name='小绿', teacher=Teacher{id=3, name='秦老师'}} Student{id=4, name='小蓝', teacher=Teacher{id=3, name='秦老师'}} Student{id=5, name='小紫', teacher=Teacher{id=2, name='王老师'}}
10.3、按照联表查询处理
我们将上个例子的ResultMap
和查询方法进行修改:
<select id="getStudent" resultMap="StudentTeacher">
select s.id sid,s.name sname,t.id tid,t.name tname
from student s,teacher t
where s.tid=t.id
</select>
<resultMap id="StudentTeacher" type="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<!--单独处理复杂属性-->
<association property="teacher" column="tid" javaType="teacher">
<result property="name" column="tname"/>
<result property="id" column="tid"/>
</association>
</resultMap>
可以看到:
- 我们使用了sql的联表查询
- 我们利用
ReusltMap
,将查询返回的列名多级映射到了teacher
的字段
此时我们得到了一样的结果:
Student{id=1, name='小王', teacher=Teacher{id=2, name='王老师'}}
Student{id=2, name='小名', teacher=Teacher{id=2, name='王老师'}}
Student{id=5, name='小紫', teacher=Teacher{id=2, name='王老师'}}
Student{id=3, name='小绿', teacher=Teacher{id=3, name='秦老师'}}
Student{id=4, name='小蓝', teacher=Teacher{id=3, name='秦老师'}}
11、一对多处理(day02_02mybatis)
11.1、测试环境搭建
我们把上个例子中的实体类属性稍作修改,使得一个教师关联了多个学生
/**
* @author ajacker
* 学生实体类
*/
public class Student {
private int id;
private String name;
private int tid;
/**
* 省略getter setter toString
*/
}
/**
* @author ajacker
* 老师实体类
*/
public class Teacher {
private int id;
private String name;
private List<Student> students;
/**
* 省略getter setter toString
*/
}
11.2、按照嵌套查询处理
-
创建接口方法
/** * 获得指定老师和他的学生 * @return */ Teacher getTeacher(int tid);
-
在mapper中添加标签
<select id="getTeacher" resultMap="TeacherStudent"> select * from teacher where id=#{tid} </select> <resultMap id="TeacherStudent" type="teacher"> <result property="id" column="id"/> <result property="name" column="name"/> <collection property="students" ofType="student" column="{tid=id}" select="getStudentsByTeacherId"/> </resultMap> <select id="getStudentsByTeacherId" resultType="student"> select * from student where tid=#{tid} </select>
- 可以看到:
- 对于集合来说,我们需要通过
ofType
属性来指名集合的泛型 column
属性可以同于传递指定列给子查询作为参数
- 对于集合来说,我们需要通过
- 可以看到:
-
运行结果为:
Teacher{id=2, name='王老师', students={ Student{id=1, name='小王', tid=2} Student{id=2, name='小名', tid=2} Student{id=5, name='小紫', tid=2} }
10.3、按照联表查询处理
我们将上个例子的标签进行修改:
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid,s.name sname,t.id tid,t.name tname
from teacher t,student s
where t.id=s.tid and tid=#{tid}
</select>
<resultMap id="TeacherStudent" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
</collection>
</resultMap>
- 通过
collection
标签指定集合类型 column
属性可以同于传递指定列给子查询作为参数- 在
collection
标签内用result
指定子类型的映射关系
12、动态SQL(day02_03mybatis)
动态SQL:根据不同的条件生成不同的SQL语句
动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
12.1、搭建环境
-
创建blog表
create table blog ( id varchar(50) not null comment '博客id', title varchar(100) not null comment '博客标题', author varchar(30) null comment '博客作者', create_time datetime not null comment '发布时间', views int default 0 not null comment '访问量' );
-
编写Blog实体类
public class Blog { private String id; private String title; private String author; private Date createTime; private int views; //省略getter setter toString }
-
创建配置文件,mapper文件等
12.2、if
-
添加条件查询的接口方法
/** * 查询博客 * 不提供条件则查询所有 * 若提供条件则按条件查询 * @param title 可为null * @param author 可为null * @return 查询结果 */ List<Blog> queryBlogIf(@Param("title") String title, @Param("author") String author);
-
在mapper中添加内容
<select id="queryBlogIf" resultType="blog"> select * from blog where true <if test="title != null"> and title = #{title} </if> <if test="author != null"> and author = #{author} </if> </select>
-
编写测试方法
@Test public void testQueryBlog() throws Exception { IBlogDao blogDao = sqlSession.getMapper(IBlogDao.class); List<Blog> blogs = blogDao.queryBlogIf("啦啦啦博客", null); blogs.forEach(System.out::println); }
-
观察结果
-
当传入两个null参数时,可以看到log信息为
[main] DEBUG acker.dao.IBlogDao.queryBlogIf - ==> Preparing: select * from blog where true [main] DEBUG acker.dao.IBlogDao.queryBlogIf - ==> Parameters: [main] DEBUG acker.dao.IBlogDao.queryBlogIf - <== Total: 3
可以看到
mybatis
为我们准备的sql语句为select * from blog where true
-
当传入第一个参数,第二个参数为null时候,sql语句为
select * from blog where true and title = ?
-
当两个参数都传入非空值的时候,sql语句为
select * from blog where true and title = ? and author = ?
-
12.3、choose(when,otherwise)
-
添加接口方法
/** * 查询博客 * 若提供title或author则忽略views条件 * 否则按照views查询 * @param title 可为null * @param author 可为null * @param views 不可为null * @return 查询结果 */ List<Blog> queryBlogChoose(@Param("title") String title, @Param("author") String author,@Param("views") int views);
-
在mapper中添加
<select id="queryBlogChoose" resultType="blog"> select * from blog where true <choose> <when test="title != null"> and title = #{title} </when> <when test="author != null"> and author = #{author} </when> <otherwise> and views = #{views} </otherwise> </choose> </select>
-
编写测试方法
@Test public void testQueryBlog() throws Exception { IBlogDao blogDao = sqlSession.getMapper(IBlogDao.class); List<Blog> blogs = blogDao.queryBlogChoose(null, null,9999); blogs.forEach(System.out::println); }
-
观察结果
-
当只传入views时:
select * from blog where true and views = ?
-
除了views之外再传入title或者author时:
select * from blog where true and author = ? select * from blog where true and title = ?
可以看到两者只要有其一就会忽略
otherwise
标签内的语句 -
除了views之外再传入title和author时:
select * from blog where true and author = ?
可以看到
when
标签只选择一个,如果遇到满足条件的不再往后判断
-
12.4、trim(where, set)
之前我们写条件语句的时候都在最前面加了一个where true
,这是为了保证每一个条件都能被正常拼接,where
和set
标签则为我们解决了这个问题
- where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除。
- set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号,因为用了条件语句之后很可能就会在生成的 SQL 语句的后面留下这些逗号。
例如我们可以将之前的if例子中的mapper内容修改为:
<select id="queryBlogIf" resultType="blog">
select * from blog
<where>
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
同理,可以把choose例子中的mapper修改为:
<select id="queryBlogChoose" resultType="blog">
select * from blog
<where>
<choose>
<when test="title != null">
and title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
一个set的例子
<update id="updateBlog">
update blog
<set>
<if test="title != null">
title=#{title},
</if>
<if test="author != null">
author=#{author}
</if>
</set>
where id = #{id}
</update>
trim的介绍:
-
prefix
:表示在trim标签内sql语句加上前缀xxx -
prefixOverrides
:表示去除最后一个后缀xxx -
suffix
:表示在trim标签内sql语句加上后缀xxx -
suffixOverrides
:表示去除最后一个后缀xxx
所以我们可以知道:
-
where
标签等价于<trim prefix="WHERE" prefixOverrides="AND |OR "> ... </trim>
-
set
标签等价于<trim prefix="SET" suffixOverrides=","> ... </trim>
12.5、sql和include
在之前我们的代码中有许多相同的片段:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.ajacker.dao.IBlogDao">
<select id="queryBlogIf" resultType="blog">
select * from blog
<where>
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
<update id="updateBlog">
update blog
<set>
<if test="title != null">
title=#{title},
</if>
<if test="author != null">
author=#{author}
</if>
</set>
where id = #{id}
</update>
</mapper>
我们可以通过sql
标签包含一个代码片段,之后通过include
标签复用它,就像这样
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.ajacker.dao.IBlogDao">
<sql id="if-title-author">
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
<select id="queryBlogIf" resultType="blog">
select * from blog
<where>
<include refid="if-title-author"/>
</where>
</select>
<update id="updateBlog">
update blog
<set>
<include refid="if-title-author"/>
</set>
where id = #{id}
</update>
</mapper>
12.6、foreach
-
添加接口方法
/** * 查询指定id的所有博客 * @param ids 一个list存要查询的id * @return 查询结果 */ List<Blog> queryBlogForeach(@Param("ids") List<Integer> ids);
-
在mapper中编写sql语句
<select id="queryBlogForeach" resultType="blog"> select * from blog <where> <foreach collection="ids" item="id" open="and (" separator="or" close=")"> id = #{id} </foreach> </where> </select>
-
编写测试方法
@Test public void testQueryBlogForeach() throws Exception { IBlogDao blogDao = sqlSession.getMapper(IBlogDao.class); List<Integer> ids = Arrays.asList(1,3); List<Blog> blogs = blogDao.queryBlogForeach(ids); blogs.forEach(System.out::println); }
-
查看运行结果
[main] DEBUG .dao.IBlogDao.queryBlogForeach - ==> Preparing: select * from blog WHERE ( id = ? or id = ? or id = ? ) [main] DEBUG .dao.IBlogDao.queryBlogForeach - ==> Parameters: 1(Integer), 2(Integer), 3(Integer) [main] DEBUG .dao.IBlogDao.queryBlogForeach - <== Total: 3 Blog{id=1, title='啦啦啦博客', author='ajacker', createTime=Sun Oct 13 18:22:18 CST 2019, views=9999} Blog{id=2, title='Spring笔记', author='ajacker', createTime=Sun Oct 13 18:22:18 CST 2019, views=8888} Blog{id=3, title='mybatis笔记', author='ajacker', createTime=Sun Oct 13 18:22:18 CST 2019, views=9999}
可以看到
mybatis
遍历了list集合并为我们拼接了sql语句:select * from blog WHERE ( id = 1 or id = 2 or id = 3 )