MyBatis使用教程详解<上>

一. 什么是MyBatis?

  • Mybatis是一个持久层框架,用于简化JDBC的操作
  • MyBatis原本是Apache的一个开源项目ibatis,后来更名为MyBatis

上面我们提到了一个概念----持久层

不知道小伙伴们有没有想到五大注解的关系,类似于下图

MyBatis使用教程详解<上>_第1张图片

其中MyBatis就是Mapper层的框架,是基于JDBC的封装,可以帮助我们更方便的操作数据库.

二. MyBatis入门

MyBatis操作数据库的步骤:

  1. 创建SpringBoot项目,准备数据库,实体类...
  2. 引入MyBatis依赖
  3. 编写SQL语句(注解/XML)

2.1 创建MyBatis项目

创建工程不必多说,但是这次我们要导入MyBatis的依赖

可以在创建项目时直接添加,也可以创建项目后使用Spring Boot Helper插件引入依赖.

下面演示一下创建项目后的引入依赖.

点开pom.xml文件->右键单击,选择Generate,就可以看到下面这样一个小框

MyBatis使用教程详解<上>_第2张图片

选择第一个->点击OK,就可以看到创建项目时的引入依赖界面

MyBatis使用教程详解<上>_第3张图片

接下来需要在SQL中找到MyBatis,因为此处博主操作的是MySQL,所以也需要引入MySQL Driver依赖.

MyBatis使用教程详解<上>_第4张图片

 然后就可以在pom.xml中看到引入的依赖了

MyBatis使用教程详解<上>_第5张图片

这时候点击启动项目,会启动失败,提示信息显示没有指定数据源的URL,因此我们需要修改一下配置文件.

2.2 数据库准备&修改Spring配置

这里演示的是.yml配置文件,properties配置文件读者可自行操作.

在修改配置时,我们需要先创建一个数据库(博主创建的数据库名为config1114)

 然后在.yml文件中添加下列项(同学们不必记忆,只需要复制粘贴即可):

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/config1114?characterEncoding=utf8&useSSL=false
    username: root
    password: '******'
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true

MyBatis使用教程详解<上>_第6张图片

为了演示方便, 这里博主创建了两个表:User表和Student表

MyBatis使用教程详解<上>_第7张图片

 MyBatis使用教程详解<上>_第8张图片

 接下来我们需要创建实体类,建立Java对象和数据库中表的映射

//Student表对应的实体类
@Data
public class Student {
    private Integer id;
    private String name;
    private Integer age;
    private Date createTime;
    private Date updateTime;
}
//User表对应的实体类
@Data
public class User {
    private Integer id;
    private String name;
    private Integer age;

}

2.3 写持久层代码

持久层操作数据库有两种方式:

1. 使用注解

2. 使用xml文件

我们简单演示一下第一种,先创建一个持久层的接口UserMapper

@Mapper
public interface UserMapper {
    @Select("select * from user")
    List selectAll();
}

@Mapper是ibatis(MyBatis的前身)提供的注解,表示MyBatis创建了这个对象,并将该对象交给Spring管理.

MyBatis使用教程详解<上>_第9张图片

这里的Mapper必须是接口类型,因为它的方法不是由程序猿实现的,而是交给MyBatis实现

 2.4 测试持久层方法

我们知道,在启动SpringBoot项目后,需要前端进行访问才可以调用后端写的方法,这样做未免太过麻烦.我们可以直接使用单元测试类解决.

右键Generate->点击test

MyBatis使用教程详解<上>_第10张图片

按照上图进行操作,这是我们会发现test目录下多了一个对应的UserMapperTest类

MyBatis使用教程详解<上>_第11张图片

下面是这个类的代码.

@SpringBootTest//测试类进行方法测试时需要启动Spring容器
@Slf4j//使用该注解方便进行日志打印
class UserMapperTest {

    @BeforeEach
    void setUp() {
        log.info("所有测试方法执行前执行");
    }

    @AfterEach
    void tearDown() {
        log.info("所有测试方法执行后执行");
    }

