Mybatis框架笔记

Mybatis

环境说明:

  • jdk 8 +
  • MySQL 5.7
  • maven-3.6.10
  • IDEA

学习前需要掌握:

  • JDBC
  • MySQL
  • Java 基础
  • Maven
  • Junit

1、简介

Mybatis中文官网:https://mybatis.net.cn/

github:https://github.com/mybatis/mybatis-3/releases

maven仓库:

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.10</version>
</dependency>

1.1、什么是mybatis

  • MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射
  • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程
  • MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类 【Plain Old Java Objects,普通的 Java对象】映射成数据库中的记录。
  • MyBatis 本是apache的一个开源项目ibatis, 2010年这个项目由apache 迁移到了google code,并且改名为MyBatis 。
  • 2013年11月迁移到Github

1.2、持久化

数据持久化

  • 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
  • 内存:断电及失
  • 数据库(jdbc),io文件持久化

为什么需要持久化

  • 有一些对象,不能让他丢掉
  • 内存太贵了

1.3、持久层

Dao层,Service层,Controller层

  • 完成持久化工作的代码块
  • 底层界限十分明显

1.4、为什么需要Mybatis

  • 帮助我们将数据存入到数据库中

  • 方便

  • 传统的JDBC代码太复杂了,简化,框架,自动化

  • 不用mybatis也可以,更容易上手,技术没有高低之分

特点:

  • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件。易于学习,易于使用。通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
  • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
  • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性
  • 提供映射标签,支持对象与数据库的orm字段关系映射。
  • 提供对象关系映射标签,支持对象关系组建维护。
  • 提供xml标签,支持编写动态sql。

最重要的一点:使用的人多


2、第一个mybatis程序

思路:环境搭建-->导入mybatis-->编写代码-->测试

2.1、搭建环境

搭建数据库:

CREATE DATABASE mybatistest;
USE mybatistest;

CREATE TABLE `user`(
	`id` INT(20) NOT NULL,
	`name` VARCHAR(30) DEFAULT NULL,
	`pwd` VARCHAR(30) DEFAULT NULL,
	PRIMARY KEY(`id`)
)ENGINE = INNODB DEFAULT CHARSET = UTF8;

INSERT INTO `user` VALUES
(1, 'admin', '123456'),
(2, '李四', '123123'),
(3, '王五', '121212');

新建项目

  1. 新建一个普通的maven项目

  2. 删除src目录(作用:当做父工程)

  3. 导入maven依赖

    pom.xml

    <dependencies>
    <!--    数据库驱动-->
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.22</version>
        </dependency>
    <!--    mybatis-->
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.5.9</version>
        </dependency>
        <!--单元测试-->
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
          <scope>test</scope>
        </dependency>
    </dependencies>
    

2.2、创建一个模块

  1. 编写mybatis的核心配置文件

    <?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>
        <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/mybatistest?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=GMT"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
        <!--每一个Mapper.xml,都需要在Mybatis核心配置文件中注册-->
        <mappers>
            <mapper resource="com/yzh/dao/UserMapper.xml"/>
        </mappers>
    </configuration>
    

