Mybatis学习文章

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: Mybatis学习文章

Mybatis

环境:

  • JDK1.8;
  • IDEA;
  • Mysql5.7;
  • maven3.6.1;

1、简介

1.1、什么是Mybatis

  • MyBatis 是一款优秀的持久层框架
  • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程
  • MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类 【Plain Old Java Objects,普通的 Java对象】映射成数据库中的记录。
  • MyBatis 本是apache的一个开源项目ibatis, 2010年这个项目由apache 迁移到了google code,并且改名为MyBatis 。
  • 2013年11月迁移到Github .
  • Mybatis官方文档 : http://www.mybatis.org/mybatis-3/zh/index.html
  • GitHub : https://github.com/mybatis/mybatis-3
  • 通过maven仓库导入MyBatis依赖:https://mvnrepository.com/search?q=mybatis

1.2、 持久化

持久化是将程序数据在持久状态和瞬时状态间转换的机制。

  • 即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。
  • JDBC就是一种持久化机制。文件IO也是一种持久化机制。

1.3、 持久层

  • 完成持久化工作的代码块 . ----> dao层 【DAO (Data Access Object) 数据访问对象】
  • 大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。

1.4、为什么需要Mybatis

  • Mybatis就是帮助程序猿将数据存入数据库中 , 和从数据库中取数据 .
  • 传统的jdbc操作 , 有很多重复代码块 .比如 : 数据取出时的封装 , 数据库的建立连接等等… , 通过框架可以减少重复代码,提高开发效率 .
  • MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射
  • 最重要的一点,使用的人多!公司需要!

2、Mybatis第一个程序

思路流程:搭建环境—>导入Mybatis—>编写代码—>测试

2.1 代码演示

1、创建工程

使用IDEA创建maven项目,理解父子模块

2、引入相关依赖

<!--导入依赖-->
<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.25</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.9</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>RELEASE</version>
        <scope>test</scope>
    </dependency>
</dependencies>

3、创建数据库

create database test;
create TABLE users(
   user_id int(32) NOT NULL auto_increment,    //用户ID
   user_name varchar(32),  //用户名
   user_password varchar(32) ,  //用户密码
   user_email varchar(32),   //邮箱
   user_role varchar(32) ,    //用户角色(管理员ADMIN、普通用户USER)
   user_status varchar(1),    //用户状态( 0:正常,1:禁用)
   PRIMARY KEY (user_id)   //主键是用户名
);
INSERT INTO `users` VALUES (1, '刘异', '123', 'liuyi@itcast.cn', 'ADMIN', '0');
INSERT INTO `users` VALUES (2, '午三', '123', 'wulan@itcast.cn', 'USER', '0');
INSERT INTO `users` VALUES (3, '布里', '123', 'buli@itcast.cn', 'USER', '0');
INSERT INTO `users` VALUES (4, '刘丽', '123', 'liuli@itcast.cn', 'USER', '1');
INSERT INTO `users` VALUES (5, '米兰', '123', 'milan@itcast.cn', 'USER', '0');
INSERT INTO `users` VALUES (6, '莱沙', '123', 'laisha@itcast.cn', 'USER', '0');

4、创建MyBatis核心配置文件

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

5、创建POJO实体类

public class User {
    private int user_id;
    private String user_name;
    private String user_password;
    private String user_email;
    private String user_role;
    private String user_status;
    public User() {
    }
    public User(int user_id, String user_name, String user_password, String user_email, String user_role, String user_status) {
        this.user_id = user_id;
        this.user_name = user_name;
        this.user_password = user_password;
        this.user_email = user_email;
        this.user_role = user_role;
        this.user_status = user_status;
    }
    @Override
    public String toString() {
        return "User{" +
                "user_id=" + user_id +
                ", user_name='" + user_name + '\'' +
                ", user_password='" + user_password + '\'' +
                ", user_email='" + user_email + '\'' +
                ", user_role='" + user_role + '\'' +
                ", user_status='" + user_status + '\'' +
                '}';
    }
    public int getUser_id() {
        return user_id;
    }
    public void setUser_id(int user_id) {
        this.user_id = user_id;
    }
    public String getUser_name() {
        return user_name;
    }
    public void setUser_name(String user_name) {
        this.user_name = user_name;
    }
    public String getUser_password() {
        return user_password;
    }
    public void setUser_password(String user_password) {
        this.user_password = user_password;
    }
    public String getUser_email() {
        return user_email;
    }
    public void setUser_email(String user_email) {
        this.user_email = user_email;
    }
    public String getUser_role() {
        return user_role;
    }
    public void setUser_role(String user_role) {
        this.user_role = user_role;
    }
    public String getUser_status() {
        return user_status;
    }
    public void setUser_status(String user_status) {
        this.user_status = user_status;
    }
}

6、创建接口文件UserMapper

public interface UserMapper {
     List<User> getUserList();
}

7、创建映射文件UserMapper.xml

  • namespace 十分重要,不能写错!
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.UserMapper">
    <select id="getUserList"  resultType="com.itheima.pojo.User">
        select * from users
    </select>
</mapper>

8、修改mybatis-config.xml配置文件

  • 十分重要,添加UserMapper.xml映射文件路径的配置
<mappers>
        <mapper resource="com/itheima/dao/UserMapper.xml"/>
    </mappers>

9、编写MyBatisUtils工具类

Mybatis的执行过程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NtHvATyt-1646732181804)(C:\Users\gyuji\AppData\Roaming\Typora\typora-user-images\image-20220224084007431.png)]

public class MybatisUtils {
    public static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        }
        catch (IOException e){
        }
    }
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

10、编写测试类

  • Junit 包测试
public class UserMapperTest {    @Test    public void test(){            SqlSession sqlSession = MybatisUtils.getSqlSession();            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);            List<User> userList = userMapper.getUserList();            for (User list : userList            ) {                System.out.println(list);            }            sqlSession.close();    }}

2.2、问题说明

  1. 初始化错误:java.lang.ExceptionInInitializerError
    解决方法:在pom.xml文件中新增资源过滤代码。
  2. 配置文件没有注册;
    解决方法:每一个Mapper.xml文件都要在Mybatis核心文件中注册。
  3. 绑定接口错误;
    解决方法:检查Mapper.xml文件中的namespace命名空间是否正确
  4. Maven静态资源过滤问题。

解决方法:在pom.xml中增加如下代码:

<build>    <resources>        <resource>            <directory>src/main/resources</directory>            <includes>                <include>**/*.yml</include>                <include>**/*.properties</include>                <include>**/*.xml</include>            </includes>            <filtering>true</filtering>        </resource>        <resource>            <directory>src/main/java</directory>            <includes>                <include>**/*.yml</include>                <include>**/*.properties</include>                <include>**/*.xml</include>            </includes>            <filtering>true</filtering>        </resource>    </resources></build>