    @Autowired
    private UserMapper userMapper;//注入userMapper对象
    @Test
    void selectAll() {
        List userList=userMapper.selectAll();
        log.info("查询全部User:{}",userList);
    }
}

下面我们点击selectAll方法旁边的倒三角进行测试(Test类的每个方法都可以独立运行).

可以看到打印日志上显示了全部的查询结果(博主自己提前加的数据).

 三. 使用注解实现方法

3.1 参数传递

刚才我们简单演示了使用注解查询user表中的全部数据,如果想要指定查询条件该怎么办呢?

这就需要借助#{}向SQL语句中传递参数.

@Select("select * from user where id=#{id}")//第一个id是user表中的字段名,第二个id是传递的参数名
User selectById(Integer id);

 现在在测试类中针对selectById()生成一个测试方法.

 @Test
 void selectById() {
        User user=userMapper.selectById(5);
        log.info("根据Id查询user:{}",user);
 }

可以看到查询结果(也是博主自己偷偷加的数据).

MyBatis使用教程详解<上>_第12张图片

可能有些友友疑惑这个SQL语句是从哪里来的?这实际上是yml配置文件中设置的,用于开发环境中,帮助程序猿调试代码.

MyBatis使用教程详解<上>_第13张图片

同时也可以看到 ,上面的SQL语句和我们使用JDBC时的语句是一样的,MyBatis框架是对JDBC进行了封装.

  •  如果SQL语句只需要传递一个参数,则这个参数可以任意命名(但不推荐).

这是再次使用运行测试方法,仍然能拿到刚才的数据

  •  可以使用@Param给传递的参数设置别名,这时SQL中的名字必须和注解内的名字对应.
@Select("select * from user where name=#{userName}")//name是user表中的字段名,userName是@Param中的参数
User selectByName(@Param("userName") String name);

针对这个方法生成一个测试方法.

@Test
void selectByName() {
    User user=userMapper.selectByName("李白");
    log.info("根据name查询user:{}",user);
}

下图为查询结果. 

MyBatis使用教程详解<上>_第14张图片

3.2 @Insert

刚才我们展示的是@Select注解,现在来学习Insert操作.

@Insert("insert into user(name,age) values(#{name},#{age})")//name是传递的user对象的name属性,age是user对象的age属性

int insertUser(User user);
  • insert,delete,updata这些更新数据库的操作都默认返回int表示表中更新的行数.(实际上是MySQL返回的)

针对这个方法生成一个测试方法.

@Test
void insertUser() {
        User user=new User();
        user.setName("薛宝钗");
        user.setAge(21);
        log.info("插入user结果:{}",userMapper.insertUser(user));
 }

 我们可以根据打印日志查看插入结果.

  •  如果传递的参数是一个对象,并且使用了@Param注解,那么#{}中的属性名就必须是对象名.属性的形式

MyBatis使用教程详解<上>_第15张图片

这时候我们再次运行测试方法,会抛出异常

 需要对原方法进行更改.

 再次运行测试方法,即可运行成功


 上面的步骤中,我们只是向数据库插入了一条记录,但是不知道这条记录的主键id,下面演示一下如何获取自增主键,需要借助另一个注解@Options

@Insert("insert into user(name,age) values(#{name},#{age})")

@Options(useGeneratedKeys = true,keyProperty = "id")//此处的id对应的是Java对象的属性名,表示将user表中的自增主键赋给哪个属性
int insertUserWithGetId(User user);

为该方法创建测试方法

@Test
void insertUserWithGetId() {
     User user=new User();
     user.setName("史湘云");
     user.setAge(19);
     userMapper.insertUserWithGetId(user);
     log.info("插入user:{}",user);
}

 可以从打印日志中看到插入该对象后的Id

3.3 @Delete

童靴们肯定都熟悉这个套路了,下面直接进行演示.

Mapper方法实现

@Delete("delete  from user where id=#{id}")
int deleteById(Integer id);

测试方法实现 

@Test
void deleteById() {
    int result=userMapper.deleteById(20);
    log.info("删除条目:"+result);
}