2.3、编写代码

  1. 编写Mybatis工具类

    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    public class MybatisUtils {
        private static SqlSessionFactory sqlSessionFactory;
        static {
            try {
                //步骤一:获取sqlSessionFactory对象
                String resource = "mybatis-config.xml";
                InputStream inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        //既然我们有了SqlSessionFactory:顾名思义,我们就可以直接从中获得SqlSession对象了
        //获取SqlSession连接(它包含了面向数据库执行SQL命令所需的全部方法)
        public static SqlSession getSession(){
            return sqlSessionFactory.openSession();
        }
    }
    
    
  2. 创建实体类

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data //自动生成get、set、toString函数
    @NoArgsConstructor //自动生成无参数构造函数。
    @AllArgsConstructor //自动生成全参数构造函数。
    public class User {
        private int id; //主键id
        private String name; //姓名
        private String pwd; //密码
    }
    

    由于我使用了lombok工具,使用相关的注解即可使用对应的方法,没有导入该lombok的需要手写set,get等方法。

  3. 编写Mapper接口

    public interface UserMapper {
        List<User> getAllUser();
    }
    
  4. 编写Mapper.xml配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--绑定一个对应的Dao/Mapper接口-->
    <mapper namespace="com.yzh.dao.UserMapper">
    
        <!--select查询语句,id和Mapper的方法名相同,返回类型返回一个User类型-->
        <select id="getAllUser" resultType="com.yzh.pojo.User">
            select * from user
        </select>
    </mapper>
    

2.4、测试

import com.yzh.pojo.User;
import com.yzh.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserTest {
    @Test
    public void getUserListTest(){
        //获得SqlSession对象
        SqlSession session = MybatisUtils.getSession();
        try {
            //方式1:调用getMapper(推荐)
            UserMapper mapper = session.getMapper(UserMapper.class);
            List<User> allUser = mapper.getAllUser();

            //方式2:使用selectList获取指定方法(不推荐)
//        List<User> allUser = session.selectList("com.yzh.dao.UserMapper.getAllUser");

            for (User user : allUser) {
                System.out.println(user.toString());
            }
        } finally {
            //记得最后要关闭
            session.close();
        }
    }
}

可能遇到的问题

  1. 配置文件没有注册

  2. 绑定接口错误

  3. 方法名不对

  4. 返回类型不对

  5. Maven静态资源过滤问题(需要在pom.xml中配置)

    <!--在build中配置resources,来防止我们资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
    

3、CRUD

3.1、Mapper.xml中的参数

  • namespace:参数中的包名要和Dao/Mapper接口的包名一致 com.yzh.dao.UserMapper
  • id:Dao/Mapper接口中的方法名
  • resultType:sql执行之后的返回类型
  • parameterType:传入的参数类型

3.2、Select

<select id="getAllUser" resultType="com.yzh.pojo.User">
    select * from user
</select>

3.3、Insert

<insert id="addUser" parameterType="com.yzh.pojo.User">
    insert into user value (#{id},#{name},#{pwd})
</insert>

3.4、Update

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

3.5、Delect

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

注意:增删改需要提交事务 sqlsession.commit();

3.6、需要注意的细节(重点)

注意:

  • namespace的参数建议把包名一起加上,虽然不加也可以,但是以后多了之后容易重名

  • mybatis核心配置文件下的数据库配置需要仔细检查

  • mybatis核心配置文件下的mapper映射,resource里面的参数使用/

    <mappers>
        <mapper resource="com/yzh/dao/UserMapper.xml"/>
    </mappers>
    

3.7、Map

Map传递参数,直接在sql中取出key即可

对象传递参数,直接在sql中取出对象的属性即可

只有一个基本类型参数的情况下,可以直接在sql中取到

多个参数用map,或者注解!

3.8、思考题

模糊查询如何写?

  1. Java代码执行的时候,传递通配符%%

    List<User> user = mapper.getUserById2("%admin%");
    
    <select id="getUserById2" resultType="com.yzh.pojo.User">
        select * from user where  name like  #{name}
    </select>
    
  2. 在sql拼接中使用通配符,会引起sql注入

    List<User> user = mapper.getUserById2("admin");
    
    <select id="getUserById2" resultType="com.yzh.pojo.User">
        select * from user where  name like '%' #{name} '%'
    </select>
    

4、配置解析

4.1、核心配置文件

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

4.2、环境配置(environments)

  • Mybatis可以配置多个环境,但是最终SqlSessionFactory实例只能选择一个环境,必须指定其中一个为默认运行 环境(通过default指定)

  • 具体的一套环境,通过设置id进行区别,id保证唯一!

  • Mybatis默认的事务管理器就是JDBC,还是一个是MANAGED

    <!-- 语法 -->
    <transactionManager type="[ JDBC | MANAGED ]"/>
    

注意:如果使用的戳 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

  • Mybatis默认的连接池数据源:type = UNPOOLED|POOLED|JND,默认的是POOLED
    • unpooled: 这个数据源的实现只是每次被请求时打开和关闭连接。
    • pooled: 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来 , 这是一种使得 并发 Web 应用快速响应请求的流行处理方式。
    • jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以 集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
    • 数据源也有很多第三方的实现,比如dbcp,c3p0,druid等等....

4.3、属性(properties)

我们可以通过properties来实现引用配置文件(db.properties)

这些属性都是可以外部传递并且动态替换的

db.properties

driver = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybatistest?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
username = root
password = 123456

在核心文件中映射

<!--引入外部配置文件,默认优先使用外部配置的-->
<properties resource="db.properties">
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</properties>
<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
</environments>

总结:

  • 可以直接引入外部文件
  • 可以在其中增加一些属性配置
  • 如果两个文件都有同一属性,优先使用外部配置文件的

4.4、类型别名(typeAliases)

  • 类型别名可为 Java 类型设置一个缩写名字, 它仅用于 XML 配置。

  • 意在降低冗余的全限定类名书写。

<!--需要一个一个取别名,不推荐-->
<typeAliases>
    <typeAlias type="com.yzh.pojo.User" alias="User" />
</typeAliases>

当这样配置时,User 可以用在任何使用 com.yzh.pojo.User 的地方。

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

扫描实体类下的Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。

<!--扫描该实体包下的所有,用首字母小写作为别名,推荐-->
<typeAliases>
    <package name="com.yzh.pojo"/>
</typeAliases>

总结:

  • 在实体类较少的情况下,建议使用第一种

  • 如果实体类比较多,推荐使用第二种

  • 第二种的别名也可以使用大写开头,但是建议小写

  • 第一种可以DIY(自定义)别名,第二种不可以,如果非要自定义别名,可以采用注解的方式 @Alias("hello")

    @Alias("hello")
    public class User {}
    
  • Java 类型内置的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。基本类型的前面有个下划线,包装类型则没有,比如说 int 类型的别名是 _int

4.5、设置(settings)

这是Mybatis中比较重要的配置,它们会改变Mybatis的运行时行为

  • 懒加载
  • 日志实现
  • 缓存开启关闭
image-20220706185728496

image-20220706190002776

image-20220706185859913

一个配置完整的 settings 元素的示例如下:

<settings>
    <!--缓存-->
    <setting name="cacheEnabled" value="true"/> 
    <!--懒加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="multipleResultSetsEnabled" value="true"/>
    <setting name="useColumnLabel" value="true"/>
    <setting name="useGeneratedKeys" value="false"/>
    <setting name="autoMappingBehavior" value="PARTIAL"/>
    <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
    <setting name="defaultExecutorType" value="SIMPLE"/>
    <setting name="defaultStatementTimeout" value="25"/>
    <setting name="defaultFetchSize" value="100"/>
    <setting name="safeRowBoundsEnabled" value="false"/>
    <!--驼峰命名-->
    <setting name="mapUnderscoreToCamelCase" value="false"/>
    <setting name="localCacheScope" value="SESSION"/>
    <setting name="jdbcTypeForNull" value="OTHER"/>
    <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

4.6、其他配置

4.7、映射器(mappers)

定义映射SQL语句文件

MapperRegistry:注册到我们绑定的Mapper文件

<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
</mappers>

<!-- 使用完全限定资源定位符(URL) -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
</mappers>

<!-- 
使用映射器接口实现类的完全限定类名 
需要配置文件名称和接口名称一致,并且位于同一目录下
-->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
</mappers>

<!-- 
将包内的映射器接口实现全部注册为映射器
需要配置文件名称和接口名称一致,并且位于同一目录下
-->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

4.8、声明周期和作用域

image-20220709210019356

作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder:

  • 一旦创建了SqlSessionFactory,就不再需要它了
  • 局部变量

SqlSessionFactory:

  • 说白了就是可以想象为:数据库连接池
  • SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
  • 因此SqlSessionFactory的最佳作用域是应用作用域。
  • 最简单的就是使用单例模式或者静态单例模式。

SqlSession:

  • 连接到连接池的一个请求!
  • SqlSession的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
  • 用完之后需要赶紧关闭,否则资源被占用!

image-20220709210715356

这里面的每一个Mapper,就代表一个具体的业务!


5、ResultMap

5.1、属性解释

  • id:一个ID结果;标记出作为ID的结果可以帮助提高整体性能
  • result:注入到字段或JavaBean属性的普通结果
  • association:一个复杂类型的关联;许多结果将包装成这种类型
    • 嵌套结果映射 - 关联本身可以是一个resultMap元素,或者从别处引用一个
  • collection:一个复杂类型的集合
    • 嵌套结果映射 - 集合本身可以是一个resultMap元素,或者从别处引用一个
  • discriminator:使用结果值来决定使用哪个resultMap
    • case - 基于某些值的结果映射
      • 嵌套结果映射 - case本身可以是一个resultMap元素,因此可以具有相同的结构和元素,或者从别处引用一个

5.2、查询为null的问题

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

环境:新建一个项目,将之前的项目拷贝过来

  1. 查看之前的数据库的字段名

    image-20220709212516613

  2. Java中的实体类设计

    public class User {
        private int id; //id
        private String name; //姓名
        private String password; //密码和数据库不一样!
        
        //构造
        //set/get
        //toString()
    }
    
  3. 接口

    //根据id查询用户
    User getUserById(int id);
    
  4. mapper映射文件

    <select id="getUserById" resultType="user">
        select * from user where id = #{id}
    </select>
    
  5. 测试

    @Test
        public void TestgetAllUser(){
            SqlSession session = MybatisUtils.getSession(); //获取SqlSession连接
            UserMapper mapper = session.getMapper(UserMapper.class);
            User user = mapper.getUserById(1);
            System.out.println(user.toString());
            session.close();
        }
    //测试结果:
    //User(id=1, name=admin, password=null)
    //查询出来发现 password 为空,说明存在问题
    

分析:

  • select * from user where id = #{id} 可以看做
    select id,name,pwd from user where id = #

  • mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 去对应的实体类中查找相应列名的set方法设值 , 由于找不到setPwd() , 所以password返回null ; 【自动映射】

5.3、解决方案

方案一:为列名指定别名 , 别名和java实体类的属性名一致 .

<select id="selectUserById" resultType="user">
    select id, name, pwd as password from user where id = #{id}
</select>

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

<!--结果集映射-->
<resultMap id="UserMap" type="user">
    <!-- column是数据库表的列名 , property是对应实体类的属性名 -->
    <result column="id" property="id" />
    <result column="name" property="name" />
    <result column="pwd" property="password" />
</resultMap>

<select id="getUserById"  resultMap="UserMap">
    select * from user where id = #{id}
</select>
  • resultMap 元素是 MyBatis 中最重要最强大的元素。
  • ResultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。
  • ResultMap 最优秀的地方在于,如果你对它相当了解了,但是根本就不需要显式地用到它们。(即相同的结果集映射可以不用定义)

你已经见过简单映射语句的示例了,但并没有显式指定 resultMap 。比如:


6、日志

6.1、日志工厂

如果一个数据库操作,出现了异常,我们需要排错。日志就是最好的助手!

曾经:sout、debug

现在:日志工厂!

image-20220706185859913

  • SLF4J
  • LOG4J 【掌握】
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING 【掌握】
  • NO_LOGGING

在Mybatis中具体使用那个一日志实现,在设置中设定!

<settings>
    <!-- 标准日志工厂 -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

6.2、Log4j

什么是log4j?

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
  • 我们也可以控制每一条日志的输出格式;
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
  • 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

1、导入依赖

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

2、在 resources目录下创建log4j.properties配置文件

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#log4j.appender.console.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/logFile.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

3、配置log4j为日志的实现

<settings>
	<setting name="logImpl" value="LOG4J"/>
</settings>

4、在程序中使用Log4j进行输出!

import com.yzh.pojo.User;
import com.yzh.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;

public class UserTest {
	
    //注意导包:org.apache.log4j.Logger
    static Logger logger = Logger.getLogger(UserTest.class);

    @Test
    public void TestLog4j(){
        logger.info("info:进入TestLog4j方法");
        logger.debug("debug:进入TestLog4j方法");
        logger.error("error: 进入TestLog4j方法");

        SqlSession session = MybatisUtils.getSession();
        
        UserMapper mapper = session.getMapper(UserMapper.class);
        User user = mapper.getUserById(1);
        System.out.println(user.toString());

        session.close();
    }
}

5、测试,看控制台输出!

  • 控制台输出了关于log4j的信息
  • 还生成了一个日志的文件 【需要修改file的日志级别】

总结:

在第4步中需要注意的地方

  • 在要使用log4j的类中,导入 import org.apache.log4j.Logger;

  • 日志对象,参数为当前类的class

    static Logger logger = Logger.getLogger(UserTest.class);
    
  • 常用日志级别

    logger.info("info:进入TestLog4j方法");
    logger.debug("debug:进入TestLog4j方法");
    logger.error("error: 进入TestLog4j方法");
    

7、分页

7.1、为什么要分页

作用:减少数据的处理量。

如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。

7.2、使用limit分页

#语法
SELECT * FROM table LIMIT stratIndex,pageSize
SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15

#为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1(该方法已被修复)
SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.

#如果只给定一个参数,它表示返回最大的记录行数目
SELECT * FROM table LIMIT 5; //检索前 5 个记录行
#换句话说,LIMIT n 等价于 LIMIT 0,n。

7.3、使用mybatis实现分页,核心sql

1、接口

//测试limit分页
List<User> getUserByLimit(Map<String,Integer> map);

2、Mapper.xml

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

3、测试

@Test
public void TestgetUserByLimit(){
    SqlSession session = MybatisUtils.getSession();

    UserMapper mapper = session.getMapper(UserMapper.class);
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("startIndex",1);
    map.put("pageSize",2);
    List<User> userList = mapper.getUserByLimit(map);

    for (User user : userList) {
        System.out.println(user.toString());
    }

    session.close();
}

7.4、RowBounds分页

除了使用Limit在SQL层面实现分页,也可以使用RowBounds在Java代码层面实现分页,当然此方式作为了解即可

1、接口

//选择全部用户RowBounds实现分页
List<User> getUserByRowBounds();

2、mapper文件

<select id="getUserByRowBounds" resultType="user">
	select * from user
</select>

3、测试类,在这里,我们需要使用RowBounds类

@Test
public void testUserByRowBounds() {
    SqlSession session = MybatisUtils.getSession();
    
    int currentPage = 2; //第几页
    int pageSize = 2; //每页显示几个
    RowBounds rowBounds = new RowBounds((currentPage1)*pageSize,pageSize);
    //通过session.**方法进行传递rowBounds,[此种方式现在已经不推荐使用了]
    List<User> users = session.selectList("com.kuang.mapper.UserMapper.getUserByRowBounds",null, rowBounds);
    
    for (User user: users){
    	System.out.println(user);
    }
    session.close();
}

7.5、分页插件PageHelper

了解即可,需要使用的时候查看官网文档

官方文档:https://pagehelper.github.io/


8、使用注解开发

8.1、面向接口编程

  • 大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
  • 根本原因:解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好
  • 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;
  • 而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。

关于接口的理解

  • 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。、
  • 接口的本身反映了系统设计人员对系统的抽象理解。
  • 接口应有两类:
    • 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
    • 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);
  • 一个体有可能有多个抽象面。抽象体与抽象面是有区别的。

三个面向区别

  • 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法.
  • 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现.
  • 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题.更多的体现就是对系统整体的架构

8.2、利用注解开发

  • mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。

  • Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建

  • 注解按类型分

    • @select ()
    • @update ()
    • @Insert ()
    • @delete ()

【注意】利用注解开发就不需要mapper.xml映射文件了 ,注解和xml可以同时使用。

1、在接口方法上定义注解

@Select("select * from user")
List<User> getUsers();

2、在mybatis的核心配置文件中绑定接口

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

3、测试

@Test
public void TestGetUsers(){
    SqlSession session = MybatisUtils.getSession();

    UserMapper mapper = session.getMapper(UserMapper.class);
    List<User> users= mapper.getUsers();

    for (User user : users) {
        System.out.println(user.toString());
    }

    session.close();
}

本质:反射机制实现

底层:动态代理

image-20220712191633537

8.3、mybatis的详细执行流程

image-20220713172957982

8.4、注意事项

注意事项:

  • 我们可以在工具类创建的时候实现自动提交事务
//设置为true为设置自动提交事务,否则需要手动增删改需要手动commit提交事务
public static SqlSession getSession(){
    return sqlSessionFactory.openSession(true);
}
  • 如果方法存在多个参数,所有的参数前面必须加上 @Param("id") 注解,以注解的属性名为准

  • 我们必须要将接口注册绑定到mybatis的核心配置文件中!

关于@Param()注解:

  • 基本类型的参数或者String类型,需要加上
  • 引用类型不需要加
  • 如果只有一个基本类型的话,可以忽略,但是建议加上!
  • 在SQL中引用的就是@Param("uid")中设定的属性名!

8.5、#{}和${}的区别

Mybatis中 #{} 和 ${} 是传递查询参数的两种方式

#{}

  • MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为 ?
  • 预编译SQL,通过 PreparedStatement 的 setXxxx 的方法进行参数赋值。
  • 使用 #{} 可以有效地防止 SQL 注入

${}

  • MyBatis 在处理 ${} 时,会直接把${} 替换为参数值
  • 存在 SQL 注入的风险。

总结:

  • #{} 比 ${} 安全,有效的防止SQL注入。
  • 预编译是提前对 SQL 进行编译,后面注入的参数不会对 SQL 的结构产生影响,从而避免安全风险。
  • #{} 针对输入字符串进行了转义过滤处理,能够防止SQL注入,适用于给SQL语句的where条件传值的使用场景。
  • ${} 设计就是用于参与SQL的语法生成,适用于需要通过传递值来拼接SQL语句的场景。

9、Lombok

9.1、简介

Lombok是一款Java开发插件,使得Java开发者可以通过其定义的一些注解来消除业务工程中冗长和繁琐的代码,尤其对于简单的Java模型对象(POJO)。在开发环境中使用Lombok插件后,Java开发人员可以节省出重复构建,诸如hashCode和equals这样的方法以及各种业务对象模型的accessor和ToString等方法的大量时间。对于这些方法,它能够在编期间自动帮我们生成这些方法,并没有如反射那样降低程序的性能。

9.2、使用步骤

1、先在idea中安装Lombok插件,安装完后需要重启idea

File —> Settings —> Plugins —> Browse repositories —> 搜索lombok

2、在pom.xml中导入依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
</dependency>

3、在实体类上添加相关注解

9.3、常用注解

  • @Data:无参构造,get,set,tostring,hashcode,equals
  • @AllArgsConstructor, @NoArgsConstructor:全参,无参
  • @Getter and @Setter
  • @EqualsAndHashCode
  • @ToString

常用注解解释:

  • @NonNull:用在方法参数前,会自动对该参数进行非空校验,为空抛出NPE(NullPointerException)
  • @Cleanup:自动管理资源,用在局部变量之前,在当前变量范围内即将执行完毕退出前会清理资源,生成try-finally的代码关闭流
  • @Getter/@Setter:用在属性上,不用自己手写setter和getter方法,还可指定访问范围
  • @ToString:用在类上,可以自动覆写toString方法
  • @EqualsAndHashCode:用在类上,自动生成equals方法和hashCode方法
  • @NoArgsConstructor,@RequiredArgsConstructor and @AllArgsConstructor:用在类上,自动生成无参构造和使用所有参数的有参构造函数。
  • @Data:用在类上,相当于@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstrutor这些注解的总和,对POJO类十分有用
  • @Value:用在类上,是@Data的不可变形式,相当于为属性添加final声明,只提供getter方法,而不提供setter方法
  • @SneakyThrows:自动抛受检异常,而无需显式在方法上使用throws语句
  • @Synchronized:用在方法上,将方法声明为同步的,并自动加锁
  • @Getter(lazy=true):可以替代经典的Double Check Lock样板代码

9.4、Lombok的优缺点

优点:

  1. 能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,提高了一定的开发效率。
  2. 让代码变得简洁,不用过多的去关注相应的方法。
  3. 属性做修改时,也简化了维护为这些属性所生成的getter/setter方法等。

缺点:

  1. 不支持多种参数构造器的重载。
  2. 虽然省去了手动创建getter/setter方法的麻烦,但大大降低了源代码的可读性和完整性,降低了阅读源代码的舒适度。

10、多对一的处理

多对一的理解:

  • 多个学生对应一个老师
  • 对于学生来说,就是多对一的现象,关联,即多个学生关联一个老师【多对一】
  • 对于老师来说,就是一对多的现象,集合,即一个老师有很多学生【一对多】

10.1、环境搭建

1、数据库sql

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');

image-20220713191554020

测试环境搭建:

  1. 导入lombok

  2. 新建实体类Student,Teacher

    @Data
    public class Student {
        private int id;
        private String name;
        private Teacher teacher;
    }
    
    @Data
    public class Teacher {
        private int id;
        private String name;
    }
    
  3. 建立Mapper接口

  4. 建立Mapper.XML文件

  5. 在核心配置文件中绑定注册我们的Mapper接口或者文件!【方式很多,随心选】

  6. 测试查询!

10.2、按照查询嵌套处理

<!--
思路:
	1.查询所有的学生信息
	2.根据查询出来的学生的tid,寻找对应的老师 子查询
-->
<resultMap id="StudentTeacher" type="student">
    <result property="id" column="id" />
    <result property="name" column="name" />
    <!--复杂的属性,我们需要单独处理 对象:association 集合:collection-->
    <!--association关联属性 property属性名 javaType属性类型 column在多的一方的表中的列名-->
    <association property="teacher" column="tid" javaType="teacher" select="getTeacher"  />
</resultMap>

<select id="getStudent"  resultMap="StudentTeacher">
    select * from student
</select>

<select id="getTeacher" resultType="teacher">
    select * from teacher where id = #{id}
</select>

10.3、按照结果嵌套处理

<!--按照结果嵌套处理,结果集映射-->
<select id="getStudent2" resultMap="StudentTeacher2">
    select s.id sid, s.name sname, t.name tname
    from student s,teacher t
    where s.tid = t.id
</select>

<resultMap id="StudentTeacher2" type="student">
    <id property="id" column="sid"/>
    <result property="name" column="sname" />
    <association property="teacher" javaType="teacher">
        <result property="name" column="tname" />
    </association>
</resultMap>

10.4、总结

在Mysql数据库中多对一查询方式:

  • 子查询
  • 联表查询

总结:

  • 按照查询进行嵌套处理就像SQL中的子查询
  • 按照结果进行嵌套处理就像SQL中的联表查询

11、一对多的处理

一对多的理解:

  • 一个老师拥有多个学生
  • 如果对于老师这边,就是一个一对多的现象,即从一个老师下面拥有一群学生(集合)!

11.1、环境搭建

和多对一环境一样,只是实体类有不同

@Data
public class Student {
    private int id;
    private String name;
    private int uid;
}

@Data
public class Teacher {
    private int id;
    private String name;
    private List<Student> students;
}

11.2、按照查询嵌套处理

<select id="getTeacherById2" resultMap="TeacherStudent2">
    select * from teacher where id = #{id}
</select>

<resultMap id="TeacherStudent2" type="teacher">
    <id property="id" column="id"/>
    <collection property="students" javaType="ArrayList" ofType="teacher" column="id" select="getStudent"/>
</resultMap>

<select id="getStudent" resultType="student">
    select * from student where tid = #{tid}
</select>

11.3、按照结果嵌套处理

<!--按结果嵌套查询-->
<resultMap id="TeacherStudent" type="teacher">
    <id property="id" column="tid" />
    <result property="name" column="tname"/>
    <!--复杂的属性,我们需要单独处理,对象:association 集合:collection
        javaType:指定属性的类型
        集合中的泛型信息,我们需要使用ofType获取
        -->
    <collection property="students" ofType="student">
        <id property="id" column="sid" />
        <result property="name" column="sname"/>
    </collection>
</resultMap>

<select id="getTeacherById" resultMap="TeacherStudent">
    select t.id tid, t.name tname, s.id sid, s.name sname
    from teacher t, student s
    where t.id = s.tid and t.id = #{id}
</select>

11.4、总结

  1. 关联 - association 【多对一】

  2. 集合 - collection 【一对多】

  3. JavaType 和 ofType 都是用来指定对象类型的

    • JavaType 是用来指定实体类中属性的类型

    • ofType 指定的是映射到List集合属性中pojo的类型,泛型中的约束类型。

注意点:

  1. 保证SQL的可读性,尽量通俗易懂
  2. 根据实际要求,尽量编写性能更高的SQL语句
  3. 注意属性名和字段不一致的问题
  4. 注意一对多和多对一中:属性名和字段对应的问题
  5. 尽量使用Log4j,通过日志来查看自己的错误

面试高频:

  • Mysql引擎
  • InnoDB底层原理
  • 索引
  • 索引优化

12、动态Sql

官网介绍:动态 SQL

12.1、介绍

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

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

if
choose (when, otherwise)
trim (where, set)
foreach

12.2、搭建环境

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

创建一个基础工程

  1. 导包

  2. 编写配置文件

  3. 编写实体类

    @Data
    public class Blog {
        private String id;
        private String title;
        private String author;
        private Date createTime; //属性名和字段名不一致
        private int views;
    }
    
  4. 编写实体类对应Mapper接口和Mapper.XML文件

  5. 编写测试类并插入几条数据

12.3、IF

<!--需求1:
    根据作者名字和博客名字来查询博客!
    如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询
    select * from blog where title = #{title} and author = #{author}
-->

<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from blog where 1=1
    <if test="title != null">
        and title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</select>

12.4、choose (when, otherwise)

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

<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>

12.5、trim (where, set)

where

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

<select id="queryBlogWhere" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <if test="title != null">
            and title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </where>
</select>

set

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

set标签会主动删除句末的逗号

<update id="updateBlog" parameterType="blog">
    update blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author}
        </if>
    </set>
    where id = #{id};
</update>

12.6、foreach

动态SQL的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建IN条件语句的时候。

<!--
    select * from blog where 1=1 and (id = 1 or id = 2 or id = 3)
    parameterType;里放的是map,map里面存一个list对象,foreach里面传的是list
    collection:指定输入对象中的集合属性
    item:每次遍历生成的对象
    open:开始遍历时的拼接字符串
    close:结束时拼接的字符串
    separator:遍历对象之间需要拼接的字符串
-->
<select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <foreach collection="ids" item="id" open="(" close=")" separator="or">
            id = #{id}
        </foreach>
    </where>
</select>

12.7、sql片段

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

提取SQL片段:

<sql id="if-title-author" >
    <where>
            <if test="title != null">
                and title = #{title}
            </if>
            <if test="author != null">
                and author = #{author}
            </if>
        </where>
</sql>

引用SQL片段:

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

注意:

  1. 最好基于单表来定义 sql 片段,提高片段的可重用性
  2. 在 sql 片段中不要包括 where标签

12.8、总结

  • 动态 sql 语句的编写往往就是一个拼接的问题
  • 为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。
  • 多在实践中使用才是熟练掌握它的技巧

13、缓存(了解)

13.1、简介

  1. 什么是缓存 [ Cache ]?
    • 存在内存中的临时数据。
    • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
  2. 为什么使用缓存?
    • 减少和数据库的交互次数,减少系统开销,提高系统效率。
  3. 什么样的数据能使用缓存?
    • 经常查询并且不经常改变的数据。【可以使用缓存】

13.2、Mybatis缓存

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

13.3、一级缓存

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

13.3.1、测试步骤

  1. 开启日志,方便查看结果

  2. 测试在一个sqlSession中查询两次相同的记录

    @Test
        public void testQueryUserById(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
    
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user1 = mapper.queryUserById(1);
            System.out.println(user1.toString());
            System.out.println("=============");
            User user2 = mapper.queryUserById(1);
            System.out.println(user2.toString());
            
            System.out.println(user1 == user2);
            sqlSession.close();
        }
    
  3. 查看日志输出

    image-20220715101254389

13.3.2、缓存失效的情况

sqlSession不同

  • 每个sqlSession中的缓存相互独立

sqlSession相同:

  • 查询不同的东西
  • 增删改操作,可能改变原来的东西,所以必定会刷新缓存
  • 查询不同的Mapper
  • 手动清理缓存 sqlSession.clearCache();

13.3.3、总结

  • 一级缓存默认是开启的,关闭不了。

  • 一级缓存只在一次sqlSession中有效,也就是拿到连接到关闭连接这个区间段。

  • 增删改操作会刷新一级缓存

  • 手动清理缓存 sqlSession.clearCache();

  • 一级缓存就是一个map

13.4、二级缓存(常用)

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

官网文档:mybatis缓存

13.4.1、使用步骤

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

    <!-- 开启全局缓存,默认是开启了的 -->
    <setting name="cacheEnabled" value="true"/>
    
  2. 每个mapper.xml中配置使用二级缓存

    <cache/>
    <!-- 官方示例 -->
    <cache
        eviction="FIFO"
        flushInterval="60000"
        size="512"
        readOnly="true"/>
    <!-- 
    这个更高级的配置创建了一个 FIFO(先进先出)缓存策略,每隔 60 秒刷新,最多可以存储结果对象或列表的512个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。 
    可以在<select>标签里面加上useCache="false"属性,该查询就不使用缓存了
    -->
    
    

    可用的清除策略有:

    • LRU – 最近最少使用:移除最长时间不被使用的对象。
    • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
    • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
    • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。

    默认的清除策略是 LRU。

    • flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
    • size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
    • readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
    • 提示 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。
  3. 代码测试

    • 所有的实体类先实现序列化接口,否则会报错(没有策略的情况下)
      Error serializing object. Cause: java.io.NotSerializableException: com.yzh.pojo.User
    • 解决:在相关的实体类上实现 implements Serializable 接口

13.4.2、总结

  • 只要开启了二级缓存,在同一个Mapper下有效
  • 所有的数据都会先放在一级缓存中
  • 只有当会话提交,或者关闭的时候,才会提交到二级缓存中!

13.5、缓存原理

image-20220715111525306

找缓存的顺序:

  1. 二级缓存
  2. 一级缓存
  3. 数据库

13.6、自定义缓存-ehcache

第三方缓存实现--EhCache

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

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

    <dependency>
        <groupId>org.mybatis.caches</groupId>
        <artifactId>mybatis-ehcache</artifactId>
        <version>1.1.0</version>
    </dependency>
    
  • 在mapper.xml中使用对应的缓存即可

    <cache type = “org.mybatis.caches.ehcache.EhcacheCache” />
    
  • 编写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>
    

14、29道练习题题目

14.1、smbms.sql

CREATE DATABASE `smbms` CHARACTER SET utf8 COLLATE utf8_general_ci;

USE `smbms`;
-- ----------------------------
-- Table structure for smbms_address
-- ----------------------------
DROP TABLE IF EXISTS `smbms_address`;
CREATE TABLE `smbms_address` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `contact` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '联系人姓名',
  `addressDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '收货地址明细',
  `postCode` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '邮编',
  `tel` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '联系人电话',
  `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者',
  `creationDate` datetime DEFAULT NULL COMMENT '创建时间',
  `modifyBy` bigint(20) DEFAULT NULL COMMENT '修改者',
  `modifyDate` datetime DEFAULT NULL COMMENT '修改时间',
  `userId` bigint(20) DEFAULT NULL COMMENT '用户ID',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

-- ----------------------------
-- Records of smbms_address
-- ----------------------------
INSERT INTO `smbms_address` VALUES ('1', '王丽', '北京市东城区东交民巷44号', '100010', '13678789999', '1', '2016-04-13 00:00:00', null, null, '1');
INSERT INTO `smbms_address` VALUES ('2', '张红丽', '北京市海淀区丹棱街3号', '100000', '18567672312', '1', '2016-04-13 00:00:00', null, null, '1');
INSERT INTO `smbms_address` VALUES ('3', '任志强', '北京市东城区美术馆后街23号', '100021', '13387906742', '1', '2016-04-13 00:00:00', null, null, '1');
INSERT INTO `smbms_address` VALUES ('4', '曹颖', '北京市朝阳区朝阳门南大街14号', '100053', '13568902323', '1', '2016-04-13 00:00:00', null, null, '2');
INSERT INTO `smbms_address` VALUES ('5', '李慧', '北京市西城区三里河路南三巷3号', '100032', '18032356666', '1', '2016-04-13 00:00:00', null, null, '3');
INSERT INTO `smbms_address` VALUES ('6', '王国强', '北京市顺义区高丽营镇金马工业区18号', '100061', '13787882222', '1', '2016-04-13 00:00:00', null, null, '3');

-- ----------------------------
-- Table structure for smbms_bill
-- ----------------------------
DROP TABLE IF EXISTS `smbms_bill`;
CREATE TABLE `smbms_bill` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `billCode` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '账单编码',
  `productName` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '商品名称',
  `productDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '商品描述',
  `productUnit` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '商品单位',
  `productCount` decimal(20,2) DEFAULT NULL COMMENT '商品数量',
  `totalPrice` decimal(20,2) DEFAULT NULL COMMENT '商品总额',
  `isPayment` int(10) DEFAULT NULL COMMENT '是否支付(1:未支付 2:已支付)',
  `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)',
  `creationDate` datetime DEFAULT NULL COMMENT '创建时间',
  `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)',
  `modifyDate` datetime DEFAULT NULL COMMENT '更新时间',
  `providerId` int(20) DEFAULT NULL COMMENT '供应商ID',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

-- ----------------------------
-- Records of smbms_bill
-- ----------------------------
INSERT INTO `smbms_bill` VALUES ('1', 'BILL2016_001', '洗发水、护发素', '日用品-洗发、护发', '瓶', '500.00', '25000.00', '2', '1', '2014-12-14 13:02:03', '15', '2019-04-16 21:43:12', '13');
INSERT INTO `smbms_bill` VALUES ('2', 'BILL2016_002', '香皂、肥皂、药皂', '日用品-皂类', '块', '1000.00', '10000.00', '2', '1', '2016-03-23 04:20:40', null, null, '13');
INSERT INTO `smbms_bill` VALUES ('3', 'BILL2016_003', '大豆油', '食品-食用油', '斤', '300.00', '5890.00', '2', '1', '2014-12-14 13:02:03', null, null, '6');
INSERT INTO `smbms_bill` VALUES ('4', 'BILL2016_004', '橄榄油', '食品-进口食用油', '斤', '200.00', '9800.00', '2', '1', '2013-10-10 03:12:13', null, null, '7');
INSERT INTO `smbms_bill` VALUES ('5', 'BILL2016_005', '洗洁精', '日用品-厨房清洁', '瓶', '500.00', '7000.00', '2', '1', '2014-12-14 13:02:03', null, null, '9');
INSERT INTO `smbms_bill` VALUES ('6', 'BILL2016_006', '美国大杏仁', '食品-坚果', '袋', '300.00', '5000.00', '2', '1', '2016-04-14 06:08:09', null, null, '4');
INSERT INTO `smbms_bill` VALUES ('7', 'BILL2016_007', '沐浴液、精油', '日用品-沐浴类', '瓶', '500.00', '23000.00', '1', '1', '2016-07-22 10:10:22', null, null, '14');
INSERT INTO `smbms_bill` VALUES ('8', 'BILL2016_008', '不锈钢盘碗', '日用品-厨房用具', '个', '600.00', '6000.00', '2', '1', '2016-04-14 05:12:13', null, null, '14');
INSERT INTO `smbms_bill` VALUES ('9', 'BILL2016_009', '塑料杯', '日用品-杯子', '个', '350.00', '1750.00', '2', '1', '2016-02-04 11:40:20', null, null, '14');
INSERT INTO `smbms_bill` VALUES ('10', 'BILL2016_010', '豆瓣酱', '食品-调料', '瓶', '200.00', '2000.00', '2', '1', '2013-10-29 05:07:03', null, null, '8');
INSERT INTO `smbms_bill` VALUES ('11', 'BILL2016_011', '海之蓝', '饮料-国酒', '瓶', '50.00', '10000.00', '1', '1', '2016-04-14 16:16:00', null, null, '1');
INSERT INTO `smbms_bill` VALUES ('12', 'BILL2016_012', '芝华士', '饮料-洋酒', '瓶', '20.00', '6000.00', '1', '1', '2016-09-09 17:00:00', null, null, '1');
INSERT INTO `smbms_bill` VALUES ('13', 'BILL2016_013', '长城红葡萄酒', '饮料-红酒', '瓶', '60.00', '800.00', '2', '1', '2016-11-14 15:23:00', null, null, '1');
INSERT INTO `smbms_bill` VALUES ('14', 'BILL2016_014', '泰国香米', '食品-大米', '斤', '400.00', '5000.00', '2', '1', '2016-10-09 15:20:00', null, null, '3');
INSERT INTO `smbms_bill` VALUES ('15', 'BILL2016_015', '东北大米', '食品-大米', '斤', '600.00', '4000.00', '2', '1', '2016-11-14 14:00:00', null, null, '3');
INSERT INTO `smbms_bill` VALUES ('16', 'BILL2016_016', '可口可乐', '饮料', '瓶', '2000.00', '6000.00', '2', '1', '2012-03-27 13:03:01', null, null, '2');
INSERT INTO `smbms_bill` VALUES ('17', 'BILL2016_017', '脉动', '饮料', '瓶', '1500.00', '4500.00', '2', '1', '2016-05-10 12:00:00', null, null, '2');

-- ----------------------------
-- Table structure for smbms_provider
-- ----------------------------
DROP TABLE IF EXISTS `smbms_provider`;
CREATE TABLE `smbms_provider` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `proCode` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商编码',
  `proName` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商名称',
  `proDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商详细描述',
  `proContact` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商联系人',
  `proPhone` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '联系电话',
  `proAddress` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '地址',
  `proFax` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '传真',
  `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)',
  `creationDate` datetime DEFAULT NULL COMMENT '创建时间',
  `modifyDate` datetime DEFAULT NULL COMMENT '更新时间',
  `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

