P1 SSM:Mybatis

175 阅读19分钟

一.环境统一

1.jdk1.8
2.mysql5.7
3.maven3.6.1/3.0.0
4.IDEA

二.学习mabatis前需要掌握的

1.JDBC
2.Mysql
3.java基础
4.Maven
5.Junit

三.如何学习

1.ssm框架,每一个框架都有官方文档,最好的方式就是看官方文档
2.mybatis:https://mybatis.org/mybatis-3/zh/index.html

四.是什么

mybatis最开始是apache的开源项目ibatis,后来迁移到了google并改名为mybatis,再后来迁移到了github;
1.持久层框架
2.支持定制化sql,存储过程和高级映射
3.避免了JDBC的代码
4.避免手动设置参数和获取结果集
5.使用xml或者注解配置接口,pojo为数据库的记录

五.为什么用

1.传统的JDBC太复杂,为了简化JDBC出来的框架,有了这个框架,我们只需要在他的规则下往里面填东西就可以,是自动化的
2.为了把数据存到数据库中
3.不用mybatis也可以
4.优点:
    <1>MyBatis 把 sql 放在 XML 文件中或注解方式编写,不用写在代码里,完成了sql语句和代码的解耦合,提高了可维护性
    <2>MyBatis 封装了底层 JDBC API 的调用细节,并能自动将结果集转换成 JavaBean 对象大大简化了 Java 数据库编程的重复工作。
    <3>因为 MyBatis 需要程序员自己去编写 sql 语句,程序员可以结合数据库自身的特点灵活控制 sql 语句,因此能够实现比 Hibernate 等全自动 orm 框架更高的查询效率,能够完成复杂查询

六.怎么用

1.如何获取mybatis

<1>maven仓库

https://mvnrepository.com/search?q=mybatis         

Snipaste_2022-01-11_12-46-47.png Snipaste_2022-01-10_11-17-00.png

<2>github

https://github.com/search?q=mybatis

Snipaste_2022-01-10_11-08-50.png Snipaste_2022-01-10_11-13-12.png

2.了解持久化与持久层

<1>持久化就是将程序的数据在持久状态和瞬时状态转化的过程
    持久状态:数据放到数据库中,只要不删库,数据都在
    瞬时状态:内存的特性是断电即失,所以需要持久化,把内存的数据放到数据库
<2>实现持久化的方式:早期的直接存在txt文本,后来io文件持久化,再后来数据库(JDBC)
<3>持久层就是用来做数据持久化,通过存到数据库可以实现数据持久化
<4>mybatis就是完成持久化的框架
    

3.入门案例以及CRUD实现(以下是查询demo,注意增删改需要提交事务)

<1>环境搭建

I.搭建数据库环境(数据库的建库建表)

Snipaste_2022-01-10_11-56-44.png

II.新建一个普通的maven项目(mybatis-study)

删除src目录把它当作父工程,pom文件中导入mybatis依赖和mysql驱动 
<dependencies>
    <!-- mysql驱动,连接数据库-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.22</version>
    </dependency>
</dependencies>

III.在父工程下新建子模块作为子项目

image.png

<2>编写核心配置文件以及工具类

I.编写xml核心配置文件

配置文件中写的是连接数据库用到的driver,url,username,password
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybaits?useSSL=false&useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT
jdbc.username=root
jdbc.password=88888888
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--加载properties属性文件-->
    <properties resource="jdbc.properties"></properties>
    <!--设置java类型别名-->
    <typeAliases>
        <package name="com.itheima.pojo"></package>
    </typeAliases>

    <!--环境配置-->
    <environments default="mysql">
        <!--mysql环境-->
        <environment id="mysql">
            <!--使用JDBC类型事务管理器 -->
            <transactionManager type="JDBC"></transactionManager>
            <!--使用连接池 POOLED-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"></property>
                <property name="url" value="${jdbc.url}"></property>
                <property name="username" value="${jdbc.username}"></property>
                <property name="password" value="${jdbc.password}"></property>
            </dataSource>
        </environment>
    </environments>
    <!--加载映射文件-->
    <mappers>
        <package name="com.itheima.mapper"></package>
    </mappers>
</configuration>

II.编写mybatis的工具类