测试结果 

MyBatis使用教程详解<上>_第16张图片

3.4 @Update

Mapper方法实现

@Update("update user set name=#{name},age=#{age} where id=#{id}")
int updateById(User user);

 测试方法实现

@Test
    void updateById() {
        User user=new User();
        user.setName("张爱莲");
        user.setAge(28);
        user.setId(21);
        int result=userMapper.updateById(user);
        log.info("更新条目: "+result);
    }

 测试结果

当然了,如果不放心可以直接查询数据库查看是否更新完成.

 MyBatis使用教程详解<上>_第17张图片

3.5 @Select

同学们肯定会疑惑,我们不是一开始演示的就是查询操作嘛.

刚才我们的查询操作中,Java对象的属性名和user表中的字段名是完全一致的,所以从user表中查询的字段值可以直接赋给对象的成员变量,但是如果二者不一致该怎么办呢? 

下面有三种解决办法:

  • 将查询的数据库字段重命名
  • 使用@Results注解
  • 修改yml配置文件(推荐)

这时我们借助的是Student表,对应的实体类属性见右图.

 MyBatis使用教程详解<上>_第18张图片

如果我们按照原来的方式进行查询 :

先创建一个StudentMapper接口.

@Mapper
public interface StudentMapper {
    @Select("select * from student")
    List selectAll();
}

在创建一个对应的测试类并在该类中注入StudentMapper接口.

@SpringBootTest
@Slf4j//使用Slf4j提供的对象进行日志打印
class StudentMapperTest {
    @Autowired
    private StudentMapper studentMapper;//注入StudentMapper对象

   @Test
    void selectAll() {
        List studentList=studentMapper.selectAll();
        log.info("查询全部Student:{}",studentList);
    }
}

 点击方法旁边的倒三角,可得到下图中类似的查询结果(我插入的)

 可以看到,所有查询出来的对象,其createTime和updateTime成员的值均为null,这是因为没有与数据库中查询的字段对应.


先来演示第一种----对查询的数据库字段重命名

@Select("select id,name,age,create_time as createTime,update_time as updateTime from student")
List selectAll();

再来运行测试方法,可以看到没有createTime和updateTime属性都被赋上了值.

 同学们肯定会觉得,select后面跟上数据库中的字段名未免过于麻烦,不如直接使用*简单,但是实际开发过程中,我们最好还是使用前者,因为*所查询的数据量往往是非常大的,这就导致了效率变低.

下面演示第二种方法----使用@Results注解

@Select("select * from student")
@Results({@Result(column = "create_time",property = "createTime"),
          @Result(column = "update_time",property = "updateTime")})
          //column对应的是数据库中的字段名,property对应的是Java对象中的属性名
List selectAll2();

生成该方法的测试方法

@Test
    void selectAll2() {
        List studentList=studentMapper.selectAll2();
        log.info("查询全部Student:{}",studentList);
    }

 可以看到,createTime和updateTime成员变量同样拿到了对应的值

 如果别的方法也需要使用这种映射关系的话,只需给@Results注解添加一个id值,然后借助@ResultMap注解实现复用

@Select("select * from student where id=#{id}")
@ResultMap("map")
Student selectById(Integer id);

生成测试方法

 @Test
    void selectById() {
        Student student=studentMapper.selectById(1);
        log.info("根据id查询Student:{}",student);
    }

 

 接下来展示第三种,也是最简单的一种----借助yml配置文件解决

我们只需要给.yml配置文件添加一项信息

MyBatis使用教程详解<上>_第19张图片

 重新编写一次查询方法

@Select("select * from student")
List selectAll3();
@Test
    void selectAll3() {
        List studentList=studentMapper.selectAll3();
        log.info("查询全部Student:{}",studentList);
    }

可以看到,这次即便我们什么也不做,同样获取到了响应的值

这里不得不强调一下企业的命名规范:

  1.  表名,字段名使用小写字母或数字,单词之间以下划线分割
  2. Java成员变量名,使用小驼峰的形式,第一个单词首字母小写,另外的单词首字母大写
  3. Java类名,使用大驼峰的形式,所有单词的首字母均大写