-- ----------------------------
-- Records of smbms_provider
-- ----------------------------
INSERT INTO `smbms_provider` VALUES ('1', 'BJ_GYS001', '北京三木堂商贸有限公司', '长期合作伙伴,主营产品:茅台、五粮液、郎酒、酒鬼酒、泸州老窖、赖茅酒、法国红酒等', '张国强', '13566669999', '北京市丰台区育芳园北路', '010-58858787', '1', '2013-03-21 16:52:07', '2019-04-12 16:44:03', '10');
INSERT INTO `smbms_provider` VALUES ('2', 'HB_GYS001', '石家庄帅益食品贸易有限公司', '长期合作伙伴,主营产品:饮料、水饮料、植物蛋白饮料、休闲食品、果汁饮料、功能饮料等', '王军', '13309094212', '河北省石家庄新华区', '0311-67738876', '1', '2016-04-13 04:20:40', null, null);
INSERT INTO `smbms_provider` VALUES ('3', 'GZ_GYS001', '深圳市泰香米业有限公司', '初次合作伙伴,主营产品:良记金轮米,龙轮香米等', '郑程瀚', '13402013312', '广东省深圳市福田区深南大道6006华丰大厦', '0755-67776212', '1', '2014-03-21 16:56:07', null, null);
INSERT INTO `smbms_provider` VALUES ('4', 'GZ_GYS002', '深圳市喜来客商贸有限公司', '长期合作伙伴,主营产品:坚果炒货.果脯蜜饯.天然花茶.营养豆豆.特色美食.进口食品.海味零食.肉脯肉', '林妮', '18599897645', '广东省深圳市福龙工业区B2栋3楼西', '0755-67772341', '1', '2013-03-22 16:52:07', null, null);
INSERT INTO `smbms_provider` VALUES ('5', 'JS_GYS001', '兴化佳美调味品厂', '长期合作伙伴,主营产品:天然香辛料、鸡精、复合调味料', '徐国洋', '13754444221', '江苏省兴化市林湖工业区', '0523-21299098', '1', '2015-11-22 16:52:07', null, null);
INSERT INTO `smbms_provider` VALUES ('6', 'BJ_GYS002', '北京纳福尔食用油有限公司', '长期合作伙伴,主营产品:山茶油、大豆油、花生油、橄榄油等', '马莺', '13422235678', '北京市朝阳区珠江帝景1号楼', '010-588634233', '1', '2012-03-21 17:52:07', null, null);
INSERT INTO `smbms_provider` VALUES ('7', 'BJ_GYS003', '北京国粮食用油有限公司', '初次合作伙伴,主营产品:花生油、大豆油、小磨油等', '王驰', '13344441135', '北京大兴青云店开发区', '010-588134111', '1', '2016-04-13 00:00:00', null, null);
INSERT INTO `smbms_provider` VALUES ('8', 'ZJ_GYS001', '慈溪市广和绿色食品厂', '长期合作伙伴,主营产品:豆瓣酱、黄豆酱、甜面酱,辣椒,大蒜等农产品', '薛圣丹', '18099953223', '浙江省宁波市慈溪周巷小安村', '0574-34449090', '1', '2013-11-21 06:02:07', null, null);
INSERT INTO `smbms_provider` VALUES ('9', 'GX_GYS001', '优百商贸有限公司', '长期合作伙伴,主营产品:日化产品', '李立国', '13323566543', '广西南宁市秀厢大道42-1号', '0771-98861134', '1', '2013-03-21 19:52:07', null, null);
INSERT INTO `smbms_provider` VALUES ('10', 'JS_GYS002', '南京火头军信息技术有限公司', '长期合作伙伴,主营产品:不锈钢厨具等', '陈女士', '13098992113', '江苏省南京市浦口区浦口大道1号新城总部大厦A座903室', '025-86223345', '1', '2013-03-25 16:52:07', null, null);
INSERT INTO `smbms_provider` VALUES ('11', 'GZ_GYS003', '广州市白云区美星五金制品厂', '长期合作伙伴,主营产品:海绵床垫、坐垫、靠垫、海绵枕头、头枕等', '梁天', '13562276775', '广州市白云区钟落潭镇福龙路20号', '020-85542231', '1', '2016-12-21 06:12:17', null, null);
INSERT INTO `smbms_provider` VALUES ('12', 'BJ_GYS004', '北京隆盛日化科技', '长期合作伙伴,主营产品:日化环保清洗剂,家居洗涤专卖、洗涤用品网、墙体除霉剂、墙面霉菌清除剂等', '孙欣', '13689865678', '北京市大兴区旧宫', '010-35576786', '1', '2014-11-21 12:51:11', null, null);
INSERT INTO `smbms_provider` VALUES ('13', 'SD_GYS001', '山东豪克华光联合发展有限公司', '长期合作伙伴,主营产品:洗衣皂、洗衣粉、洗衣液、洗洁精、消杀类、香皂等', '吴洪转', '13245468787', '山东济阳济北工业区仁和街21号', '0531-53362445', '1', '2015-01-28 10:52:07', null, null);