从sqlsessionfactorybuilder/构造者设计模式中获取sqlsessionfactory/工厂设计模式,从工厂中获取sqlsession
i.为什么定义mybatis工具类
    1)每一个mybatis应用都是以SqlSessionFactory(工厂模式)的实例为核心的,SqlSessionFactory的实例可以通过SqlSessionFactoryBuilder(建造者模式)创建,而SqlSessionFactoryBuilder可以从xml配置文件构建出SqlSessionFactory的实例
    2)从SqlSessionFactory获取SqlSession的实例,使用SqlSession中执行数据库的sql命令
    3)使用SqlSession调用xml或者注解定义的sql语句
ii.怎么定义mybatis工具类
    怎么从SqlSessionFactory获取SqlSession
    1)获取 SqlSessionFactory    
    2)从SqlSessionFactory获取SqlSession          
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            String resource = "mybatisConfig.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

<3>编写实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}

<4>查询实现

I.dao层

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

II.编写dao层(mapper)配置类与mapper绑定

xml文件中写sql语句crud,满足sqlsession的调用,注意增删改进行commit事务提交
<?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.itheima.mapper.UserMapper">
    <select id="getUserList" resultType="com.itheima.pojo.User">
        select * from mybatis.user
    </select>
</mapper>

III.junit测试(使用sqlsession对象执行sql语句)

public class MyTest {
    @Test
    public void test(){
        //第一步:获取sqlsession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //第二部:getmapper
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = userMapper.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }

        //第三步:关闭sqlsession
        sqlSession.close();
    }
}

image.png

IV.报错分析

i.mapper注册报错

Snipaste_2022-01-10_20-15-58.png Snipaste_2022-01-10_20-05-27.png