四. 使用XML文件实现方法

 学习了使用注解的方式操作数据库,与JDBC相比是不是灰常简单!但是这种方式只能完成一些简单的SQL语句,如果想要实现复杂的功能,需要借助XML文件.

4.1 配置文件&XML文件准备

如果想借助XML实现接口的方法,我们需要先告诉配置文件XML文件的位置.

MyBatis使用教程详解<上>_第20张图片

我们现在resources目录下建一个mapper目录,然后建一个与UserXMLMMapper对应的xml文件.

 MyBatis使用教程详解<上>_第21张图片

然后在xml文件中添加下列内容(无需记忆,只需复制粘贴即可)






细心的同学会发现我的idea界面上有两个魂斗罗图标,这实际上是MyBatisX插件.童靴们自行在Idea上安装即可.

MyBatisX是基于MyBatis的又一层封装.

MyBatis使用教程详解<上>_第22张图片

 MyBatis使用教程详解<上>_第23张图片

 4.2

刚才我们已经准备好了一个接口UserXMLMapper,现在尝试实现一个增加user的方法.

@Mapper
public interface UserXMLMapper {
    int insertUser(User user);
}

在接口中定义好方法后,需要在xml文件中添加一项标签.

MyBatis使用教程详解<上>_第24张图片

如果我们的操作正确,会发现MyBatisX插件自动给我们添加了一对小鸟,点击它们中任意一个可以跳转到对方的位置.

 接下来在标签中编写SQL语句实现这个方法,与注解中的SQL语句一样,参数需要使用#{}传递.

    
        insert into user(name,age) values(#{name},#{age})
    

接着对这个方法进行单元测试.

@SpringBootTest
@Slf4j
class UserXMLMapperTest {

    @Autowired
    private UserXMLMapper userXMLMapper;//注入UserXMLMapper对象

    @Test
    void insertUser() {
        User user=new User();
        user.setName("欧阳修");
        user.setAge(78);
        int result=userXMLMapper.insertUser(user);
        log.info("增加条目:{}",result);
    }
}

我们可以在日志打印上发现结果.


在讲解@Insert的时候,我们提到可以使用@Options获取自增主键,那么在xml文件中如何获取呢?

需要给标签添加额外的参数.

    
        insert into user(name,age) values(#{name},#{age})
    

 细心的童靴会发现这两个参数与@Options中的参数是一样滴.

这次我们插入对象后打印该对象.

    @Test
    void insertUser() {
        User user=new User();
        user.setName("欧阳修");
        user.setAge(78);
        int result=userXMLMapper.insertUser(user);
        log.info("增加条目:{}",result);
        log.info("插入对象:{}",user);
    }

这样就拿到了自增id

 4.3

话不多说,我们直接cue流程.

UserXMLMapper方法定义.

int deleteById(Integer id);

 标签实现.

    
        delete from user where id=#{id}
    

测试方法编写.

    @Test
    void deleteById() {
        int result=userXMLMapper.deleteById(23);
        log.info("删除条目:"+result);
    }

4.4

 UserXMLMapper方法定义.

int updateById(User user);

标签实现 

    
        update user set name=#{name},age=#{age} where id=#{id}
    

单元测试方法实现.

@Test
    void updateById() {
        User user=new User();
        user.setId(13);
        user.setName("西施");
        user.setAge(24);
        int result=userXMLMapper.updateById(user);
        log.info("更新条目:"+result);
    }


上述的update方法,我们是根据user对象中的id找到了这个记录并进行了修改.当然,我们可以额外传递一个id指定某条记录进行更新.

int updateById2(User user,Integer id);
    
        update user set name=#{name},age=#{age} where id=#{id}
    

 编辑并运行下面的测试方法.

    @Test
    void updateById2() {
        User user=new User();
        user.setName("林黛玉");
        user.setAge(19);
        int result=userXMLMapper.updateById2(user,13);
        log.info("更新条目:"+result);
    }

这时候我们发现代码抛出了异常..仔细观察,是不是和使用@Param时没有指定对象名的异常类似?

这时候name和age字段名需要显式指定 对象名.属性名

    
        update user set name=#{user.name},age=#{user.age} where id=#{id}
    

再次运行测试方法,通过日志可发现修改成功.

童靴们会不会这样想,是不是因为传入的参数id与User对象中的id属性名字发生冲突了,所以MyBatis才会找不到这个参数.

于是乎,博主将传入的id改了个名字iid

 但是运行测试方法时,仍然抛出了同样的异常.

如果传入的是多个参数,需要以 对象名.属性名 的方式组织,否则MyBatis会找不到传递的参数.

4.5 标签至少需要两个属性.

selectAll()方法定义.

List selectAll();

select * from user

生成对应的测试方法.

    @Test
    void selectAll() {
        List userList=userXMLMapper.selectAll();
        log.info("查询全部user:{}",userList);
    }


同样地,当数据库中的字段与对象的属性不一致时,有三种办法来实现.

  1. 对数据库中查询的字段重命名
  2. 使用标签
  3. 开启驼峰命名 

这次我们使用的依然是Student表.

MyBatis使用教程详解<上>_第25张图片

第一种,第三种方法我们不再进行演示,与注解的使用是一样的.下文只演示第二种方法. 

先来创建一个StudentXMLMapper类,并定义selectAll方法.

@Mapper
public interface StudentXMLMapper {
    List selectAll();
}

再来配置xml文件,并生成对应的 select * from student

生成对应的测试方法并运行.

@SpringBootTest
@Slf4j
class StudentXMLMapperTest {

    @Autowired
    private StudentXMLMapper mapper;
    @Test
    void selectAll() {
        List list=mapper.selectAll();
        log.info("查询全部Student:{}",list);
    }
}

 可以在打印结果中看到,createTime和updateTime正确地显示出来了.

五. 多表联合查询

上面的演示中,我们查询的都是单表,现在来演示一下如何查询多个表.

5.1 数据库准备

这次要用到的是user表和article表.

MyBatis使用教程详解<上>_第26张图片

 MyBatis使用教程详解<上>_第27张图片

实际上,查询多个表和查询单个表是一样的操作,关键在于对应的实体类怎么定义.

我们需要在entity包下定义两个实体类对象.

@Data
public class User {
    private Integer id;
    private String name;
    private Integer age;

}
@Data
public class Article {
    private Integer id;
    private String title;
    private String content;
    private Integer authorId;
    private Date createTime;
    private Date updateTime;
}

现在,我们的要求是-----查询全部Article并且显示与其对应的作者名.

于是我们需要定义一个视图类来建立与查询数据的映射.

@Data
public class ArticleView extends Article{
    private String name;
}

下面创建一个ArticleVIewMapper类,然后用注解的方式编写SQL语句

@Mapper
public interface ArticleViewMapper {
    @Select("select article.*,name from article,user where article.author_id=user.id")
    List selectAll();
}

创建测试类并生成对应的测试方法.

@SpringBootTest
@Slf4j
class ArticleViewMapperTest {

    @Autowired
    private ArticleViewMapper mapper;
    @Test
    void selectAll() {
        List viewList=mapper.selectAll();
        log.info("查询结果:{}",viewList);
    }
}

现在来测试一下.

可以看到,打印结果并不是很理想,我们想要的是Article部分的全部信息,因为ArticleView类是Article类的子类,所以@Data帮助我们写的toString()方法并没有打印Article的信息. 

我们可以借助Idea重新生成一个toString()方法,这时代码在运行的时候会以我们写的方法为主.

@Data
public class ArticleView extends Article{
    private String name;

    @Override
    public String toString() {
        return "ArticleView{" +
                "name='" + name + '\'' +
                "} " + super.toString();
    }
}

再次进行测试,就可以显示出全部的信息了.

你可能感兴趣的:(mybatis,spring,boot,java-ee)