3、增删改查实现

3.1 namespace

配置文件中namespace中的名称为对应Mapper接口或者Dao接口的完整包名,必须一致!

3.2 select

  • select标签是mybatis中最常用的标签之一
  • select语句有很多属性可以详细配置每一条SQL语句
  • SQL语句返回值类型。【完整的类名或者别名】
  • 传入SQL语句的参数类型 。【万能的Map,可以多尝试使用】
  • 命名空间中唯一的标识符
  • 接口中的方法名与映射文件中的SQL语句ID 一一对应
  • id
  • parameterType
  • resultType
  • 需求:根据id查询用户

1、在UserMapper中添加对应方法

public interface UserMapper {    //查询全部用户    List<User> getUserList();    //根据id查询员工信息    List<User> findById(int id);}

2、在UserMapper.xml中添加Select语句

<select id="findById" resultType="com.itheima.pojo.User" parameterType="Integer">    select * from users where user_id = #{user_id}</select>

3、测试类中测试

@Test    public void testfindById(){        SqlSession sqlSession = MybatisUtils.getSqlSession();        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);        List<User> userList = userMapper.findById(3);        System.out.println(userList);        sqlSession.close();    }

3.3 insert

我们一般使用insert标签进行插入操作,它的配置和select标签差不多!

需求:给数据库增加一个用户

1、在UserMapper接口中添加对应的方法

// 新增用户信息    int insertUser(User user);

2、在UserMapper.xml中添加insert语句

<insert id="insertUser" parameterType="com.itheima.pojo.User">    insert into users(user_id,user_name,user_password,user_email,user_role,user_status) values(#{user_id},#{user_name},#{user_password},#{user_email},#{user_role},#{user_status})</insert>

3、测试类中测试

@Testpublic void testinsertUser(){    SqlSession sqlSession = MybatisUtils.getSqlSession();    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);    int num = userMapper.insertUser(new User(7,"test","123","123@123","USER","0"));    if(num>0){        System.out.println("插入成功");    }    //第三步提交事物    sqlSession.commit();    //第四步关闭sqlSession    sqlSession.close();}

注意点:增、删、改操作需要提交事务!

3.4 update

一般使用update标签进行更新操作,它的配置和select标签差不多!

需求:修改用户的信息

1、在UserMapper接口中添加对应的方法

// 根据id修改用户信息int updateUser(User user);

2、在UserMapper.xml中添加update语句

<update id="updateUser" parameterType="com.itheima.pojo.User">     update users set user_id=#{user_id},user_name=#{user_name},user_password=#{user_password},user_email=#{user_email},user_role=#{user_role},user_status=#{user_status} where user_id=#{user_id}</update>

3、测试类中测试

@Testpublic void testupdateUser(){    SqlSession sqlSession = MybatisUtils.getSqlSession();    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);    int num = userMapper.updateUser(new User(7,"test2","123","123@123","USER","0"));    if(num>0){        System.out.println("修改成功");    }    //第三步提交事物    sqlSession.commit();    //第四步关闭sqlSession    sqlSession.close();}

3.5 delete

一般使用delete标签进行删除操作,它的配置和select标签差不多!

需求:根据id删除一个用户

1、同理,编写接口方法

// 根据id删除用户信息int delUser(int id);

2、在UserMapper.xml中添加delete语句

<delete id="delUser" parameterType="Integer">     delete from users where user_id=#{user_id}</delete>

3、测试类中测试

@Testpublic void testdelUser(){    SqlSession sqlSession = MybatisUtils.getSqlSession();    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);    int num = userMapper.delUser(7);    if(num>0){        System.out.println("删除成功");    }    //第三步提交事物    sqlSession.commit();    //第四步关闭sqlSession    sqlSession.close();}

小结:

  • 所有的增、删、改、操作都需要提交事务
  • 接口所有的普通参数,尽量都写上@Param参数,尤其是多个参数时,必须写上!
  • 有时候根据业务的需求,可以考虑使用map传递参数!
  • 为了规范操作,在SQL的配置文件中,我们尽量将parameter参数和resultType都写上

4、配置解析

4.1 核心配置文件

  • mybatis-config.xml 系统核心配置文件
  • MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
  • 能配置的内容如下(的子元素的执行顺序):
configuration(配置)properties(属性)settings(设置)typeAliases(类型别名)typeHandlers(类型处理器)objectFactory(对象工厂)plugins(插件)environments(环境配置)environment(环境变量)transactionManager(事务管理器)dataSource(数据源)databaseIdProvider(数据库厂商标识)mappers(映射器)<!-- 注意元素节点的顺序!顺序不对会报错 -->

配置文件各元素之间的关系如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sw2ISYX4-1646732181806)(C:\Users\gyuji\AppData\Roaming\Typora\typora-user-images\image-20220223225457915.png)]4.2 environments元素

<environments default="development"> <environment id="development">   <transactionManager type="JDBC">     <property name="..." value="..."/>   </transactionManager>   <dataSource type="POOLED">     <property name="driver" value="${driver}"/>     <property name="url" value="${url}"/>     <property name="username" value="${username}"/>     <property name="password" value="${password}"/>   </dataSource></environments>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yb1OhCmh-1646732181807)(C:\Users\gyuji\AppData\Roaming\Typora\typora-user-images\image-20220223225227886.png)]

  • 配置MyBatis的多套运行环境,将SQL映射到多个不同的数据库上,必须指定其中一个为默认运行环境(通过default指定)
  • 子元素节点:transactionManager - [ 事务管理器 ]
<!-- 语法 --><transactionManager type="[ JDBC | MANAGED ]"/>
  • 子元素节点:dataSource
  • 数据源是必须配置的。
  • 有三种内建的数据源类型
type="[UNPOOLED|POOLED|JNDI]")
  • unpooled:这个数据源的实现只是每次被请求时打开和关闭连接。
  • pooled:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来 , 这是一种使得并发 Web 应用快速响应请求的流行处理方式。
  • jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
  • 数据源也有很多第三方的实现,比如dbcp,c3p0,druid等等…

4.3 mappers元素

mappers

  • 映射器 : 在MyBatis的核心配置文件中,元素用于引入MyBatis映射文件。映射文件包含了POJO对象和数据表之间的映射信息,MyBatis通过核心配置文件中的元素找到映射文件并解析其中的映射信息。
  • 通过元素引入映射文件的方法有4种

引入资源方式

  1. 使用类路径引入
<mappers>    <mapper resource="com/itheima/dao/UserMapper.xml"/></mappers>
  1. 使用本地文件路径引入
<mappers>    <mapper url="file:///D:/com/itheima/dao/UserMapper.xml"/></mappers>
  1. 这种方式不推荐使用
  2. 使用接口类引入