ii.配置文件资源过滤问题
(maven中讲过,在pom文件中使用<build>...</build>

Snipaste_2022-01-10_20-17-18.png Snipaste_2022-01-10_20-31-41.png

<5>crud实现(需要提交事务)

I.接口编写

public interface UserMapper {
    //查询全部用户
    List<User> getUserList();
    //根据id查询用户
    User getUserById(int id);
    //新增一个用户
    int addUser(User user);
    //修改一个用户
    int updateUser(User user);
    //删除一个用户
    int deleteUser(int id);
}

II.映射文件编写sql语句

<?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.itheima.mapper.UserMapper">

    <select id="getUserList" resultType="com.itheima.pojo.User">
        select * from user
    </select>

    <select id="getUserById" resultType="com.itheima.pojo.User" parameterType="int">
        select * from user where id = #{id}
    </select>
    <!--对象中的属性可以直接取出来-->
    <insert id="addUser" parameterType="com.itheima.pojo.User">
        insert into user (id,name,pwd) value (#{id},#{name},#{pwd})
    </insert>

    <update id="updateUser" parameterType="com.itheima.pojo.User">
        update user set name = #{name},pwd = #{pwd} where id = #{id}
    </update>

    <delete id="deleteUser" parameterType="int">
        delete from user where id = #{id}
    </delete>
</mapper>

III.测试类编写以及测试

i.查询指定id测试
@Test
public void getUserById(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    User user = userMapper.getUserById(1);
    System.out.println(user);
    sqlSession.close();
}

image.png

ii.增加用户测试
@Test
public void addUser(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    userMapper.addUser(new User(4,"rain","1111"));

    List<User> userList = userMapper.getUserList();
    for (User user : userList) {
        System.out.println(user);
    }
    //增删改必须提交事务
    sqlSession.commit();
    sqlSession.close();
}

image.png

iii.更改用户测试
@Test
public void updateUser(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    userMapper.updateUser(new User(4,"老赵","2222"));
    List<User> userList = userMapper.getUserList();
    for (User user : userList) {
        System.out.println(user);
    }
    //增删改必须提交事务
    sqlSession.commit();
    sqlSession.close();
}

image.png

iv.删除用户测试
@Test
public void deleteUser(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    userMapper.deleteUser(4);
    List<User> userList = userMapper.getUserList();
    for (User user : userList) {
        System.out.println(user);
    }
    //增删改必须提交事务
    sqlSession.commit();
    sqlSession.close();
}

image.png

<6>.CRUD优化

I.万能map(优化传递的参数过多的情况)

万能map的使用(如果实体类或者数据库表的字段或者参数过多,考虑使用map
使用:map传递参数,直接在sql取出key就可以
好处:不需要知道数据库表里面有什么,sql随便自己写

image.png

II.模糊查询

<1>java代码执行的时候传递通配符,推荐这么写

Snipaste_2022-01-10_21-18-19.png Snipaste_2022-01-10_21-18-55.png

<2>sql拼接中使用通配符,注意sql注入问题

Snipaste_2022-01-10_21-25-08.png

七.mybatis三个核心接口(mybatis的作用域和生命周期)

作用域和生命周期很重要,使用错误会导致并发问题

1.sqlsessionfactorybuilder:

一旦创建sqlsessionfactory就不再需要,作用域是局部变量

2.sqlsessionfactory:

可以理解为数据库连接池,一旦创建就一直运行,作用域是应用作用域(全局),实现方式是可以通过单例模式或者静态单例模式;多次创建会视为代码的坏味道(浪费内存)

3.sqlsession:

可以理解为连接到连接池的请求,需要开启和关闭,作用域是方法作用域,用完就关闭,否则资源被占用(代码实现是用try..catch

4.mybatis的Demo执行流程

Snipaste_2022-01-11_13-20-14.png Snipaste_2022-01-11_13-29-00.png

八.配置优化解析

0.位置顺序

Snipaste_2022-01-10_21-51-08.png

1.核心配置文件: mybatis-config.xml

Snipaste_2022-01-10_21-27-04.png

2.environments

<1>mybatis默认事务管理器是JDBC,连接池是POOLED
<2>environment
    可以有多套环境,但是每一个SqlSessionFactory只能选择一种环境,默认development
    transactionManager:
        有两个,默认JDBC
    dataSource:
        数据源,用来连接数据库,学过的有dbcp,c3p0,druid(阿里的),Hikari(springboot集成的)
        默认的是POOLED,池子,用完还可以拿来连接,作用就是为了web响应更快
        

Snipaste_2022-01-10_21-38-15.png

3.properties

Snipaste_2022-01-10_21-53-44.png

4.typeAliases

<1>直接使用<typeAlias>标签写别名

Snipaste_2022-01-11_12-16-53.png

<2>也可以指定包名,Mybaits会在包名下搜索需要的JavaBean,默认别名就是这个实体类的类名,首字母小写  (也可以大写,默认小写)

Snipaste_2022-01-11_12-21-13.png

<3>两种方式使用场景
    i.实体类少用第一种,实体类多用第二种
    ii.第一种可以直接在写xml核心配置文件的时候支持DIY别名,第二种想要实现DIY,需要在实体类上加注解@Alias(" ")   

Snipaste_2022-01-11_12-29-46.png

5.settings

Snipaste_2022-01-11_12-42-44.png

常用设置:
<1>cacheEnabled 
    开启缓存
<2>lazyLoadingEnabled
    开启懒加载,提高效率
<3>mapUnderscoreToCameICase
    开启自动驼峰命名规则,把数据库表字段和实体类属性名做自动的映射(last_name -> lastName)
<4>logImpl
    Mybatis的日志具体实现,支持的值有SEL4J/LOG4J...,为了打印日志

6.映射器(mappers)

MapperRegistry:注册绑定我们的mapper.xml文件
<1>使用资源路径(推荐)和class方式   
    i.mapper和mapper.xml必须同名
    ii.mapper和mapper.xml必须在同一个包下,如果不在一个包,使用class方式不可能绑定注册,此时只能用资源路径mapper方式注册

Snipaste_2022-01-11_13-01-59.png

<2>使用扫描包进行绑定    

Snipaste_2022-01-11_13-10-12.png

<3>没有注册绑定就会报错

Snipaste_2022-01-11_13-11-56.png

7.其他配置

<1>typeHandlers(类型处理器)
<2>objectFactory(对象工厂)
<3>plugins插件
    mybatis-gengerator-core
    mybatis-plus
    通用mapper
    

九.ResultMap(结果集映射)

1.为什么用

<1>解决实体类属性名和数据库表中字段名不一致的问题

Snipaste_2022-01-11_13-44-32.png Snipaste_2022-01-11_13-46-10.png
Snipaste_2022-01-11_13-47-01.png

<2>为什么会出现查不到,为null的结果

mybatis配置中的类型处理器会把pwd与实体类属性对应

Snipaste_2022-01-11_13-51-24.png

<3>怎么解决(resultMap里只有result单一字段的解决方案)

i.用别名的方式把pwd as password,这样类型处理器就会把pwd转为password,就可以查询出结果

Snipaste_2022-01-11_14-00-39.png

ii.resultmap解决    

Snipaste_2022-01-11_17-51-32.png Snipaste_2022-01-11_17-54-13.png

3.怎么用

实体类属性名和数据库表中字段名不一致就出现了冲突,有冲突就用resultmap解决
resultMap对于简单的语句不需要配置显示的结果集映射,对于复杂的语句描述关系就可以    

image.png

<1>简单用法——单表操作(result元素)

image.png

<2>高级结果映射用法——多表操作(association/collection)

I.数据库设计

image.png

CREATE TABLE teacher (
  id INT(10) NOT NULL,
  name VARCHAR(30) DEFAULT NULL,
  PRIMARY KEY (id)

) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(id, name) VALUES (1, '秦老师');

CREATE TABLE student (
  id INT(10) NOT NULL,
  name VARCHAR(30) DEFAULT NULL,
  tid INT(10) DEFAULT NULL,
  PRIMARY KEY (id),
  KEY fktid (tid),
  CONSTRAINT fktid FOREIGN KEY (tid) REFERENCES teacher (id)

) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO student (id, name,tid) VALUES (1, '小明', 1);
INSERT INTO student (id, name,tid) VALUES (2, '小红', 1);
INSERT INTO student (id, name,tid) VALUES (3, '小张', 1); 
INSERT INTO student (id, name,tid) VALUES (4, '小李', 1); 
INSERT INTO student (id, name,tid) VALUES (5, '小王', 1);

II.多对一处理

多对一的理解:
  多个学生对应一个老师
  如果对于学生这边,就是一个多对一的现象,即从学生这边关联一个老师!   
i.搭建测试环境

image.png

@Data
public class Student {
    private int id;
    private String name; //多个学生可以是同一个老师,即多对一
    private Teacher teacher;
}
@Data
public class Teacher {
    private int id;
    private String name;
}
ii.接口编写
public interface TeacherMapper {
    @Select("select * from teacher where id = #{tid}")
    Teacher getTeacher(@Param("tid") int id);
}
public interface StudentMapper {
    //获取所有学生及对应老师的信息
    List<Student> getStudents();
}
iii.按查询嵌套处理(类似sql中的子查询)

image.png

①.sql语句编写
②mapper.xml配置文件
<select id="getStudents" resultMap="StudentTeacher">
  select * from student
</select>

<resultMap id="StudentTeacher" type="Student">
    <!--association关联属性 property属性名 javaType属性类型 column在多 的一方的表中的列名-->
    <association property="teacher"  column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>

<!-- 这里传递过来的id,只有一个属性的时候,下面可以写任何值 association中column多参数配置:
    column="{key=value,key=value}"
其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的 字段名。
-->
<select id="getTeacher" resultType="teacher">
    select * from teacher where id = #{id}
</select>
③测试
@Test
public void getTeacher(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

    List<Student> students = mapper.getStudents();
    for (Student student : students) {
        System.out.println("学生名:"+student.getName()+
                            "——老师名:"+student.getTeacher().getName());
    }
    sqlSession.close();
}
iv.按结果嵌套处理(类似联表查询)
①.sql语句编写
②mapper.xml配置文件
<select id="getStudents" resultMap="StudentTeacher" >
    select s.id sid, s.name sname , t.name tname
    from student s,teacher t
    where s.tid = t.id
</select>

<resultMap id="StudentTeacher" type="Student">
    <id property="id" column="sid"/>
    <result property="name" column="sname"/>
    <!--关联对象property 关联对象在Student实体类中的属性-->
    <association property="teacher" javaType="Teacher">
        <result property="name" column="tname"/>
    </association>
</resultMap>
③测试
@Test
public void getTeacher(){
   SqlSession sqlSession = MybatisUtils.getSqlSession();
   StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

   List<Student> students = mapper.getStudents();
   for (Student student : students) {
       System.out.println("学生名:"+student.getName()+
                           "——老师名:"+student.getTeacher().getName());
   }
   sqlSession.close();
}

IV.一对多处理

一对多的理解:
  一个老师拥有多个学生
  如果对于老师这边,就是一个一对多的现象,即从一个老师下面拥有一群学生(集合)!
i.搭建环境

image.png

@Data
public class Student {
    private int id;
    private String name;
    private int tid;
}
@Data
public class Teacher {
    private int id;
    private String name; //一个老师多个学生
    private List<Student> students;
}
ii.接口编写
public interface TeacherMapper {
    //获取指定老师,及老师下的所有学生
    Teacher getTeacher(@Param("id") int id);
}
iii.按结果嵌套处理(类似sql中的子查询)
①sql语句编写

image.png

②mapper.xml配置文件
<select id="getTeacher" resultMap="TeacherStudent">
    select s.id sid, s.name sname , t.name tname, t.id tid
    from student s,teacher t
    where s.tid = t.id and t.id=#{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
    <result  property="name" column="tname"/>
    <collection property="students" ofType="Student">
        <result property="id" column="sid" />
        <result property="name" column="sname" />
        <result property="tid" column="tid" />
    </collection>
</resultMap>
③测试
@Test
public void testGetTeacher(){
    SqlSession session = MybatisUtils.getSqlSession();
    TeacherMapper mapper = session.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacher(1);
    System.out.println(teacher);
    
}
iv.按查询嵌套处理

image.png

①sql语句编写
②mapper.xml配置文件
<select id="getTeacher2" resultMap="TeacherStudent2">
  select * from teacher where id = #{tid}
</select>

<resultMap id="TeacherStudent2" type="Teacher">
    <!--column是一对多的外键 , 写的是一的主键的列名-->
    <collection property="students" javaType="ArrayList"
                ofType="Student" column="id" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
    select * from student where tid = #{id}
</select>
③测试
@Test
public void testGetTeacher(){
    SqlSession session = MybatisUtils.getSqlSession();
    TeacherMapper mapper = session.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacher(1);
    System.out.println(teacher);

}

十.日志

1.是什么

<1>数据库的操作出现异常,需要排错的时候,就用得到日志
<2>日志工厂LogImpl
    Mybatis内置了很多的日志工厂,在settings配置中,具体使用哪一个日志实现,用setting指定       

Snipaste_2022-01-11_23-46-35.png

    1SLF4J
    2LOG4J (重点)
    3) LOG4J2 :LOG4J加强版
    4STDOUT_LOGGING :控制台输出 (重点)
        标准日志输出,不需要配置
    5JDK_LOGGING :java自带的日志工厂

2.为什么用

mapper映射文件中写sql语句,如果没有日志,sql语句写错只会在控制台报错,所以需要日志把错误打印出来来提高效率(之前是sout,debug,现在是日志工厂)

3.怎么用

<1>STDOUT_LOGGING

标准日志输出,在mybatis的xml核心配置文件中手动配置

Snipaste_2022-01-12_00-04-06.png

<2>Log4j

I.Log4j概述

i.Log4j是Apache的开源项目(Tomcat,maven,Log4j都是Apache开源的),通过使用Log4j,可以控制日志信息输出的目的地是控制台,文件 ...
ii.可以控制每一条日志的输出格式
iii.可以通过定义日志信息级别把输出信息分等级输出
iv.可以通过配置文件灵活配置,不用修改应用的代码

II.导包并编写log4j.properties

i.先导包         

Snipaste_2022-01-12_00-24-52.png

ii.在项目的类路径下建立log4j.properties并且配置     

Snipaste_2022-01-12_00-27-03.png

III.在mybatis的xml核心配置文件中手动配置

xml核心配置文件中settings设置,配置log4j为日志的实现       

Snipaste_2022-01-12_00-32-50.png

IV.测试类使用

i.在要使用log4j的类中导包对应的包
ii.日志对象,参数为当前类的class,注意设置日志的级别
    logger.info()相当于带权限的sout()
    logger.debug()是在调试阶段使用的
    logger.error()可以放在try..catch里面

Snipaste_2022-01-12_00-49-17.png

V.实际使用

public class MyTest {

    static Logger logger = Logger.getLogger(MyTest.class);
    @Test
    public void test(){
        //第一步:获取sqlsession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        //做一个提示信息
        logger.info("info:进入test()方法成功");
        
        //第二部:getmapper
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = userMapper.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }

        //第三步:关闭sqlsession
        sqlSession.close();
    }
}

image.png

十一.分页

使用分页是为了减少数据的处理量

1.limit分页

image.png

<1>编写接口以及mapper.xml写sql

public interface UserMapper {
    //选择全部用户实现分页
    List<User> getUserByLimit(Map<String,Integer>map);
}
<resultMap id="UserMap" type="User">
    <result column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="pwd" property="pwd"/>
</resultMap>

<select id="getUserByLimit" parameterType="map" resultType="UserMap">
    select * from user limit #{startIndex},#{pageSize}
</select>

<2>测试

@Test
public void getUserByLimit(){
    SqlSession session = MybatisUtils.getSqlSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("startIndex",0);
    map.put("pageSize",2);

    List<User> userList = mapper.getUserByLimit(map);
    for (User user : userList) {
        System.out.println(user);
    }
}

2.RowBounds类实现分页

面向对象的特点,不建议在开发中用这种java的方式实现分页,还是推荐使用sql的方式

<1>编写接口以及mapper.xml写sql

public interface UserMapper {
    //选择全部用户实现分页
    List<User> getUserByRowBounds();
}
<resultMap id="UserMap" type="User">
    <result column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="pwd" property="pwd"/>
</resultMap>

<select id="getUserByRowBounds" resultMap="UserMap">
     select * from user
</select>

<2>测试

@Test
public void getUserByRowBounds(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();

    //RowBounds实现
    RowBounds rowBounds = new RowBounds(0, 2);

    //通过java代码层面实现分页
    List<User> userList = sqlSession.selectList("com.itheima.mapper.UserMapper.getUserByRowBounds", null, rowBounds);
    
    for (User user : userList) {
        System.out.println(user);
    }
}

3.分页插件PageHelper

官网:https://pagehelper.github.io/docs/howtouse/

十二.注解开发

1.mybatis如何使用注解开发

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

2.sql 类型主要分类

@select () 
@update () 
@Insert () 
@delete ()
【注意】利用注解开发就不需要mapper.xml映射文件了 .

3.使用入门案例

image.png

<1>在接口上添加注解替代mapper.xml配置文件

public interface UserMapper {
    //查询全部用户
    @Select("select * from user")
    List<User> getUserList();
}

<2>mybatis的核心配置文件绑定接口

<!--使用class绑定接口,之前是绑定配置类-->
<mappers>
    <mapper class="com.itheima.mapper.UserMapper"/>
</mappers>

<3>测试

@Test
public void test(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    List<User> userList = mapper.getUserList();
    for (User user : userList) {
        System.out.println(user);
    }
}

image.png

<4>注意

如果数据库表字段和实体类属性不一致,需要用到resultMap解决冲突,注解的方式就不能解决了,还需要用到xml配置文件

4.注解方式实现crud

<1>自动提交事务

sqlSessionFactory中的方法opensession()方法,通过方法重载,可以设置参数的方式自动提交事务,不需要在commit()

I.源码分析

image.png

II.代码实现

image.png

<2>@Param使用

方法需要传递多个参数时,参数类型是基本类型用这个注解,引用类型不用加
使用注解举例:

I.接口编写

public interface UserMapper {
    //方法存在多个参数,所有的参数前面必须加上@Param("xxx")注解
    @Select("select * from user where id = #{id}")
    User getUserById(@Param("id") int id);
}

II.测试

@Test
public void test(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.getUserById(1);
    System.out.println(user);
    sqlSession.close();
}

image.png

<3>crud实现(自动开启事务提交)

I.设置自动提交事务

image.png

II.接口编写

public interface UserMapper {
    //添加一个用户
    @Insert("insert into user (id,name,pwd) values (#{id},#{name},#{pwd})") 
    int addUser(User user);
    //修改一个用户
    @Update("update user set name=#{name},pwd=#{pwd} where id = #{id}") 
    int updateUser(User user);
    //根据id删除用
    @Delete("delete from user where id = #{id}") 
    int deleteUser(@Param("id")int id);
}

III.测试

@Test
public void testAddUser() {
    SqlSession session = MybatisUtils.getSqlSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = new User(6, "join", "123456"); mapper.addUser(user);
    session.close();
}

@Test
public void testUpdateUser() {
    SqlSession session = MybatisUtils.getSqlSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = new User(6, "join", "zxcvbn"); mapper.updateUser(user);
    session.close();
}

@Test
public void testDeleteUser() {
    SqlSession session = MybatisUtils.getSqlSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    mapper.deleteUser(6);
    session.close();
}

5.#{}和${}区别

6.注解配合xml方式实现crud

<1>.结构目录

image.png

<2>.设置自动提交事务

image.png

<3>.接口编写以及mapper.xml配置文件编写

public interface UserMapper {
    //查询全部用户
    List<User> getUserList();
    //模糊查询
    List<User> getUserLike(@Param("value") String value);

    //根据id查询用户
    User getUserById(@Param("id") int id);
    //map优化
    List<User> getUserById2(Map<String, Integer> map);

    //新增一个用户
    int addUser(User user);
    //map优化传参
    int addUser2(Map<String, Object> map);
    int addUser3(Map<String, Object> map);

    //修改一个用户
    int updateUser(User user);
    //删除一个用户
    int deleteUser(@Param("id") int id);
}

<4>.测试

参考上

十三.动态sql

1.是什么

动态sql就是根据不同的条件生成不同的sql语句

2.为什么用

简化JDBC拼接sql语句的痛苦

3.怎么用

<1>搭建环境

image.png

I.数据库环境搭建

CREATE TABLE blog (
id varchar(50) NOT NULL COMMENT '博客id', 
title varchar(100) NOT NULL COMMENT '博客标题', 
author varchar(30) NOT NULL COMMENT '博客作者', 
create_time datetime NOT NULL COMMENT '创建时间', 
views int(30) NOT NULL COMMENT '浏览量'
) ENGINE=InnoDB DEFAULT CHARSET=utf8

II.编写核心配置文件

实体类驼峰命名属性与数据库表字段_命名自动转换的设置
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
    <!--下划线驼峰自动转换-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

III.编写工具类

i.编写mybatisutils工具类进行自动开启事务的管理

image.png

ii.编写idutils工具类为了生成随机id
public class IDUtils {
    public static String genId(){
        //这个工具类为了生成随机的id,而不是从1开始i++的id
        return UUID.randomUUID().toString().replaceAll("-","");
    }
}

IV.编写实体类

@Data
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}

V.编写实体类对应的Mapper接口和Mapper.xml文件

public interface BlogMapper {
    //新增一个博客
    int addBlog(Blog blog);
}
<insert id="addBlog" parameterType="blog">
    insert into blog (id, title, author, create_time, views)
    values (#{id},#{title},#{author},#{createTime},#{views});
</insert>

VI.测试

@Test
public void addInitBlog(){
    SqlSession session = MybatisUtils.getSqlSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);

    Blog blog = new Blog();
    blog.setId(IDUtils.genId());
    blog.setTitle("Mybatis如此简单");
    blog.setAuthor("狂神说");
    blog.setCreateTime(new Date());
    blog.setViews(9999);

    mapper.addBlog(blog);

    blog.setId(IDUtils.genId());
    blog.setTitle("Java如此简单");
    mapper.addBlog(blog);

    blog.setId(IDUtils.genId());
    blog.setTitle("Spring如此简单");
    mapper.addBlog(blog);

    blog.setId(IDUtils.genId());
    blog.setTitle("微服务如此简单");
    mapper.addBlog(blog);

    session.close();
}

image.png

<2>常用标签

I.if

需求:根据作者名字和博客名字来查询博客!如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询

image.png

i.接口编写以及mapper.xml编写sql
public interface BlogMapper {
    //if标签
    List<Blog> queryBlogIf(Map map);
}
<select id="queryBlogIf" parameterType="map" resultType="blog">
    select * from blog where
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</select>
ii.测试
@Test
public void addInitBlog() {
    SqlSession session = MybatisUtils.getSqlSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);

    HashMap map = new HashMap();
    map.put("title","Java如此简单");
    //map.put("author","狂神说");
    List<Blog> blogs = mapper.queryBlogIf(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }

    session.close();
}
iii.出现的问题
如果 author 等于 null,那么查询语句为 select * from user where title=#{title}, 但是如果title为空呢?那么查询语句为 select * from user where and author=#{author},这是错误的 SQL 语句,可以用where标签来解决    

