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) -- 作者国籍
);
//书籍类
@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 books;
}
在写 MyBatis 代码的时候有一个非常好用的插件——MyBatisX
。
选择 File->Settings ,然后进行如下操作进行插件的安装
该插件可以实现接口代码和对应的 XML 文件中的代码的跳转
而且写好一个 接口方法后可以在对应的 XML 文件中自动生成代码(出现红下划线,Alt+Enter,选择第一个选项),当然,这样的方式生成的标签不一定是我们想要的
为了查看写好的 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
,返回一个字典映射
一本书对应一个作者的情况
interface
@Mapper
public interface BookMapper {
public Book getBookById(Integer id);
}
XML
BookMapper.xml
解释:
type
属性值为映射的实体类的包名加类名
id
标签指的是主键
,result
标签指的是普通列
程序中的属性名
,column 属性指的是数据库中的字段名
。因此,即使程序中的属性名和数据库中的字段名不一致也不要紧,通过这样的映射就不会出错association
标签。
property
属性指的是 Book 类中关联的 Author 类的变量,即 author;resultMap
属性指的是指定关联的结果集映射,将基于该映射配置来组织用户数据,这里关联的就是 AuthorMapper 里的 BaseMap(就是下面的代码);columnPrefix
属性指的是给关联的数据库中的 column 添加一个前缀(如果不添加前缀,当 author 表和 book 表中同时有 id 字段,查询结果时一定会产生覆盖,使得两个 id 的值一样);AuthorMapper.xml
单元测试代码
@SpringBootTest
class BookMapperTest {
@Autowired
private BookMapper mapper;
@Test
void getBookById() {
System.out.println(mapper.getBookById(1));
}
}
结果显示
一对多和一对一的写法大体上是一样的额,不同的是一对一使用的是 association 标签,一对多使用的是 collection
标签
interface
public List getAuthor(Integer id);
XML
关联的BookMapper 中的 BaseMap 在上面一对一的代码中已经写了
单元测试代码
@Test
void getAuthor() {
List list = mapper.getAuthor(1);
list.stream().forEach(n-> System.out.println(n));
}
结果展示
为了应对各种各样的需求,就需要动态 SQL 完成不同条件下不同的 SQL 拼接
在完善信息的时候,有些信息是必须要填的,有些是非必填的。就像 author 表中作者的信息一样,只有 authorName 是一定要填写的,age 和 nationality 是非必填的,那么在插入数据的时候,就需要应对各种信息插入情况,就需要用 < if > 标签来判断传来的值是否为 null,如果是就不将内容拼接到 SQL 中
interface
public int setAuthor3(Author author);
XML
insert into author(authorName
,age
,nationality
) values(#{authorName}
,#{age}
,#{nationality}
)
解释:
test
属性中的内容(对象中的属性)是否为空来决定是否将其拼接到 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));
}
结果显示
该标签是配合 if 标签进行使用,试想极端情况,所有的参数都是非必传的,那么在不知道哪个参数是第一个,哪个参数是最后一个的情况下,一定会有多出来的逗号,trim 标签就可以解决这样的问题
trim 标签的属性
prefix
:表示整个语句块以 prefix 的值作为前缀suffix
:表示整个语句块以 suffix 的值作为后缀prefixOverrides
:表示整个语句块要去除的前缀suffixOverrides
:表示整个语句块要去除的后缀interface
public int setAuthor4(Author author);
XML
insert into author
,authorName
,age
,nationality
values
#{authorName},
#{age},
#{nationality},
注意:如果 trim 标签中的内容没有一条条件成立 ,就不会执行 trim 语句的内容了,包括其属性也不会生效
。因此如果有必传参数,就往 trim 标签中添加必传参数;如果没有任何必传参数,前缀后缀相关属性就在 trim 中设置
单元测试代码
@Test
void setAuthor4() {
Author author = new Author();
author.setAuthorName("朱八");
author.setAge(66);
System.out.println("更新的数据条数:"+mapper.setAuthor4(author));
}
结果显示
传入一个对象,根据属性进行 where 条件查询,只要对象中的属性不为 null 就是查询条件
interface
public List getBookByIdOrAuthorId(Book book);
XML
解释:
自带 where 关键字
,并且会自动去除多余的 and
单元测试代码
@Test
void getBookByIdOrAuthorId() {
Book book = new Book();
book.setAuthorId(1);
List list = mapper.getBookByIdOrAuthorId(book);
list.stream().forEach(n-> System.out.println(n));
}
结果显示
传入一个对象,根据属性进行更新用户的数据,比如根据传入的对象的 id ,修改其他不为 null 的属性
interface
public int updateBook(Book book);
XML
update book
bookName=#{bookName},
content=#{content},
authorId=#{authorId}
where id=#{id}
解释:
自带 set 关键字
,会自动去除多余的逗号
单元测试代码
@Test
void updateBook() {
Book book = new Book();
book.setId(1);
book.setContent("更新内容~");
System.out.println("更新数据的条数:"+mapper.updateBook(book));
}
结果显示
需要对一个集合遍历时使用该标签
foreach 标签属性
collection
:绑定方法参数中的集合(List、Set、Map、数组…)item
:用于指定遍历时的每一个对象open
:整个语句块开头的字符串close
:整个语句块结束的字符串separator
:遍历元素之间间隔的字符串interface
public int deleteByIdList(List list);
XML
delete from book where id in
#{bookId}
单元测试代码
@Test
void deleteByIdList() {
List list = new ArrayList<>();
list.add(1);
list.add(3);
System.out.println("更新数据的条数:"+mapper.deleteByIdList(list));
}
结果显示
完~~~
先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