<mappers>    <mapper class="com.itheima.dao.UserMapper"/></mappers>
  1. 注意:接口和它的Mapper配置文件必须同名,并且接口和它的Mapper配置文件必须在同一包下
  2. 使用包名引入
<mappers>    <package name="com.itheima.dao"/></mappers>

4.4 properties优化

是一个配置属性的元素,该元素的作用是读取外部文件的配置信息

我们来优化我们的配置文件

  1. 资源目录下新建一个db.properties
driver=com.mysql.cj.jdbc.Driverurl=jdbc:mysql://localhost:3306/test?useSSL=true&useUnicode=true&characterEncoding=UTF8username=rootpassword=123456
  1. 在MyBatis核心配置文件mybatis-config.xml中使用<properties… />元素引入db.properties文件,以获取数据库连接信息。
<properties resource="db.properties" />

4.5 typeAliases优化

核心配置文件若要引用一个POJO实体类,需要输入POJO实体类的全限定类名,而全限定类名比较冗长,如果直接输入,很容易拼写错误。这时可以使用元素为POJO实体类设置一个简短的别名,再通过MyBatis的核心配置文件与映射文件相关联。例如,POJO实体类User的全限定类名是com.itheima.pojo.User,未设置别名之前,映射文件的select语句块若要引用POJO类User,必须使用其全限定类名,引用代码如下。

select * from users

多个全限定类设置别名的方式

  1. 在元素下,使用多个元素为每一个全限定类逐个配置别名。
<typeAliases>        <typeAlias alias="User" type="com.itheima.pojo.User"/></typeAliases>
  1. 通过自动扫描包的形式自定义别名,默认别名就是这个类的类名,首字母小写。
<typeAliases>    <package name="com.itheima.pojo"/></typeAliases>

总结:在实体类比较少的时候,使用方式一;如果实体类十分多,建议使用方式二

mybatis框架已经为我们设置好的一些常用的类型的别名

别名 数据类型
string String
long Long
int Integer
double Double
boolean Boolean
… … … …

4.6 Mybatis核心对象概述

  • SqlSessionFactoryBuilder
    一旦创建了SqlSessionFactory,就不再需要它了
    局部变量
  • SqlSessionFactory
    相当于数据连接池
    SqlSessionFactory一旦被创建在运行期间会一直存在
  • SqlSession
    连接到连接池的一个请求
    SqlSession的实例不是线程安全的,不能被共享
    用完之后需要立刻关闭,否则资源无法释放
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aKN9TN2O-1646732181808)(C:\Users\gyuji\AppData\Roaming\Typora\typora-user-images\image-20220224084007431.png)]

5、ResultMap

5.1 问题说明

要解决的问题:属性名和字段名不一致

假设在pojo实体类的设计中密码和数据库中的不一样,进行测试。

public class User {    private int user_id;    private String user_name;    private String password;    private String user_email;    private String user_role;    private String user_status;    public User() {    }    public User(int user_id, String user_name, String password, String user_email, String user_role, String user_status) {        this.user_id = user_id;        this.user_name = user_name;        this.password = password;        this.user_email = user_email;        this.user_role = user_role;        this.user_status = user_status;    }    public int getUser_id() {        return user_id;    }    public void setUser_id(int user_id) {        this.user_id = user_id;    }    public String getUser_name() {        return user_name;    }    public void setUser_name(String user_name) {        this.user_name = user_name;    }    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;    }    public String getUser_email() {        return user_email;    }    public void setUser_email(String user_email) {        this.user_email = user_email;    }    public String getUser_role() {        return user_role;    }    public void setUser_role(String user_role) {        this.user_role = user_role;    }    public String getUser_status() {        return user_status;    }    public void setUser_status(String user_status) {        this.user_status = user_status;    }    @Override    public String toString() {        return "User{" +                "user_id=" + user_id +                ", user_name='" + user_name + '\'' +                ", password='" + password + '\'' +                ", user_email='" + user_email + '\'' +                ", user_role='" + user_role + '\'' +                ", user_status='" + user_status + '\'' +                '}';    }}

结果:

  • User{user_id=1, user_name=‘刘异’, password=‘null’, user_email=‘liuyi@itcast.cn’, user_role=‘ADMIN’, user_status=‘0’}
  • 查询出来发现 password 为空 . 说明出现了问题!

分析:

  • select * from users 可以看做:
    select user_id,user_name,user_password,user_email,user_role,user_status from users
  • mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 去对应的实体类中查找相应列名的set方法设值 , 由于找不到setPassword() , 所以user_password返回null ; 【自动映射】

5.2 解决方案

方案一:在SQL语句里面为列名指定别名 , 别名和java实体类的属性名一致 .

<select id="getUserList"  resultType="com.itheima.pojo.User">    select user_id,user_name,user_password as password,user_email,user_role,user_status from users</select>

方案二:使用结果集映射->ResultMap 【推荐】

1、返回值类型为resultMap;2、编写resultMap,实现手动映射!

<resultMap id="UserMap" type="User">    <!-- column是数据库表的列名 , property是对应实体类的属性名 -->    <result property="password" column="user_password"/></resultMap><select id="getUserList"  resultMap="UserMap">    select * from users</select>

6、使用注解开发

6.1 利用注解开发

  • mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建
  • sql 类型主要分成 :
  • @select ()
  • @update ()
  • @Insert ()
  • @delete ()

**注意:**利用注解开发就不需要mapper.xml映射文件了 。

1、我们在接口中添加注解

//根据全部员工信息    @Select("select * from users")    List<User> getUserList();

2、在mybatis的核心配置文件中注入

<!--使用class绑定接口-->    <mappers>        <mapper class="com.itheima.dao.UserMapper"/>    </mappers>

3、测试

@Testpublic void test(){    //第一个:获取SqlSession对象    SqlSession sqlSession = MybatisUtils.getSqlSession();    //方法一:getMapper    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);    List<User> userList = userMapper.getUserList();    System.out.println(userList);    //第三步:关闭SqlSession    sqlSession.close();}

6.2 注解增删改

【注意】确保实体类和数据库字段对应

根据用户id查询

1、编写接口方法注解

//根据id查询员工信息@Select("select * from users where user_id = #{id}")List<User> findById(@Param("id") int id);

2、测试

@Testpublic void testfindById(){    SqlSession sqlSession = MybatisUtils.getSqlSession();    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);    List<User> userList = userMapper.findById(3);    System.out.println(userList);    sqlSession.close();}

新增用户

1、编写接口方法注解

// 新增用户信息@Insert("insert into users(user_id,user_name,user_password,user_email,user_role,user_status) values(#{user_id},#{user_name},#{user_password},#{user_email},#{user_role},#{user_status})")int insertUser(User user);