-- ----------------------------
-- Table structure for smbms_role
-- ----------------------------
DROP TABLE IF EXISTS `smbms_role`;
CREATE TABLE `smbms_role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `roleCode` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '角色编码',
  `roleName` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '角色名称',
  `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者',
  `creationDate` datetime DEFAULT NULL COMMENT '创建时间',
  `modifyBy` bigint(20) DEFAULT NULL COMMENT '修改者',
  `modifyDate` datetime DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

-- ----------------------------
-- Records of smbms_role
-- ----------------------------
INSERT INTO `smbms_role` VALUES ('1', 'SMBMS_ADMIN', '系统管理员', '1', '2016-04-13 00:00:00', null, null);
INSERT INTO `smbms_role` VALUES ('2', 'SMBMS_MANAGER', '经理', '1', '2016-04-13 00:00:00', null, null);
INSERT INTO `smbms_role` VALUES ('3', 'SMBMS_EMPLOYEE', '普通员工', '1', '2016-04-13 00:00:00', null, null);

-- ----------------------------
-- Table structure for smbms_user
-- ----------------------------
DROP TABLE IF EXISTS `smbms_user`;
CREATE TABLE `smbms_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `userCode` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '用户编码',
  `userName` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '用户名称',
  `userPassword` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '用户密码',
  `gender` int(10) DEFAULT NULL COMMENT '性别(1:女、 2:男)',
  `birthday` date DEFAULT NULL COMMENT '出生日期',
  `phone` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '手机',
  `address` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '地址',
  `userRole` int(10) DEFAULT NULL COMMENT '用户角色(取自角色表-角色id)',
  `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)',
  `creationDate` datetime DEFAULT NULL COMMENT '创建时间',
  `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)',
  `modifyDate` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

-- ----------------------------
-- Records of smbms_user
-- ----------------------------
INSERT INTO `smbms_user` VALUES ('1', 'wen', '系统管理员', '123', '1', '1997-01-01', '15200981234', '湖南省衡阳市蒸湘区南华大学', '1', '1', '2019-04-07 10:15:55', null, null);
INSERT INTO `smbms_user` VALUES ('5', 'hanlubiao', '韩路彪', '0000000', '2', '1984-06-05', '18567542321', '北京市朝阳区北辰中心12号', '2', '1', '2014-12-31 19:52:09', null, null);
INSERT INTO `smbms_user` VALUES ('6', 'zhanghua', '张华', '0000000', '1', '1983-06-15', '13544561111', '北京市海淀区学院路61号', '3', '1', '2013-02-11 10:51:17', null, null);
INSERT INTO `smbms_user` VALUES ('7', 'wangyang', '王洋', '0000000', '2', '1982-12-31', '13444561124', '北京市海淀区西二旗辉煌国际16层', '3', '1', '2014-06-11 19:09:07', null, null);
INSERT INTO `smbms_user` VALUES ('8', 'zhaoyan', '赵燕', '0000000', '1', '1986-03-07', '18098764545', '北京市海淀区回龙观小区10号楼', '3', '1', '2016-04-21 13:54:07', null, null);
INSERT INTO `smbms_user` VALUES ('10', 'sunlei', '孙磊', '0000000', '2', '1981-01-04', '13387676765', '北京市朝阳区管庄新月小区12楼', '3', '1', '2015-05-06 10:52:07', null, null);
INSERT INTO `smbms_user` VALUES ('11', 'sunxing', '孙兴', '0000000', '2', '1978-03-12', '13367890900', '北京市朝阳区建国门南大街10号', '3', '1', '2016-11-09 16:51:17', null, null);
INSERT INTO `smbms_user` VALUES ('12', 'zhangchen', '张晨', '0000000', '1', '1986-03-28', '18098765434', '朝阳区管庄路口北柏林爱乐三期13号楼', '3', '1', '2016-08-09 05:52:37', '1', '2016-04-14 14:15:36');
INSERT INTO `smbms_user` VALUES ('13', 'dengchao', '邓超', '0000000', '2', '1981-11-04', '13689674534', '北京市海淀区北航家属院10号楼', '3', '1', '2016-07-11 08:02:47', null, null);
INSERT INTO `smbms_user` VALUES ('14', 'yangguo', '杨过', '0000000', '2', '1980-01-01', '13388886623', '北京市朝阳区北苑家园茉莉园20号楼', '3', '1', '2015-02-01 03:52:07', null, null);
INSERT INTO `smbms_user` VALUES ('15', 'test', 'test', '111', '1', '2019-04-16', '123456789', '南华大学', '1', '1', '2019-04-16 19:52:37', null, null);

14.2、dao层接口

public interface BillMapper {
    //根据供应商ID查询订单数量
    public int getBillCountByProviderId(@Param("providerId") Integer providerId);
    //增加订单
    public int add(Bill bill);
     //通过查询条件获取供应商列表-getBillList
    public List<Bill> getBillList(@Param("productName") String productName, @Param("providerId") Integer providerId, @Param("isPayment") Integer isPayment, @Param("from") Integer currentPageNo, @Param("pageSize") Integer pageSize);
    //通过条件查询-订单表记录数
    public int getBillCount(@Param("productName") String productName, @Param("providerId") Integer providerId, @Param("isPayment") Integer isPayment);
    //通过delId删除Bill
    public int deleteBillById(@Param("id") Integer delId);
    //通过billId获取Bill
    public Bill getBillById(@Param("id") Integer id);
    //修改订单信息
    public int modify(Bill bill);
    //根据供应商上ID删除订单信息
    public int deleteBillByProviderId(@Param("providerId") Integer providerId);
}

public interface RoleMapper {
    //获取角色列表
    public List<Role> getRoleList();
    //增加角色信息
    public int add(Role role);
    //通过ID删除role
    public int deleteRoleById(@Param("id") Integer delId);
    //修改角色信息
    public int modify(Role role);
    //通过Id获取role
    public Role getRoleById(@Param("id") Integer id);
    //根据RoleCode,进行角色编码得唯一性验证
    public int roleCodeIsExist(@Param("roleCode") String roleCode);
}

public interface ProviderMapper {
    
    //通过条件查询供应商列表
    List<Provider> getProviderList(@Param("proCode")String proCode, @Param("proName")String proName, @Param("from") Integer currentPageNo, @Param("pageSize")Integer pageSize);
    //增加用户信息
     int add(Provider provider);
    //获得供应商list
    List<Provider> getList();
    //通过条件查询供应商表记录数
    int getProCount(@Param("proName") String proName,@Param("proCode")String proCode);
    //通过id删除信息
    int deleteProById(@Param("id") Integer delId);
    //根据provider id获取供应商信息
    Provider getProById(@Param("id") Integer id);
    //修改供应商
    int modify(Provider provider);
}

public interface UserMapper {
    //通过userCode获取user
    public User getLoginUser(@Param("userCode") String userCode);
    //增加用户信息
    public int add(User user);
    //通过条件查询 userList
    public List<User> getUserList(@Param("userName") String userName, @Param("userRole") Integer userRole, @Param("from") Integer currentPageNo, @Param("pageSize") Integer pageSize);
    //通过条件查询-用户表记录数
    public int getUserCount(@Param("userName") String userName,@Param("userRole") Integer userRole);
    //通过userId删除user
    public int deleteUserById(@Param("id") Integer delId);
    //通过userId获取user
    public User getUserById(@Param("id") Integer id);
    //修改用户信息
    public int modify(User user);
    //修改当前用户密码
    public int updatePwd(@Param("id") Integer id,@Param("userPassword") String pwd);
}

END

posted @   lyluoye  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示