image.png

II.trim(where,set)

i.接口编写以及mapper.xml编写sql
//where标签
List<Blog> queryBlogWhere(Map map);

//set标签
int updateBlog(Map map);
<!--where标签-->
<select id="queryBlogWhere" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </where>
</select>

<!--set标签-->
<!--注意set是用的逗号隔开-->
<update id="updateBlog" parameterType="map">
    update blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author}
        </if> </set>
    where id = #{id};
</update>
ii.测试
public void queryBlogWhere() {
    SqlSession session = MybatisUtils.getSqlSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);

    HashMap map = new HashMap();
    map.put("title","Java如此简单");
    map.put("author","狂神说");
    List<Blog> blogs = mapper.queryBlogWhere(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    session.close();
}

@Test
public void testUpdateBlog(){
    SqlSession session = MybatisUtils.getSqlSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);
    HashMap<String, String> map = new HashMap<String, String>();
    map.put("title","动态SQL");
    map.put("author","秦疆");
    map.put("id","9d6a763f5e1347cebda43e2a32687a77");
    mapper.updateBlog(map);
    session.close();
}

III.choose(when、otherwise)

有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句
i.接口编写以及mapper.xml编写sql
List<Blog>queryBlogChoose(Mapmap);
<select id="queryBlogChoose" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <choose>
            <when test="title != null">
                title = #{title}
            </when>
            <when test="author != null">
                and author = #{author}
            </when>
            <otherwise>
                and views = #{views}
            </otherwise>
        </choose>
    </where>