2、测试

@Testpublic void testinsertUser(){    SqlSession sqlSession = MybatisUtils.getSqlSession();    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);    int num = userMapper.insertUser(new User(7,"test3","123","123@123","USER","0"));    if(num>0){        System.out.println("插入成功");    }    //第三步提交事物    sqlSession.commit();    //第四步关闭sqlSession    sqlSession.close();}

修改用户

1、编写接口方法注解

//修改用户信息@Update("update users set user_id=#{user_id},user_name=#{user_name},user_password=#{user_password},user_email=#{user_email},user_role=#{user_role},user_status=#{user_status} where user_id=#{user_id}")int updateUser(User user);

2、测试

@Testpublic void testupdateUser(){    SqlSession sqlSession = MybatisUtils.getSqlSession();    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);    int num = userMapper.updateUser(new User(7,"test2","123","123@123","USER","0"));    if(num>0){        System.out.println("修改成功");    }    //第三步提交事物    sqlSession.commit();    //第四步关闭sqlSession    sqlSession.close();}

删除用户

1、编写接口方法注解

// 根据id删除用户信息@Delete("delete from users where user_id=#{user_id}")int delUser(@Param("user_id")int user_id);

2、测试

@Testpublic void testdelUser(){    SqlSession sqlSession = MybatisUtils.getSqlSession();    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);    int num = userMapper.delUser(7);    if(num>0){        System.out.println("删除成功");    }    //第三步提交事物    sqlSession.commit();    //第四步关闭sqlSession    sqlSession.close();}

【注意点:增删改一定记得对事务的处理】

6.3 关于@Param

在方法参数的前面写上**@Param(“参数名”),表示给参数命名,名称就是括号中的内容**

//根据id查询员工信息    @Select("select * from users where user_id = #{user_id}")    List<User> findById(@Param("user_id") int id);

给入参 int id 命名为user_id,然后sql语句where user_id= #{user_id} 中就可以根据user_id得到参数值了。

6.4 #与$的区别

#{} 的作用主要是替换预编译语句(PrepareStatement)中的占位符? 【推荐使用】

INSERT INTO user (name) VALUES (#{name});INSERT INTO user (name) VALUES (?);

${} 的作用是直接进行字符串替换

INSERT INTO user (name) VALUES ('${name}');INSERT INTO user (name) VALUES ('kuangshen');

6.5 总结

使用注解和配置文件协同开发,才是MyBatis的最佳实践!

7、动态SQL

7.1 介绍

什么是动态SQL:动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句.

官网描述:MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。  -------------------------------  - if  - choose (when, otherwise)  - trim (where, set)  - foreach  -------------------------------

我们之前写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL ,稍微不注意,由于引号,空格等缺失可能都会导致错误。

那么怎么去解决这个问题呢?这就要使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。

元素 说明
判断语句,用于单条件判断
(、) 相当于Java中的switch…case…default语句,用于多条件判断
简化SQL语句中where的条件判断
可以灵活地去除多余的关键字
用于SQL语句的动态更新
循环语句,常用于in语句等列举条件中

7.2 环境搭建

1、创建数据库

CREATE TABLE t_customer (    id int(32) PRIMARY KEY AUTO_INCREMENT,    username varchar(50),    jobs varchar(50),    phone varchar(16));INSERT INTO t_customer VALUES ('1', 'joy', 'teacher', '13733333333');INSERT INTO t_customer VALUES ('2', 'jack', 'teacher', '13522222222');INSERT INTO t_customer VALUES ('3', 'tom', 'worker', '15111111111');INSERT INTO t_customer VALUES ('4', 'elina', 'worker', '1545333111');

2、创建MyBatis核心配置文件

<?xml version="1.0" encoding="UTF8" ?><!DOCTYPE configuration        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"        "http://mybatis.org/dtd/mybatis-3-config.dtd"><!--核心配置文件--><configuration>    <environments default="development">        <environment id="development">            <transactionManager type="JDBC"/>            <dataSource type="POOLED">                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>                <property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF8"/>                <property name="username" value="root"/>                <property name="password" value="123456"/>            </dataSource>        </environment>    </environments></configuration>

3、创建POJO实体类

public class Customer {    private Integer id;    private String username; // 主键ID、客户名称    private String jobs;    private String phone;      // 职业、电话    public Customer(){};    public Customer(Integer id, String username, String jobs, String phone) {        this.id = id;        this.username = username;        this.jobs = jobs;        this.phone = phone;    }    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getUsername() {        return username;    }    public void setUsername(String username) {        this.username = username;    }    public String getJobs() {        return jobs;    }    public void setJobs(String jobs) {        this.jobs = jobs;    }    public String getPhone() {        return phone;    }    public void setPhone(String phone) {        this.phone = phone;    }    @Override    public String toString() {        return "Customer{" +                "id=" + id +                ", username='" + username + '\'' +                ", jobs='" + jobs + '\'' +                ", phone='" + phone + '\'' +                '}';    }}

4、创建接口文件CustomerMapper

public interface CustomerMapper {   }

5、创建映射文件CustomerMapper.xml

<?xml version="1.0" encoding="UTF8" ?><!DOCTYPE mapper        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"        "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.itheima.dao.CustomerMapper">    </mapper>

6、修改mybatis-config.xml配置文件

  • 十分重要,添加CustomerMapper.xml映射文件路径的配置
<mappers>        <mapper resource="com/itheima/dao/CustomerMapper.xml"/>    </mappers>

7、编写MyBatisUtils工具类

public class MybatisUtils {    public static SqlSessionFactory sqlSessionFactory;    static {        try {            String resource = "mybatis-config.xml";            InputStream inputStream = Resources.getResourceAsStream(resource);            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);        }        catch (IOException e){        }    }    public static SqlSession getSqlSession(){        return sqlSessionFactory.openSession();    }}

8、编写测试类

  • Junit 包测试
public class UserMapperTest {  }

7.3 if语句

需求:根据用户名和职业来查询客户信息!如果用户名字为空,那么只根据职业查询,反之,则根据用户名来查询

1、编写接口类

//查找客户信息方法    List<Customer> queryCustomerIf(Map map);

2、编写SQL语句

<!--需求1:根据用户名和职业来查询客户信息!如果用户名字为空,那么只根据职业查询,反之,则根据用户名来查询select * from t_customer where username=#{username} and jobs=#{jobs}--><select id="queryCustomerIf" resultType="com.itheima.pojo.Customer" parameterType="map">        select * from t_customer where        <if test="username!=null">             username=#{username}        </if>        <if test="jobs!=null">            and jobs= #{jobs}        </if>    </select>

3、编写测试类

@Test   public void testIf(){        SqlSession sqlSession = MybatisUtils.getSqlSession();        CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);        HashMap map=new HashMap();        map.put("username","jack");        map.put("jobs","teacher");        List<Customer> datalist=customerMapper.queryCustomerIf(map);        for (Customer list : datalist) {            System.out.println(list);        }        sqlSession.close();    }

