一、前言
1.1 数据库与表
create database if not exists library;
use library;
drop table if exists book;
create table book(
id int primary key auto_increment, -- 书籍Id
bookName varchar(100) not null, -- 书名
content varchar(1024) not null, -- 书籍内容
authorId int not null, -- 作者Id
`state` int default 1 -- 借出状态,默认为1,未借出
);
drop table if exists author;
create table author(
id int primary key auto_increment, -- 作者Id
authorName varchar(100) not null, -- 作者名字
age int, -- 作者年龄
nationality varchar(250) -- 作者国籍
);
1.2 实体类
//书籍类
@Data
public class Book {
private Integer id;
private String bookName;
private String content;
private Integer authorId;
private Integer state;
private Author author;
}
//作者类
@Data
public class Author {
private Integer id;
private String authorName;
private Integer age;
private String nationality;
private List<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
@Mapper
public interface BookMapper {
public Book getBookById(Integer id);
}
XML
BookMapper.xml
<?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.example.demo.Mapper.BookMapper">
<resultMap id="BaseMap" type="com.example.demo.model.Book">
<id property="id" column="id"></id>
<result property="bookName" column="bookName"></result>
<result property="content" column="content"></result>
<result property="authorId" column="authorId"></result>
<result property="state" column="state"></result>
<association property="author"
resultMap="com.example.demo.Mapper.AuthorMapper.BaseMap"
columnPrefix="a_">
</association>
</resultMap>
<select id="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"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.Mapper.AuthorMapper">
<resultMap id="BaseMap" type="com.example.demo.model.Author">
<id property="id" column="id"></id>
<result property="authorName" column="authorName"></result>
<result property="age" column="age"></result>
<result property="nationality" column="nationality"></result>
</resultMap>
</mapper>
单元测试代码
@SpringBootTest
class BookMapperTest {
@Autowired
private BookMapper mapper;
@Test
void getBookById() {
System.out.println(mapper.getBookById(1));
}
}
结果显示
2.2 一对多
一对多和一对一的写法大体上是一样的额,不同的是一对一使用的是 association 标签,一对多使用的是 collection
标签
interface
public List<Author> getAuthor(Integer id);
XML
<?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.example.demo.Mapper.AuthorMapper">
<resultMap id="BaseMap" type="com.example.demo.model.Author">
<id property="id" column="id"></id>
<result property="authorName" column="authorName"></result>
<result property="age" column="age"></result>
<result property="nationality" column="nationality"></result>
<collection property="books"
resultMap="com.example.demo.Mapper.BookMapper.BaseMap"
columnPrefix="b_">
</collection>
</resultMap>
<select id="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 在上面一对一的代码中已经写了
单元测试代码
@Test
void getAuthor() {
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
public int setAuthor3(Author author);
XML
<insert id="setAuthor3" >
insert into author(authorName
<if test="age != null">
,age
</if>
<if test="nationality != null">
,nationality
</if>
) values(#{authorName}
<if test="age != null">
,#{age}
</if>
<if test="nationality != null">
,#{nationality}
</if>
)
</insert>
解释:
- 通过 if 标签中的
test
属性中的内容(对象中的属性)是否为空来决定是否将其拼接到 SQL 语句中 - 需要注意拼接时的逗号以及括号,保证在任何情况下都能组装成正确的 SQL 语句
- 由于需要判空,因此
创建实体类的时候最好使用包装类
,比如使用 Integer 类型而不是 int 类型。因为 int 类型默认值为 0 ,不会为 null,而 0 和 null 还是有很大区别的,使用 int 类型会存在报错的风险
测试代码展示
@Test
void setAuthor3() {
Author author = new Author();
author.setAuthorName("钱七");
author.setNationality("马来西亚");
System.out.println("更新的数据条数:"+mapper.setAuthor3(author));
}
结果显示
3.2 < trim > 标签
该标签是配合 if 标签进行使用,试想极端情况,所有的参数都是非必传的,那么在不知道哪个参数是第一个,哪个参数是最后一个的情况下,一定会有多出来的逗号,trim 标签就可以解决这样的问题
trim 标签的属性
prefix
:表示整个语句块以 prefix 的值作为前缀suffix
:表示整个语句块以 suffix 的值作为后缀prefixOverrides
:表示整个语句块要去除的前缀suffixOverrides
:表示整个语句块要去除的后缀
interface
public int setAuthor4(Author author);
XML
<insert id="setAuthor4">
insert into author
<trim prefix="(" suffix=")" prefixOverrides=",">
<if test="authorName != null">
,authorName
</if>
<if test="age != null">
,age
</if>
<if test="nationality != null">
,nationality
</if>
</trim>
values
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="authorName != null">
#{authorName},
</if>
<if test="age != null">
#{age},
</if>
<if test="nationality != null">
#{nationality},
</if>
</trim>
</insert>
注意:如果 trim 标签中的内容没有一条条件成立 ,就不会执行 trim 语句的内容了,包括其属性也不会生效
。因此如果有必传参数,就往 trim 标签中添加必传参数;如果没有任何必传参数,前缀后缀相关属性就在 trim 中设置
单元测试代码
@Test
void setAuthor4() {
Author author = new Author();
author.setAuthorName("朱八");
author.setAge(66);
System.out.println("更新的数据条数:"+mapper.setAuthor4(author));
}
结果显示
3.3 < where > 标签
传入一个对象,根据属性进行 where 条件查询,只要对象中的属性不为 null 就是查询条件
interface
public List<Book> getBookByIdOrAuthorId(Book book);
XML
<select id="getBookByIdOrAuthorId" resultType="com.example.demo.model.Book">
select * from book
<where>
<if test="id != null">
id=#{id}
</if>
<if test="authorId != null">
and authorId=#{authorId}
</if>
</where>
</select>
解释:
- where 标签
自带 where 关键字
,并且会自动去除多余的 and
- 可以使用 < trim prefix=“where” prefixOverrides=“and” > 替换
单元测试代码
@Test
void getBookByIdOrAuthorId() {
Book book = new Book();
book.setAuthorId(1);
List<Book> list = mapper.getBookByIdOrAuthorId(book);
list.stream().forEach(n-> System.out.println(n));
}
结果显示
3.4 < set > 标签
传入一个对象,根据属性进行更新用户的数据,比如根据传入的对象的 id ,修改其他不为 null 的属性
interface
public int updateBook(Book book);
XML
<update id="updateBook">
update book
<set>
<if test="bookName != null">
bookName=#{bookName},
</if>
<if test="content != null">
content=#{content},
</if>
<if test="authorId != null">
authorId=#{authorId}
</if>
</set>
where id=#{id}
</update>
解释:
- set 标签
自带 set 关键字
,会自动去除多余的逗号
- 可以使用 < trim prefix=“set” suffixOverrides=“,”> 替换
单元测试代码
@Test
void updateBook() {
Book book = new Book();
book.setId(1);
book.setContent("更新内容~");
System.out.println("更新数据的条数:"+mapper.updateBook(book));
}
结果显示
3.5 < foreach > 标签
需要对一个集合遍历时使用该标签
foreach 标签属性
collection
:绑定方法参数中的集合(List、Set、Map、数组…)item
:用于指定遍历时的每一个对象open
:整个语句块开头的字符串close
:整个语句块结束的字符串separator
:遍历元素之间间隔的字符串
interface
public int deleteByIdList(List<Integer> list);
XML
<delete id="deleteByIdList">
delete from book where id in
<foreach collection="list" item="bookId" open="(" close=")" separator=",">
#{bookId}
</foreach>
</delete>
单元测试代码
@Test
void deleteByIdList() {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
System.out.println("更新数据的条数:"+mapper.deleteByIdList(list));
}
结果显示
完~~~
转载:https://blog.csdn.net/weixin_46103589/article/details/125499725