</select>
ii.测试
@Test
public void testQueryBlogChoose(){
    SqlSession session = MybatisUtils.getSqlSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("title","Java如此简单");
    map.put("author","狂神说");
    map.put("views",9999);//这个条件必须有
    List<Blog> blogs = mapper.queryBlogChoose(map);
    System.out.println(blogs);
    session.close();
}

IV.sql片段

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

image.png image.png

V.foreach(遍历)

i.接口编写以及mapper.xml编写sql
public interface BlogMapper {
    List<Blog> queryBlogForeach(Map map);
}
<!--
    select * from blog where 1=1 and (id=1 or id=2 or id=3)
-->
<select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <!--
        collection:指定输入对象中的集合属性
        item:每次遍历生成的对象
        open:开始遍历时的拼接字符串
        close:结束时拼接的字符串
        separator:遍历对象之间需要拼接的字符串
        -->
        <foreach collection="ids"  item="id" open="and (" close=")"
                 separator="or">
            id=#{id}
        </foreach>
    </where>
</select>
ii.测试
@Test
public void testQueryBlogForeach(){
    SqlSession session = MybatisUtils.getSqlSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    List<String> ids = new ArrayList<String>();
    ids.add("b3c46d9b58bb4b319f3746efe434b9ea");
    ids.add("e24e72a4eaba4af8a7d131e8c26dbc90");
    ids.add("b8942069301143a0a68c0299eae35686");
    map.put("ids",ids);
    List<Blog> blogs = mapper.queryBlogForeach(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }

    session.close();
}