这样写我们可以看到,如果jobs等于 null,那么查询语句为 select * from t_customer where username=#{username},但是如果username为空呢?那么查询语句为 select * from t_customer where and jobs=#{jobs},这是错误的 SQL 语句,如何解决呢?请看下面的 where 语句!

7.4 where

修改上面的SQL语句;

<select id="queryCustomerIf" resultType="com.itheima.pojo.Customer" parameterType="map">        select * from t_customer        <where>            <if test="username!=null">                username=#{username}            </if>            <if test="jobs!=null">                and jobs= #{jobs}            </if>        </where>    </select>

这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。

7.5 Set

同理,上面的对于查询 SQL 语句包含 where 关键字,如果在进行更新操作的时候,含有 set 关键词,我们怎么处理呢?

1、编写接口方法

//使用set更新    int queryCustomerUpdate(Map map);

2、sql配置文件

<update id="queryCustomerUpdate" parameterType="map">    update t_customer    <set>        <if test="username!=null">            username=#{username},</if>        <if test="jobs !=null">            jobs=#{jobs},</if>        <if test="phone !=null">            phone=#{phone}</if>    </set>        where id=#{id}</update>

3、测试

@Test    public void testSet(){        SqlSession sqlSession = MybatisUtils.getSqlSession();        CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);        HashMap map=new HashMap();        map.put("id",1);        map.put("username","jane");        int num=customerMapper.queryCustomerUpdate(map);        if(num>0){            System.out.println("更新成功");        }        sqlSession.commit();        sqlSession.close();    }

7.6 choose语句

有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句。

1、编写接口方法

//使用choose查询客户信息    List<Customer> queryCustomerChoose(Map map);

2、sql配置文件

<select id="queryCustomerChoose" resultType="com.itheima.pojo.Customer" parameterType="map">        select * from t_customer        <where>            <choose>                <when test="username!=null">                    username=#{username}                </when>                <when test="jobs!=null">                    and jobs=#{jobs}                </when>                <otherwise>                    and phone is not null                </otherwise>            </choose>        </where>    </select>

3、测试

@Testpublic void testChoose(){    SqlSession sqlSession = MybatisUtils.getSqlSession();    CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);    HashMap map=new HashMap();    //map.put("username","joy");    map.put("jobs","teacher");    List<Customer> datalist=customerMapper.queryCustomerChoose(map);    for (Customer list : datalist) {        System.out.println(list);    }    sqlSession.close();}

7.7 SQL片段

有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。

提取SQL片段:

<sql id="if-name-job">  <if test="username!=null">          username=#{username}  </if>  <if test="jobs!=null">         and jobs= #{jobs}  </if></sql>

引用SQL片段:

<select id="queryCustomerIf" resultType="Customer" parameterType="map">    select * from t_customer     <where>    <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->       <include refid="if-name-job"></include>       <!-- 在这里还可以引用其他的 sql 片段 -->    </where></select>

注意:

①、最好基于 单表来定义 sql 片段,提高片段的可重用性

②、在 sql 片段中不要包括 where

7.8 Foreach

需求:我们需要查询t_customer表中 id 分别为1,2的客户信息

1、编写接口

//foreach使用方法List<Customer> findCustomerMap(Map map);

2、编写SQL语句

<select id="findCustomerMap" resultType="com.itheima.pojo.Customer" parameterType="map">        <!--       collection:指定输入对象中的集合属性       item:每次遍历生成的对象       open:开始遍历时的拼接字符串       close:结束时拼接的字符串       separator:遍历对象之间需要拼接的字符串       select * from blog where 1=1 and (id=1 or id=2 or id=3)     -->        select * from t_customer where id in        <foreach item="id"  collection="array"                 open="(" separator="," close=")">            #{id}        </foreach></select>

3、测试

@Test    public void testForeachArray(){        SqlSession sqlSession = MybatisUtils.getSqlSession();        CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);        HashMap map=new HashMap();        ArrayList<Integer> array=new ArrayList<Integer>();        array.add(1);        array.add(2);        map.put("array",array);        List<Customer> datalist=customerMapper.findCustomerMap(map);        for (Customer list : datalist) {            System.out.println(list);        }        sqlSession.close();    }

8、关联映射

8.1 关联映射概述

Java对象描述数据之间的关联映射关系有三种,分别是一对一一对多和多对多。

  • 一对一
    就是在本类中定义与之关联的类的对象作为属性,例如,A类中定义B类对象b作为属性,在B类中定义A类对象a作为属性。
  • 一对多
    就是一个A类对象对应多个B类对象的情况,例如,定义在A类中,定义一个B类对象的集合作为A类的属性;在B类中,定义A类对象a作为B类的属性。
  • 多对多
    在两个相互关联的类中,都可以定义多个与之关联的类的对象。例如,在A类中定义B类类型的集合作为属性,在B类中定义A类类型的集合作为属性。

8.2 一对一处理

一对一的理解:

  • 在现实生活中,一对一关联关系是十分常见的。例如,一个人只能有一个身份证,同时一个身份证也只会对应一个人。
  • 在MyBatis中,通过元素来处理一对一关联关系。元素提供了一系列属性用于维护数据表之间的关系。

数据库设计:

# 创建一个名称为tb_idcard的表CREATE TABLE  tb_idcard(      id INT PRIMARY KEY AUTO_INCREMENT,     CODE VARCHAR(18) );# 插入3条数据INSERT INTO tb_idcard(CODE) VALUES('152221198711020624');INSERT INTO tb_idcard(CODE) VALUES('152221198711020625');INSERT INTO tb_idcard(CODE) VALUES('152221198711020626');# 创建一个名称为tb_person的表CREATE TABLE  tb_person(      id INT PRIMARY KEY AUTO_INCREMENT,     name VARCHAR(18),     age int,     sex VARCHAR(18) );# 插入3条数据INSERT INTO tb_person VALUES(null,'王一',18,'女');INSERT INTO tb_person VALUES(null,'李二',22,'女');INSERT INTO tb_person VALUES(null,'张三',20,'男');

元素属性:

属性 说明
property 用于指定映射到的实体类对象的属性,与表字段一一对应
column 用于指定表中对应的字段
javaType 用于指定映射到实体对象的属性的类型
jdbcType 用于指定数据表中对应字段的类型
fetchType 用于指定在关联查询时是否启用延迟加载。fetchType属性有lazy和eager两个属性值,默认值为lazy
select 用于指定引入嵌套查询的子SQL语句
autoMapping 用于指定是否自动映射
typeHandler 用于指定一个类型处理器

