文章目录
一、前言
快速掌握 MyBatis 框架(一)
1.1 数据库与表
createdatabaseifnotexists library;use library;droptableifexists book;createtable book(
id intprimarykeyauto_increment,-- 书籍Id
bookName varchar(100)notnull,-- 书名
content varchar(1024)notnull,-- 书籍内容
authorId intnotnull,-- 作者Id`state`intdefault1-- 借出状态,默认为1,未借出);droptableifexists author;createtable author(
id intprimarykeyauto_increment,-- 作者Id
authorName varchar(100)notnull,-- 作者名字
age int,-- 作者年龄
nationality varchar(250)-- 作者国籍);
1.2 实体类
//书籍类@DatapublicclassBook{privateInteger id;privateString bookName;privateString content;privateInteger authorId;privateInteger state;privateAuthor author;}
//作者类@DatapublicclassAuthor{privateInteger id;privateString authorName;privateInteger age;privateString nationality;privateList<Book> books;}
1.3 MyBatisX 插件
在写 MyBatis 代码的时候有一个非常好用的插件——
MyBatisX
。
选择 File->Settings ,然后进行如下操作进行插件的安装
该插件可以实现接口代码和对应的 XML 文件中的代码的跳转
而且写好一个 接口方法后可以在对应的 XML 文件中自动生成代码(出现红下划线,Alt+Enter,选择第一个选项),当然,这样的方式生成的标签不一定是我们想要的
1.4 SQL 日志查看配置
为了查看写好的 SQL ,可以在配置文件中进行配置,如此,在控制台就可以查看 SQL 日志
mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
二、多表查询
在前面一篇文章中有讲数据库的增加(insert)、删除(delete)、修改(update)、查询(select)操作
增删改
这三个操作
返回的值都是影响的行数
,所以在写 SQL 的 XML 时并不需要指定返回类型,但是如果是
查询操作
就需要
通过 resultType 来设置返回类型
,就算是 String 类型,返回值也要设置为 resultType =“java.lang.String”
但是如果进行
多表查询
,实体类中就会有一个属性为另一个实体类,比如上面的实体类定义中,一本书对应一个作者,想要将作者的完整信息都放在书籍类中。一个作者可以写很多本书,想要将这个作者写的所有书的信息都放在一个 List 中。这样的情况,简简单单通过 resultType 已经没有办法实现,如果只是使用 resultType ,会发现对应的类的属性值为 null(变量 author 和 变量 books 的值为 null)
此时就需要使用
resultMap
,返回一个字典映射
2.1 一对一
一本书对应一个作者的情况
interface
@MapperpublicinterfaceBookMapper{publicBookgetBookById(Integer id);}
XML
BookMapper.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.example.demo.Mapper.BookMapper"><resultMapid="BaseMap"type="com.example.demo.model.Book"><idproperty="id"column="id"></id><resultproperty="bookName"column="bookName"></result><resultproperty="content"column="content"></result><resultproperty="authorId"column="authorId"></result><resultproperty="state"column="state"></result><associationproperty="author"resultMap="com.example.demo.Mapper.AuthorMapper.BaseMap"columnPrefix="a_"></association></resultMap><selectid="getBookById"resultMap="BaseMap">
select b.*,a.id a_id,a.authorName a_authorName,a.age a_age,a.nationality a_nationality
from book b left join author a
on b.authorId = a.id where b.id = #{id}
</select></mapper>
解释:
- select 标签中的 resultMap 的值 “BaseMap” 就是一个标识和上面 resultMap 标签中的 id 属性值 “BaseMap” 是对应的,起什么名字都行,
- resultMap 标签中的
type
属性值为映射的实体类的包名加类名
id
标签指的是主键
,result
标签指的是普通列
- property 属性指的是
程序中的属性名
,column 属性指的是数据库中的字段名
。因此,即使程序中的属性名和数据库中的字段名不一致也不要紧,通过这样的映射就不会出错 - 由于是一对一的多表查询,使用的是
association
标签。 -property
属性指的是 Book 类中关联的 Author 类的变量,即 author;-resultMap
属性指的是指定关联的结果集映射,将基于该映射配置来组织用户数据,这里关联的就是 AuthorMapper 里的 BaseMap(就是下面的代码);-columnPrefix
属性指的是给关联的数据库中的 column 添加一个前缀(如果不添加前缀,当 author 表和 book 表中同时有 id 字段,查询结果时一定会产生覆盖,使得两个 id 的值一样);
AuthorMapper.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.example.demo.Mapper.AuthorMapper"><resultMapid="BaseMap"type="com.example.demo.model.Author"><idproperty="id"column="id"></id><resultproperty="authorName"column="authorName"></result><resultproperty="age"column="age"></result><resultproperty="nationality"column="nationality"></result></resultMap></mapper>
单元测试代码
@SpringBootTestclassBookMapperTest{@AutowiredprivateBookMapper mapper;@TestvoidgetBookById(){System.out.println(mapper.getBookById(1));}}
结果显示
2.2 一对多
一对多和一对一的写法大体上是一样的额,不同的是一对一使用的是 association 标签,一对多使用的是
collection
标签
interface
publicList<Author>getAuthor(Integer id);
XML
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.example.demo.Mapper.AuthorMapper"><resultMapid="BaseMap"type="com.example.demo.model.Author"><idproperty="id"column="id"></id><resultproperty="authorName"column="authorName"></result><resultproperty="age"column="age"></result><resultproperty="nationality"column="nationality"></result><collectionproperty="books"resultMap="com.example.demo.Mapper.BookMapper.BaseMap"columnPrefix="b_"></collection></resultMap><selectid="getAuthor"resultMap="BaseMap">
select a.*,b.id b_id,b.bookName b_bookName,b.content b_content
from author a left join book b
on a.id = b.authorId where a.id = #{id}
</select></mapper>
关联的BookMapper 中的 BaseMap 在上面一对一的代码中已经写了
单元测试代码
@TestvoidgetAuthor(){List<Author> list = mapper.getAuthor(1);
list.stream().forEach(n->System.out.println(n));}
结果展示
三、动态 SQL 使用
为了应对各种各样的需求,就需要动态 SQL 完成不同条件下不同的 SQL 拼接
3.1 < if > 标签
在完善信息的时候,有些信息是必须要填的,有些是非必填的。就像 author 表中作者的信息一样,只有 authorName 是一定要填写的,age 和 nationality 是非必填的,那么在插入数据的时候,就需要应对各种信息插入情况,就需要用 < if > 标签来判断传来的值是否为 null,如果是就不将内容拼接到 SQL 中
interface
publicintsetAuthor3(Author author);
XML
<insertid="setAuthor3">
insert into author(authorName
<iftest="age != null">
,age
</if><iftest="nationality != null">
,nationality
</if>
) values(#{authorName}
<iftest="age != null">
,#{age}
</if><iftest="nationality != null">
,#{nationality}
</if>
)
</insert>
解释:
- 通过 if 标签中的
test
属性中的内容(对象中的属性)是否为空来决定是否将其拼接到 SQL 语句中 - 需要注意拼接时的逗号以及括号,保证在任何情况下都能组装成正确的 SQL 语句
- 由于需要判空,因此
创建实体类的时候最好使用包装类
,比如使用 Integer 类型而不是 int 类型。因为 int 类型默认值为 0 ,不会为 null,而 0 和 null 还是有很大区别的,使用 int 类型会存在报错的风险
测试代码展示
@TestvoidsetAuthor3(){Author author =newAuthor();
author.setAuthorName("钱七");
author.setNationality("马来西亚");System.out.println("更新的数据条数:"+mapper.setAuthor3(author));}
结果显示
3.2 < trim > 标签
该标签是配合 if 标签进行使用,试想极端情况,所有的参数都是非必传的,那么在不知道哪个参数是第一个,哪个参数是最后一个的情况下,一定会有多出来的逗号,trim 标签就可以解决这样的问题
trim 标签的属性
prefix
:表示整个语句块以 prefix 的值作为前缀suffix
:表示整个语句块以 suffix 的值作为后缀prefixOverrides
:表示整个语句块要去除的前缀suffixOverrides
:表示整个语句块要去除的后缀
interface
publicintsetAuthor4(Author author);
XML
<insertid="setAuthor4">
insert into author
<trimprefix="("suffix=")"prefixOverrides=","><iftest="authorName != null">
,authorName
</if><iftest="age != null">
,age
</if><iftest="nationality != null">
,nationality
</if></trim>
values
<trimprefix="("suffix=")"suffixOverrides=","><iftest="authorName != null">
#{authorName},
</if><iftest="age != null">
#{age},
</if><iftest="nationality != null">
#{nationality},
</if></trim></insert>
注意:如果
trim 标签中的内容没有一条条件成立 ,就不会执行 trim 语句的内容了,包括其属性也不会生效
。因此如果有必传参数,就往 trim 标签中添加必传参数;如果没有任何必传参数,前缀后缀相关属性就在 trim 中设置
单元测试代码
@TestvoidsetAuthor4(){Author author =newAuthor();
author.setAuthorName("朱八");
author.setAge(66);System.out.println("更新的数据条数:"+mapper.setAuthor4(author));}
结果显示
3.3 < where > 标签
传入一个对象,根据属性进行 where 条件查询,只要对象中的属性不为 null 就是查询条件
interface
publicList<Book>getBookByIdOrAuthorId(Book book);
XML
<selectid="getBookByIdOrAuthorId"resultType="com.example.demo.model.Book">
select * from book
<where><iftest="id != null">
id=#{id}
</if><iftest="authorId != null">
and authorId=#{authorId}
</if></where></select>
解释:
- where 标签
自带 where 关键字
,并且会自动去除多余的 and
- 可以使用 < trim prefix=“where” prefixOverrides=“and” > 替换
单元测试代码
@TestvoidgetBookByIdOrAuthorId(){Book book =newBook();
book.setAuthorId(1);List<Book> list = mapper.getBookByIdOrAuthorId(book);
list.stream().forEach(n->System.out.println(n));}
结果显示
3.4 < set > 标签
传入一个对象,根据属性进行更新用户的数据,比如根据传入的对象的 id ,修改其他不为 null 的属性
interface
publicintupdateBook(Book book);
XML
<updateid="updateBook">
update book
<set><iftest="bookName != null">
bookName=#{bookName},
</if><iftest="content != null">
content=#{content},
</if><iftest="authorId != null">
authorId=#{authorId}
</if></set>
where id=#{id}
</update>
解释:
- set 标签
自带 set 关键字
,会自动去除多余的逗号
- 可以使用 < trim prefix=“set” suffixOverrides=“,”> 替换
单元测试代码
@TestvoidupdateBook(){Book book =newBook();
book.setId(1);
book.setContent("更新内容~");System.out.println("更新数据的条数:"+mapper.updateBook(book));}
结果显示
3.5 < foreach > 标签
需要对一个集合遍历时使用该标签
foreach 标签属性
collection
:绑定方法参数中的集合(List、Set、Map、数组…)item
:用于指定遍历时的每一个对象open
:整个语句块开头的字符串close
:整个语句块结束的字符串separator
:遍历元素之间间隔的字符串
interface
publicintdeleteByIdList(List<Integer> list);
XML
<deleteid="deleteByIdList">
delete from book where id in
<foreachcollection="list"item="bookId"open="("close=")"separator=",">
#{bookId}
</foreach></delete>
单元测试代码
@TestvoiddeleteByIdList(){List<Integer> list =newArrayList<>();
list.add(1);
list.add(3);System.out.println("更新数据的条数:"+mapper.deleteByIdList(list));}
结果显示
完~~~
版权归原作者 富春山居_ZYY 所有, 如有侵权,请联系我们删除。