image.png

十四.缓存Cache与mybatis缓存

1.缓存(读数据用得到,写数据不用缓存)

<1>是什么

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

<2>为什么用

连接数据库做查询的时候耗资源,我们想让一次查询的结果,可以暂存到一个可以直接取到的地方,这个地方就是内存,放在内存里查询的这些数据就是缓存,再次查询相同数据的时候,直接走缓存,不用走数据库(也就是不用再次连接数据库了)

<3>时候时候用

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

2.mybatis缓存

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

<1>一级缓存

一级缓存也叫本地缓存
    与数据库同一次会话期间查询到的数据会放在本地缓存中。
    以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
一级缓存的作用范围就是session的开启一直到关闭的这一段代码

image.png

I.接口以及对应的mapper.xml配置文件

public interface UserMapper {
    //根据id查询用户
    User queryUserById(@Param("id") int id);
}
<select id="queryUserById" resultType="user">
    select * from user where id = #{id}
</select>

II.测试

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

image.png

III.缓存失效的场景

i.查询的内容不一样
①.接口编写以及mapper.xml编写sql
public interface UserMapper {
    //根据id查询用户
    User queryUserById(@Param("id") int id);
}
<select id="queryUserById" resultType="user">
    select * from user where id = #{id}
</select>
②.测试
@Test
public void testCacheFail(){
    SqlSession session = MybatisUtils.getSqlSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = mapper.queryUserById(1);
    System.out.println(user);
    System.out.println("===============================");
    User user2 = mapper.queryUserById(2);
    System.out.println(user2);
    System.out.println(user==user2);
    session.close();
}

image.png

ii.增删改
可能会改变原来的数据,必定会刷新缓存导致缓存失效
iii.手动清理缓存
@Test
public void testCacheFail2(){
    SqlSession session = MybatisUtils.getSqlSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = mapper.queryUserById(1);
    System.out.println(user);
    session.clearCache();//手动清除缓存
    System.out.println("===============================");
    User user2 = mapper.queryUserById(1);
    System.out.println(user2);
    System.out.println(user==user2);
    session.close();
}

image.png

<2>二级缓存

3.自定义缓存

4.缓存原理

image.png