元素是元素的子元素,它有两种配置方式,嵌套查询方式和嵌套结果方式,下面对这两种配置方式分别进行介绍。

问题: 一个人只能有一个身份证,同时一个身份证也只会对应一个人。

1、搭建测试环境

IDEA新建工程项目,引入Maven依赖。

2、创建IdCard实体类

public class IdCard {    private Integer id;                 // 主键id    private String code;              // 身份证号码    public IdCard() {    }    public IdCard(Integer id, String code) {        this.id = id;        this.code = code;    }    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getCode() {        return code;    }    public void setCode(String code) {        this.code = code;    }    @Override    public String toString() {        return "IdCard{" +                "id=" + id +                ", code='" + code + '\'' +                '}';    }

3、创建Person实体类

public class Person {    private Integer id;    private String name;    private Integer age;    private String sex;    private IdCard card;    public Person() {    }    public Person(Integer id, String name, Integer age, String sex, IdCard card) {        this.id = id;        this.name = name;        this.age = age;        this.sex = sex;        this.card = card;    }    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }    public String getSex() {        return sex;    }    public void setSex(String sex) {        this.sex = sex;    }    public IdCard getCard() {        return card;    }    public void setCard(IdCard card) {        this.card = card;    }    @Override    public String toString() {        return "Person{" +                "id=" + id +                ", name='" + name + '\'' +                ", age=" + age +                ", sex='" + sex + '\'' +                ", card=" + card +                '}';    }

4、创建接口文件

public interface PersonMapper {    //方法一:查询每个人对应的身份证号    List<Person> getPersonOne();    //方法二:查询每个人对应的身份证号    List<Person> getPersonTwo();}

5、创建xml映射文件

方式一:嵌套查询方式

<!--   需求:获取所有人员及对应身份证号的信息   思路:       1. 获取所有人员的信息       2. 根据获取的人员信息的card-ID->获取该身份证的信息       3. 思考问题,这样人员的结果集中应该包含身份证,该如何处理呢,数据库中我们一般使用关联查询?           1. 做一个结果集映射:PersonIdcard1           2. PersonIdcard1结果集的类型为 Person           3. 人员中身份证的属性为card,对应数据库中为id。              一个 [1,...)人员关联一个身份证=> 一对一、一对多           4. 查看官网找到:association – 一个复杂类型的关联;使用它来处理关联查询   --> <!--association关联属性 property属性名 javaType属性类型 column在多的一方的表中的列名-->
<select id="getPersonOne" resultMap="PersonIdcard1">        select * from tb_person</select>    <resultMap id="PersonIdcard1" type="com.itheima.pojo.Person">        <result property="id" column="id"/>        <result property="name" column="name"/>        <result property="age" column="age"/>        <result property="sex" column="sex"/>        <association property="card" column="id" javaType="com.itheima.pojo.IdCard" select="getIdCard"/>    </resultMap>    <select id="getIdCard" resultType="com.itheima.pojo.IdCard">        select * from tb_idcard where id=#{id}    </select>
<!--这里传递过来的id,只有一个属性的时候,下面可以写任何值association中column多参数配置:   column="{key=value,key=value}"   其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的字段名。-->

方式二:嵌套结果方式

<!--按查询结果嵌套处理思路:   1. 直接查询出结果,进行结果集的映射-->
<select id="getPersonTwo" resultMap="PersonIdcard2">        select a.id id,a.name name,a.age age,a.sex sex,b.id cardid,b.CODE code from tb_person a,tb_idcard b where a.id=b.personid    </select>    <resultMap id="PersonIdcard2" type="com.itheima.pojo.Person">        <result property="id" column="id"/>        <result property="name" column="name"/>        <result property="age" column="age"/>        <result property="sex" column="sex"/>        <association property="card" javaType="com.itheima.pojo.IdCard">            <result property="id" column="cardid"/>            <result property="code" column="code"/>        </association>    </resultMap>

6、添加PersonMapper.xml映射文件路径的配置

<mappers>    <mapper resource="com/itheima/dao/PersonMapper.xml"/></mappers>

7、测试

public void getPersonOneTest() {    //第一个:获取SqlSession对象    SqlSession sqlSession = MybatisUtils.getSqlSession();    //方法一:getMapper    PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);    List<Person> personList = personMapper.getPersonTwo();    System.out.println(personList);    //第三步:关闭SqlSession    sqlSession.close();}

8.3 一对多处理

在MyBatis中,通过元素来处理一对多关联关系。元素的属性大部分与元素相同,但其还包含一个特殊属性一ofType。ofType属性与javaType属性对应,它用于指定实体类对象中集合类属性所包含的元素的类型。

元素是元素的子元素,元素有嵌套查询和嵌套结果两种配置方式。

一对多的理解:

一个用户可以有多个订单,多个订单也可以归一个用户所有。

image-20220306144830461

数据库设计:

# 创建一个名称为tb_user的表CREATE TABLE tb_user (  id int(32) PRIMARY KEY AUTO_INCREMENT,  username varchar(32),  address varchar(256) );# 插入3条数据,其他语句省略INSERT INTO tb_user VALUES ('1', '小明', '北京');INSERT INTO tb_user VALUES ('2', '小红', '深圳');INSERT INTO tb_user VALUES ('3', '小爱', '上海');# 创建一个名称为tb_orders的表CREATE TABLE tb_orders (  id int(32) PRIMARY KEY AUTO_INCREMENT,  number varchar(32),  userid int);# 插入5条数据INSERT INTO tb_orders VALUES ('1001', '订单1', '1');INSERT INTO tb_orders VALUES ('1002', '订单2', '1');INSERT INTO tb_orders VALUES ('1003', '订单1', '2');INSERT INTO tb_orders VALUES ('1004', '订单3', '3');INSERT INTO tb_orders VALUES ('1005', '订单4', '2');

问题: 一个用户可以有多个订单,多个订单也可以归一个用户所有。

1、搭建测试环境

IDEA新建工程项目,引入Maven依赖。

2、创建Orders实体类

public class Orders {    private Integer id;          //订单id    private String number;      //订单编号    private String userid;          //用户编号    public Orders(){};    public Orders(Integer id, String number, String userid) {        this.id = id;        this.number = number;        this.userid = userid;    }    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getNumber() {        return number;    }    public void setNumber(String number) {        this.number = number;    }    public String getUserid() {        return userid;    }    public void setUserid(String userid) {        this.userid = userid;    }    @Override    public String toString() {        return "Orders{" +                "id=" + id +                ", number='" + number + '\'' +                ", userid='" + userid + '\'' +                '}';    }}

3、创建Users实体类

public class Users {    private Integer id;                         // 用户编号    private String username;              // 用户姓名    private String address;                 // 用户地址    private List<Orders> ordersList;  // 用户关联的订单    public Users(){};    public Users(Integer id, String username, String address, List<Orders> ordersList) {        this.id = id;        this.username = username;        this.address = address;        this.ordersList = ordersList;    }    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getUsername() {        return username;    }    public void setUsername(String username) {        this.username = username;    }    public String getAddress() {        return address;    }    public void setAddress(String address) {        this.address = address;    }    public List<Orders> getOrdersList() {        return ordersList;    }    public void setOrdersList(List<Orders> ordersList) {        this.ordersList = ordersList;    }    @Override    public String toString() {        return "Users{" +                "id=" + id +                ", username='" + username + '\'' +                ", address='" + address + '\'' +                ", ordersList=" + ordersList +                '}';    }}

4、创建接口文件

public interface UsersMapper {    //第一种方法    List<Users> getUsersOne();    //第二种方法    List<Users> getUsersTwo();}

5、创建xml映射文件

方式一:嵌套查询方式

<select id="getUsersOne" resultMap="UsersOrders1">        select * from tb_user    </select>    <resultMap id="UsersOrders1" type="com.itheima.pojo.Users">        <collection property="ordersList" javaType="ArrayList" ofType="com.itheima.pojo.Orders" select="getOrdersByUsers" column="id"/>    </resultMap>    <select id="getOrdersByUsers" resultType="com.itheima.pojo.Orders">        select * from tb_orders where userid=#{id}    </select>
<!--column是一对多的外键 , 写的是一的主键的列名-->

方式二:嵌套结果方式

<!--   思路:       1. 从客户表和订单表表中查出客户订单的相关信息       2. 对查询出来的操作做结果集映射           1. 集合的话,使用collection!               JavaType和ofType都是用来指定对象类型的               JavaType是用来指定pojo中属性的类型               ofType指定的是映射到list集合属性中pojo的类型。   -->
<select id="getUsersTwo" resultMap="UsersOrders2">     select tb_user.id id,username,address,tb_orders.id ordersid,number,userid from tb_user,tb_orders where tb_user.id=tb_orders.userid     </select>    <resultMap id="UsersOrders2" type="com.itheima.pojo.Users">        <result property="id" column="id"/>        <result property="username" column="username"/>        <result property="address" column="address"/>        <collection property="ordersList" ofType="com.itheima.pojo.Orders">            <result property="id" column="ordersid"/>            <result property="number" column="number"/>            <result property="userid" column="userid"/>        </collection>    </resultMap>

6、添加PersonMapper.xml映射文件路径的配置

<mappers>    <mapper resource="com/itheima/dao/UsersMapper.xml"/></mappers>

7、测试

@Test    public void getUsersOneTest() {        //第一个:获取SqlSession对象        SqlSession sqlSession = MybatisUtils.getSqlSession();        //方法一:getMapper       UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);        List<Users> usersList = usersMapper.getUsersTwo();        System.out.println(usersList);        //第三步:关闭SqlSession        sqlSession.close();    }

课堂练习:

学生和班级:一个学生属于一个班级

学生和班级:一个班级中有多个学生

8.3 多对多处理

自行练习:书籍161-166页内容。

8.4 小结

1、关联-association

2、集合-collection

3、所以association是用于一对一和多对一,而collection是用于一对多的关系

4、JavaType和ofType都是用来指定对象类型的

  • JavaType是用来指定pojo中属性的类型
  • ofType指定的是映射到list集合属性中pojo的类型。

注意说明:

1、保证SQL的可读性,尽量通俗易懂

2、根据实际要求,尽量编写性能更高的SQL语句

3、注意属性名和字段不一致的问题

4、注意一对多和多对一 中:字段和属性对应的问题

5、尽量使用Log4j,通过日志来查看自己的错误

9、缓存

9.1 简介

1、什么是缓存 [ Cache ]?

  • 存在内存中的临时数据。
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

2、为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率。

3、什么样的数据能使用缓存?

  • 经常查询并且不经常改变的数据。

9.2 Mybatis缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
  • MyBatis系统中默认定义了两级缓存:一级缓存二级缓存
  • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
  • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
  • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

9.3 一级缓存

一级缓存也叫本地缓存:

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
  • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

测试

1、在mybatis中加入日志,方便测试结果

2、编写接口方法

//根据id查询用户User queryUserById(@Param("id") int id);

3、接口对应的Mapper文件

<select id="queryUserById" resultType="user">  select * from user where id = #{id}</select>

4、测试

@Testpublic void testQueryUserById(){   SqlSession session = MybatisUtils.getSession();   UserMapper mapper = session.getMapper(UserMapper.class);   User user = mapper.queryUserById(1);   System.out.println(user);   User user2 = mapper.queryUserById(1);   System.out.println(user2);   System.out.println(user==user2);   session.close();}

5、结果分析

一级缓存失效的四种情况

一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;

一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!

1、sqlSession不同

@Testpublic void testQueryUserById(){   SqlSession session = MybatisUtils.getSession();   SqlSession session2 = MybatisUtils.getSession();   UserMapper mapper = session.getMapper(UserMapper.class);   UserMapper mapper2 = session2.getMapper(UserMapper.class);   User user = mapper.queryUserById(1);   System.out.println(user);   User user2 = mapper2.queryUserById(1);   System.out.println(user2);   System.out.println(user==user2);   session.close();   session2.close();}

观察结果:发现发送了两条SQL语句!

结论:每个sqlSession中的缓存相互独立

2、sqlSession相同,查询条件不同

@Testpublic void testQueryUserById(){   SqlSession session = MybatisUtils.getSession();   UserMapper mapper = session.getMapper(UserMapper.class);   UserMapper mapper2 = session.getMapper(UserMapper.class);   User user = mapper.queryUserById(1);   System.out.println(user);   User user2 = mapper2.queryUserById(2);   System.out.println(user2);   System.out.println(user==user2);   session.close();}

观察结果:发现发送了两条SQL语句!很正常的理解

结论:当前缓存中,不存在这个数据

3、sqlSession相同,两次查询之间执行了增删改操作!

增加方法

//修改用户int updateUser(Map map);

编写SQL

<update id="updateUser" parameterType="map">  update user set name = #{name} where id = #{id}</update>

测试

@Testpublic void testQueryUserById(){   SqlSession session = MybatisUtils.getSession();   UserMapper mapper = session.getMapper(UserMapper.class);   User user = mapper.queryUserById(1);   System.out.println(user);   HashMap map = new HashMap();   map.put("name","kuangshen");   map.put("id",4);   mapper.updateUser(map);   User user2 = mapper.queryUserById(1);   System.out.println(user2);   System.out.println(user==user2);   session.close();}

观察结果:查询在中间执行了增删改操作后,重新执行了

结论:因为增删改操作可能会对当前数据产生影响

4、sqlSession相同,手动清除一级缓存

@Testpublic void testQueryUserById(){   SqlSession session = MybatisUtils.getSession();   UserMapper mapper = session.getMapper(UserMapper.class);   User user = mapper.queryUserById(1);   System.out.println(user);   session.clearCache();//手动清除缓存   User user2 = mapper.queryUserById(1);   System.out.println(user2);   System.out.println(user==user2);   session.close();}

一级缓存就是一个map

9.4 二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
  • 工作机制
  • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
  • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
  • 新的会话查询信息,就可以从二级缓存中获取内容;
  • 不同的mapper查出的数据会放在自己对应的缓存(map)中;

使用步骤

1、开启全局缓存 【mybatis-config.xml】

<setting name="cacheEnabled" value="true"/>

2、去每个mapper.xml中配置使用二级缓存,这个配置非常简单;【xxxMapper.xml】

<cache/>官方示例=====>查看官方文档<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

3、代码测试

  • 所有的实体类先实现序列化接口
  • 测试代码
@Testpublic void testQueryUserById(){   SqlSession session = MybatisUtils.getSession();   SqlSession session2 = MybatisUtils.getSession();   UserMapper mapper = session.getMapper(UserMapper.class);   UserMapper mapper2 = session2.getMapper(UserMapper.class);   User user = mapper.queryUserById(1);   System.out.println(user);   session.close();   User user2 = mapper2.queryUserById(1);   System.out.println(user2);   System.out.println(user==user2);   session2.close();}

结论

  • 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
  • 查出的数据都会被默认先放在一级缓存中
  • 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中

缓存原理图

EhCache

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JXcliWpm-1646732181813)(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==)]

9.5 第三方缓存实现–EhCache

Ehcache是一种广泛使用的java分布式缓存,用于通用缓存;

要在应用程序中使用Ehcache,需要引入依赖的jar包

<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache --><dependency>   <groupId>org.mybatis.caches</groupId>   <artifactId>mybatis-ehcache</artifactId>   <version>1.1.0</version></dependency>

在mapper.xml中使用对应的缓存即可

<mapper namespace = “org.acme.FooMapper” >   <cache type = “org.mybatis.caches.ehcache.EhcacheCache” /></mapper>

编写ehcache.xml文件,如果在加载时未找到/ehcache.xml资源或出现问题,则将使用默认配置。

<?xml version="1.0" encoding="UTF-8"?><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"        updateCheck="false">   <!--      diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:      user.home – 用户主目录      user.dir – 用户当前工作目录      java.io.tmpdir – 默认临时文件路径    -->   <diskStore path="./tmpdir/Tmp_EhCache"/>      <defaultCache           eternal="false"           maxElementsInMemory="10000"           overflowToDisk="false"           diskPersistent="false"           timeToIdleSeconds="1800"           timeToLiveSeconds="259200"           memoryStoreEvictionPolicy="LRU"/>   <cache           name="cloud_user"           eternal="false"           maxElementsInMemory="5000"           overflowToDisk="false"           diskPersistent="false"           timeToIdleSeconds="1800"           timeToLiveSeconds="1800"           memoryStoreEvictionPolicy="LRU"/>   <!--      defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。    -->   <!--     name:缓存名称。     maxElementsInMemory:缓存最大数目     maxElementsOnDisk:硬盘最大缓存个数。     eternal:对象是否永久有效,一但设置了,timeout将不起作用。     overflowToDisk:是否保存到磁盘,当系统当机时     timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。     timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。     diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.     diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。     diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。     memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。     clearOnFlush:内存数量最大时是否清除。     memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。     FIFO,first in first out,这个是大家最熟的,先进先出。     LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。     LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。  --></ehcache>

合理的使用缓存,可以让我们程序的性能大大提升!

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
7月前
|
Java Spring 容器
Spring系列文章:Spring6集成MyBatis3.5
Spring系列文章:Spring6集成MyBatis3.5
|
7月前
|
SQL 测试技术 数据库
一篇文章带你学会MybatisPlus~(三)
一篇文章带你学会MybatisPlus~
|
2月前
|
Java 关系型数据库 MySQL
springboot学习五:springboot整合Mybatis 连接 mysql数据库
这篇文章是关于如何使用Spring Boot整合MyBatis来连接MySQL数据库,并进行基本的增删改查操作的教程。
181 0
springboot学习五:springboot整合Mybatis 连接 mysql数据库
|
3月前
|
Java 关系型数据库 数据库连接
mybatis-plus学习
MyBatis-Plus ,MyBatis 最佳搭档,只做增强不做改变,为简化开发、提高效率而生。
51 5
|
4月前
|
安全 Java 数据库连接
后端框架的学习----mybatis框架(3、配置解析)
这篇文章详细介绍了MyBatis框架的核心配置文件解析,包括环境配置、属性配置、类型别名设置、映射器注册以及SqlSessionFactory和SqlSession的生命周期和作用域管理。
后端框架的学习----mybatis框架(3、配置解析)
|
4月前
|
Java 数据库连接 mybatis
后端框架的学习----mybatis框架(9、多对一处理和一对多处理)
这篇文章介绍了在MyBatis框架中如何处理多对一和一对多的关联查询,通过定义`<resultMap>`和使用`<association>`与`<collection>`元素来实现对象间的关联映射。
|
4月前
|
Java 数据库连接 测试技术
后端框架的学习----mybatis框架(8、lombok)
这篇文章介绍了如何在MyBatis框架中使用lombok库来简化Java实体类的编写,包括在IDEA中安装Lombok插件、在项目中导入lombok依赖以及在实体类上使用Lombok提供的注解。
|
4月前
|
Java 数据库连接 数据库
后端框架的学习----mybatis框架(6、日志)
这篇文章介绍了如何在MyBatis框架中使用日志功能,包括配置MyBatis的日志实现、使用log4j作为日志工具,以及如何通过配置文件控制日志级别和输出格式。
|
4月前
|
SQL Java 数据库连接
后端框架的学习----mybatis框架(5、分页)
这篇文章介绍了如何在MyBatis框架中实现分页功能,包括使用SQL的`limit`语句进行分页和利用MyBatis的`RowBounds`对象进行分页的方法。
|
4月前
|
SQL Java 数据库连接
后端框架的学习----mybatis框架(7、使用注解开发)
这篇文章讲述了如何使用MyBatis框架的注解方式进行开发,包括在接口上使用注解定义SQL语句,并通过动态代理实现对数据库的增删改查操作,同时强调了接口需要在核心配置文件中注册绑定。