Mybatis

一、Mybatis简介

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

1.2持久化

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

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

  • JDBC就是一种持久化机制。文件IO也是一种持久化机制。

  • 在生活中 : 将鲜肉冷藏,吃的时候再解冻的方法也是。将水果做成罐头的方法也是。

为什么需要持久化服务呢?那是由于内存本身的缺陷引起的

  • 内存断电后数据会丢失,但有一些对象是无论如何都不能丢失的,比如银行账号等,遗憾的是,人们还无法保证内存永不掉电。

  • 内存过于昂贵,与硬盘、光盘等外存相比,内存的价格要高2~3个数量级,而且维持成本也高,至少需要一直供电吧。所以即使对象不需要永久保存,也会因为内存的容量限制不能一直呆在内存中,需要持久化来缓存到外存。

1.3 什么是持久层?

  • 完成持久化工作的代码块 . ----> dao层 【DAO (Data Access Object) 数据访问对象】
  • 大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。
  • 不过这里有一个字需要特别强调,也就是所谓的“层”。对于应用系统而言,数据持久功能大多是必不可少的组成部分。也就是说,我们的系统中,已经天然的具备了“持久层”概念?也许是,但也许实际情况并非如此。之所以要独立出一个“持久层”的概念,而不是“持久模块”,“持久单元”,也就意味着,我们的系统架构中,应该有一个相对独立的逻辑层面,专注于数据持久化逻辑的实现.
  • 与系统其他部分相对而言,这个层面应该具有一个较为清晰和严格的逻辑边界。【说白了就是用来操作数据库存在的!】

1.4 为什么需要Mybatis?

  • Mybatis就是帮助程序猿将数据存入数据库中 , 和从数据库中取数据 .
  • 传统的jdbc操作 , 有很多重复代码块 .比如 : 数据取出时的封装 , 数据库的建立连接等等… ,通过框架可以减少重复代码,提高开发效率 .
  • MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射
  • 所有的事情,不用Mybatis依旧可以做到,只是用了它,所有实现会更加简单!技术没有高低之分,只有使用这个技术的人有高低之别

1.5 Mybatis的优点?

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

二、Mybatis第一个程序

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

2.1搭建实验数据库

CREATE DATABASE `mybatis`;

USE `mybatis`;

DROP TABLE IF EXISTS `user`;

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`(`id`,`name`,`pwd`) values (1,'狂神','123456'),(2,'张三','abcdef'),(3,'李四','987654');

2.2导入Mybatis相关的jar包

在父工程中的pom文件中

<dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis</artifactId>
   <version>3.5.2</version>
</dependency>
<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.47</version>
</dependency>

2.3编写MyBatis核心配置文件

建立数据库连接
在这里插入图片描述
在这里插入图片描述
在resources文件下的mybatis-config.xml里

<?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核心配置文件-->
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
<!--                                                                                安全连接 and   使用Unicod编码   and   编码集utf-8        -->
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
<!--等把UserMapper.xml创建了再加入这个注册
    每个Mapper.xml文件都需要在Mybatis核心配置文件中注册-->
<!--    <mappers>-->
<!--        <mapper resource="com/yhn/dao/UserMapper.xml"/>-->
<!--    </mappers>-->
</configuration>

2.4编写MyBatis工具类

//sqlSessionFactory --> sqlSession
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
//           使用Mybatis第一步把资源加载进来获取sqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //sqlSessionFactory完全包含了面向数据库执行sql命令所需要的所有方法
    //获取SqlSession连接
    public static SqlSession getSession(){
        return sqlSessionFactory.openSession();
    }
}

2.5、创建实体类

com.yhn.pojo下的

public class User {
   
   private int id;  //id
   private String name;   //姓名
   private String pwd;   //密码
   
   //构造,有参,无参
   //set/get
   //toString()
   
}

2.6、编写Mapper接口类

com.yhn.dao包下UserMapper就是UserDao

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

2.7、编写Mapper.xml配置文件

namespace 十分重要,不能写错!
UserMapper.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">
<!--相当于UserMapper的实现类-->
<!--命名空间 namespace=绑定一个对应的Dao/Mapper接口,不像原来要用实现类实现它-->
<mapper namespace="com.yhn.dao.UserMapper">
<!--    id对应原来的方法名字   返回类型(集合里面的泛型)  -->
    <select id="getUserList" resultType="com.yhn.pojo.User">
  select * from mybatis.user
 </select>
</mapper>

写完这个配置文件一定要在MyBatis核心配置文件mybatis-config.xml下加上
注册

<!--    每个Mapper.xml文件都需要在Mybatis核心配置文件中注册-->
    <mappers>
        <mapper resource="com/yhn/dao/UserMapper.xml"/>
    </mappers>

2.8、编写测试类

测试包和实体包之间最好对应

public class UserMapperTest {
    @Test
    public void test() {
        SqlSession sqlSession = null;
        try {
            //第一步获取SqlSession对象
            sqlSession = MybatisUtils.getSession();
            //方式一:getMapper执行sql
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<User> userList = mapper.getUserList();
            for (User user : userList) {
                System.out.println(user);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭SqlSession
            sqlSession.close();
        }
    }
}

在这里插入图片描述

9、运行测试,成功的查询出来的我们的数据,ok!
可能出现问题说明:Maven静态资源过滤问题
最好在父工程和子过程的pro文件中都有加入这个

<!--解决我们写的配置文件无法被导出或者无法生效的问题
让Java和resources两个文件下的.xml和.properties配置文件
能够被导出,过滤开启-->
<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

三、CURD

3.1根据id查询用户

UserMapper

  //根据id查询用户
    User getUserById(int id);

UserMapper.xml

 <!--方法名一定要和接口里的一样   参数类型-->
    <select id="getUserById" parameterType="int" resultType="com.yhn.pojo.User">
        select * from mybatis.user where id = #{id}
    </select>

UserMapperTest

    @Test
    public void getUserById() {
        SqlSession sqlSession = MybatisUtils.getSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.getUserById(1);
        System.out.println(user);
        sqlSession.close();
    }

3.2插入一个用户

//插入一个用户
   int addUser(User user);

UserMapper.xml

<!--    对象中的属性可以直接取出来-->
    <insert id="addUser" parameterType="com.yhn.pojo.User" >
        insert into mybatis.user(id, name, pwd) value (#{id},#{name},#{pwd});
    </insert>

UserMapperTest

 @Test
    public void addUser() {
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        int res = mapper.addUser(new User(4, "fdfds", "12344"));
        if (res > 0) {
            System.out.println("插入成功");
        }
        //        插入数据需要添加事务
        session.commit();
        session.close();
    }

注意事项:
当User类里的密码写成pew和sql里面的pwd不一致时候,value里是user类的名称所以要和use类里保持一致
在mybatis-config.xml给dao包设置了别名,所以不用写全路径

<insert id="addUser" parameterType="user">  
        insert into  mybatis.user(id,name,pwd) value (#{id},#{name},#{pew});
    </insert>

3.3修改用户

 //修改用户
    int updateUser(User user);

UserMapper.xml

 <update id="updateUser" parameterType="com.yhn.pojo.User">
        update mybatis.user set name =#{name },pwd=#{pwd} where id =#{id};
    </update>
   @Test
    public void updateUser() {
        SqlSession session = MybatisUtils.getSession();

        UserMapper mapper = session.getMapper(UserMapper.class);

        mapper.updateUser(new User(3, "hhhhh", "123"));
//      修改数据要提交事务
        session.commit();
        session.close();
    }

3.4删除用户

 //    删除一个用户
    int deleteUser(int id);

UserMapper.xml

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

UserMapperTest

    @Test
    public void deleteUser() {

        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        mapper.deleteUser(2);
        //添加事务
        session.commit();
        session.close();
    }

注意点:增删改需要提交事务
在idea中连了数据库再写数据库名select * from mybatis.user where id = #{id}
没有连直接写表名user
错误:

  • 标签不要匹配错

  • resource绑定mapper,需要使用路径com/yhn/dao/UserMapper.xml

  • NullPointerException,需要注册到资源

  • maven中资源没有导出,maven 中加一段配置文件

3.5万能的map

假设我们的实体类,或者数据库中的表,字段或者参数过多,我们应该适当考虑使用Map

添加一个用户

//万能的map
    int addUser2(Map<String, Object> map);
<!--map里重要key和key对应-->
    <insert id="addUser2" parameterType="map" >
        insert into mybatis.user(id, name, pwd) value (#{userId},#{userName},#{passWord});
    </insert>

@Test
    public void testUser2() {
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        HashMap<String, Object> map = new HashMap<>();
        map.put("userId",5);
        map.put("userName", "eheloo");
        map.put("passWord", "12344");
        mapper.addUser2(map);
        session.close();
    }
    User getUserById2(Map<String, Object> map);

3.6模糊查询

 //模糊查询
    List<User> getUserLike(String value);
 <select id="getUserLike" resultType="com.yhn.pojo.User">
        select * from mybatis.user where  name like #{value};
    </select>
  @Test
    public void getUserLike() {
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> userLike = mapper.getUserLike("%杨%");
        for (User user : userLike) {
            System.out.println(user);
        }
        session.close();
    }

四、配置解析

4.1 原来的配置

<?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核心配置文件-->
<configuration>
<!--    default表示默认的环境使用id为development-->
    <environments default="development">
        <environment id="development">
            <!-- 默认的事务管理器是JDBC-->
            <transactionManager type="JDBC"/>
            <!-- 数据源 用来连接数据库 连接池:POOLED有池 这是一种使得并发web应用快速响应请求的流行处理方式-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
<!--                                                                                安全连接 and   使用Unicod编码   and   编码集utf-8        -->
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
<!--    每个Mapper.xml文件都需要在Mybatis核心配置文件中注册-->
    <mappers>
        <mapper resource="com/yhn/dao/UserMapper.xml"/>
    </mappers>
</configuration>


4.2优化

编写一个db.properties配置文件

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8
username=root
password=123456

在核心配置文件中引入

<?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核心配置文件-->
<configuration>
<!--属性    引入外部配置文件要写在最上面-->
    <properties resource="db.properties"/>

<!--    default表示默认的环境使用id为development-->
    <environments default="development">
        <environment id="development">
            <!-- 默认的事务管理器是JDBC-->
            <transactionManager type="JDBC"/>
            <!-- 数据源 用来连接数据库 连接池:POOLED有池 这是一种使得并发web应用快速响应请求的流行处理方式-->
            <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>
<!--    每个Mapper.xml文件都需要在Mybatis核心配置文件中注册-->
    <mappers>
        <mapper resource="com/yhn/dao/UserMapper.xml"/>
    </mappers>
</configuration>


可以增加一些属性,如果两个文件有同一个字段优先使用db.properties配置文件中的

<!--    引入外部配置文件要写在最上面-->
    <properties resource="db.properties">
        <property name="username" value="root"/>
        <property name="pwd" value="123456"/>
    </properties>    

4.3设置别名 typeAliases

1、给实体类起别名
mybatis-config.xml

<!--  可以给实体类别名,默认放在第三的位置,第二setting,要是放在最后会报错-->
    <typeAliases>
        <typeAlias type="com.yhn.pojo.User" alias="User"/>
    </typeAliases>

UserMapper.xml里返回结果直接写实体类的别名就行,不用写包路径了

<select id="getUserList" resultType="User">
      select * from mybatis.user
     </select>

2、扫描实体类的包,它的默认别名就为这个类的类名,首字母小写
mybatis-config.xml

 <typeAliases>
        <package name="com.yhn.pojo"/>
    </typeAliases>

UserMapper.xm

<select id="getUserList" resultType="user">
      select * from mybatis.user
     </select>

在实体类比较少使用第一种,实体类比较多建议使用第二种,但是第二种不支持自定义别名,如果非要改需要在实体类上通过注解修改别名

//实体类
@Alias("hello")
public class User {
    private int id;
    private String name;
    private String pwd;
<select id="getUserList" resultType="hello">
      select * from mybatis.user
     </select>

4.4映射器

MapperRegisty:注册绑定我们的Mapper文件
方式一:

<!--    每个Mapper.xml文件都需要在Mybatis核心配置文件中注册-->
    <mappers>
      <mapper resource="com/yhn/dao/UserMapper.xml"/>
    </mappers>

方式二

<!--    每个Mapper.xml文件都需要在Mybatis核心配置文件中注册-->
    <mappers>
        <mapper class="com.yhn.dao.UserMapper"/>
    </mappers>

注意点:
接口和它的配置文件必须同名
接口和它 的

五、解决属性名和字段名不一致的问题

数据库中的字段
在这里插入图片描述

//实体类
public class User {
    private int id;
    private String name;
    private String password;

解决方法一:起别名

 <!--方法名一定要和接口里的一样   参数类型-->
    <select id="getUserById" parameterType="int" resultType="User">
        select id,name,pwd as password from mybatis.user where id=#{id};
       <!-- select * from mybatis.user where id = #{id} -->
    </select>

2.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 mybatis.user where id = #{id}
    </select>

六、日志

6.1日志工厂

如果一个数据库操作,出现了异常,我们需要排错,日志就是最好的助手
曾经:sout,debug
现在:日志工厂

logImpl LOG4j【掌握】
STDOUT_LOGGING【掌握】
1、 STDOUT_LOGGING
在核心配置文件中配置,必须写在properties和typeAliases中间
mybatis-config.xml

<configuration>
<!-- 属性  引入外部配置文件要写在最上面-->
    <properties resource="db.properties"/>
<!--配置日志-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
<!--给包起别名。默认为类名小写-->
    <typeAliases>
        <package name="com.yhn.pojo"/>
    </typeAliases>
pening JDBC Connection    //打开数据库连接
Created connection 265119009.   //创建一个连接
Setting autocommit to false on JDBC Connection  [com.mysql.jdbc.JDBC4Connection@fcd6521]   //设置自动提交为false
==>  Preparing: select * from mybatis.user where id = ? 
==> Parameters: 1(Integer)   //参数值
<==    Columns: id, name, pwd   //查询的列是
<==        Row: 1, 杨浩闹, 12333  //查询出来的值
<==      Total: 1   //记录数
User{id=1, name='杨浩闹', password='12333'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@fcd6521]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@fcd6521]  //关闭数据库连接
Returned connection 265119009 to pool.  //返回连接到池中

2、LOG4j

  • Log4j是Apache的一个开源项目

  • 通过使用Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI组件…

  • 我们也可以控制每一条日志的输出格式;

  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
    使用步骤
    1、导包:
    pom.xml

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

2、配置文件的编写
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.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.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的使用

mybatis-config.xml

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

4、使用
先导包 import org.apache.log4j.Logger;
日志对象,参数为当前类的class

    static Logger logger = Logger.getLogger(UserMapperTest.class);

日志级别:

logger.info("info:加入了testLog4j");
        logger.debug("debug:加入了testLog4j");
        logger.error("error:加入了testLog4j");

七、分页

7.1使用limit分页

UserMapper

  List<User> getUserByLimit(Map<String, Integer> map);

UserMapper.xml

<!--    分页-->
<select id="getUserByLimit" parameterType="map" resultType="user">
    select * from mybatis.user limit #{startIndex},#{pageSize};
</select>
    @Test
    public void getUserByLimit() {
        SqlSession sqlSession = MybatisUtils.getSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        HashMap<String, Integer> map = new HashMap<>();
        map.put("startIndex", 0);
        map.put("pageSize", 3);

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

因为password和数据库中字段pwd不对应所以查不出来密码
可以使用结果集映射

<!--    结果集映射-->
    <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="getUserByLimit" parameterType="map" resultMap="UserMap">
        select * from mybatis.user limit #{startIndex},#{pageSize};
    </select>

7.2RowBounds分页

八、使用注解开发

8.1注解查询

注解在接口上实现

public interface UserMapper {
    @Select("select * from user")
    List<User> getUsers();
}

需要在核心配置文件中绑定接口


<!-- 绑定接口:使用接口class文件绑定注册:接口必须和他的Mapper配置文件同名,
接口和他的Mapper配置文件必须在同一包下(用注解就不要Mapper配置文件了)-->
<!--    绑定接口-->
    <mappers>
        <mapper class="com.yhn.dao.UserMapper"/>
    </mappers>
 @Test
    public void test() {
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> userList = mapper.getUsers();
        for (User user : userList) {
            System.out.println(user);
        }
        session.close();
    }

本质:反射机制实现
底层:动态代理

8.2Mybatis具体执行流程:

在这里插入图片描述

8.3注解增删改

1、MybatisUtils里设置自动提交事务为true,测试的时候就不要手动提交

 //获取SqlSession连接
    public static SqlSession getSession(){
        return sqlSessionFactory.openSession(true); //设置自动提交事务为true
    }

2、编写接口

@Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
    int addUser(User user);

    @Update("update user set name=#{name},pwd=#{password} where id=#{id}")
    int updateUser(User user);

    //没有@Param("uid"),默认就是int id 的id。@Param("uid")里面是什么名字,id =#{uid}就取什么名字
    @Delete("delete from user where id =#{uid}")
    int deleteUser(@Param("uid") int id);

3、测试

  @Test
    public void addUser() {
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        mapper.addUser(new User(5, "hellp", "12344"));
        session.close();
    }
    @Test
    public void updateUser() {
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        mapper.updateUser(new User(5, "to", "33233"));
        session.close();
    }
    @Test
    public void deleteUser() {
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        mapper.deleteUser(5);
        session.close();
    }

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

关于@Param()注解:
在方法只接受一个参数的情况下,可以不使用@Param。

在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。

如果参数是 JavaBean , 则不能使用@Param。

不使用@Param注解时,参数只能有一个,并且是Javabean。

8.4、Lombok

加一个@Data会自动生成方法get set tostring equls hascode

//实体类
@Data   //所有方法
@AllArgsConstructor  //有参
@NoArgsConstructor //无参
public class User {
    private int id;
    private String name;
    private String password;

}


也可以自己写补充

九、多对一

1、数据库

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

2、导入lombok

<dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>
    </dependencies>

3、新建实体类
com.yhn.dao

@Data
public class Student {
    private int id;
    private String name;
    //学生需要关联一个老师
    private Teacher teacher;
}
@Data
public class Teacher {
    private int id;
    private String name;
}

4、新建map接口

public interface TeacherMapper {
    @Select("select * from  teacher where id =#{tid}")
    Teacher getTeacher(@Param("tid") int id);
}
public interface StudentMapper {
}

5、建立Mapper.xml文件
StudentMapper.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">
<mapper namespace="com.yhn.dao.StudentMapper">

</mapper>

TeacherMapper.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">
<mapper namespace="com.yhn.dao.TeacherMapper">

</mapper>

6.在核心配置文件中绑定我们的Mapper接口或者文件
mybatis-config.xml

<?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核心配置文件-->
<configuration>
<!-- 属性  引入外部配置文件要写在最上面-->
    <properties resource="db.properties"/>
<!--配置日志-->
    <settings>
<!--        标准的日志工厂实现-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
<!--        <setting name="logImpl" value="Log4j"/>-->
    </settings>
<!--给包起别名。默认为类名小写-->
    <typeAliases>
        <package name="com.yhn.pojo"/>
    </typeAliases>

<!--    default表示默认的环境使用id为development-->
    <environments default="development">
        <environment id="development">
            <!-- 默认的事务管理器是JDBC-->
            <transactionManager type="JDBC"/>
            <!-- 数据源 用来连接数据库 连接池:POOLED有池 这是一种使得并发web应用快速响应请求的流行处理方式-->
            <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>

    <mappers>
        <mapper class="com.yhn.dao.TeacherMapper"/>
        <mapper class="com.yhn.dao.StudentMapper"/>
    </mappers>
</configuration>


7、测试
查询出来老师为null
8、按照查询嵌套处理
StudentMapper.xml

<!--
 思路:
    1、查询所有的学生信息
    2、根据查询出来的学生的tid寻找对应的老师
-->
    <select id="getStudent" resultMap="StudentTeacher">
    select * from student
    </select>
    <resultMap id="StudentTeacher" type="Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
<!--        复杂的=属性需要单独处理
             对象:association
             集合: collection
-->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    <select id="getTeacher" resultType="Teacher">
    <!--    会根据Teacher来推断id,所以tid写什么都不会影响结果-->
        select * from teacher where id=#{tid}
    </select>

按照结果查询

 List<Student> getStudent2();

StudentMapper.xml

<!--    按照结果嵌套处理 在resultMap中指定数据库中字段别名对应的实体类的属性名-->
    <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">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
             <result property="name" column="tname"/>
        </association>
    </resultMap>


 @Test
    public void testStudent() {
        SqlSession session = MybatisUtils.getSession();
        StudentMapper mapper = session.getMapper(StudentMapper.class);
        List<Student> studentList = mapper.getStudent2();
        for (Student student : studentList) {
            System.out.println(student);
        }
        session.close();
    }

结果:

==> Parameters: 
<==    Columns: sid, sname, tname
<==        Row: 1, 小明, 秦老师
<==        Row: 2, 小红, 秦老师
<==        Row: 3, 小张, 秦老师
<==        Row: 4, 小李, 秦老师
<==        Row: 5, 小王, 秦老师
<==      Total: 5
Student(id=1, name=小明, teacher=Teacher(id=0, name=秦老师))
Student(id=2, name=小红, teacher=Teacher(id=0, name=秦老师))
Student(id=3, name=小张, teacher=Teacher(id=0, name=秦老师))
Student(id=4, name=小李, teacher=Teacher(id=0, name=秦老师))
Student(id=5, name=小王, teacher=Teacher(id=0, name=秦老师))

十、一对多

1、创建实体类

@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;
}

public interface TeacherMapper {
    //获取老师
  List<Teacher> getTeacher();

2.配置xml
TeacherMapper.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">
<mapper namespace="com.yhn.dao.TeacherMapper">
     <select id="getTeacher" resultType="Teacher">
         select * from mybatis.teacher;
     </select>
</mapper>

测试结果:
Teacher(id=1, name=秦老师, students=null)

10.1按结果嵌套查询

 //获取指定老师下的所有学生和老师信息
    Teacher getTeacher(@Param("tid") int id);
<!--    按结果嵌套查询-->
     <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="id" column="tid"/>
        <result property="name" column="name"/>
<!--  复杂的=属性需要单独处理 对象:association 集合: collection
        javaType指定属性的类型
        集合指定泛型信息,我们使用ofType获取-->
       <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 test() {
        SqlSession session = MybatisUtils.getSession();
        Teacher teacher= session.getMapper(TeacherMapper.class).getTeacher(1);
        System.out.println(teacher);
        session.close();

    }

结果:
Teacher(id=1, name=null, students=[Student(id=1, name=小明, tid=1), Student(id=2, name=小红, tid=1), Student(id=3, name=小张, tid=1), Student(id=4, name=小李, tid=1), Student(id=5, name=小王, tid=1)])

10.2按查询嵌套处理

  Teacher getTeacher2(@Param("tid") int id);

TeacherMapper.xml

<select id="getTeacher2" resultMap="TeacherStudent2">
        select * from mybatis.teacher where id=#{tid}
    </select>
    <resultMap id="TeacherStudent2" type="Teacher">
        <collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
    </resultMap>

    <select id="getStudentByTeacherId" resultType="Student">
        select * from mybatis.student where tid = #{tid}
    </select>

小结:
1.关联-association[多对一]
2.集合-collection [一对多]
3.javaType & ofType
javaType用来指定实体类中属性的类型
ofType用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
private List students;
注意点:
保证sql的可读性,尽量通俗易懂
注意一对多和多对一中属性名和字段的问题
可以使用日志排查错误

十二、动态SQL

1、 介绍

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

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

2、 案例

2.1搭建环境
1、数据库

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

2、导包
lombok
3、编写配置文件

在这里插入代码片

3、编写配置文件
IDutil工具类

public class IDUtils {
    public static String getId() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

}

实体类的编写

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

4、编写Mapper接口及xml文件

public interface BlogMapper {
    //插入数据
    int addBlog(Blog blog);
}

BlogMapper.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">
<mapper namespace="com.yhn.dao.BlogMapper">
    <insert id="addBlog" parameterType="blog">
       insert into blog (id, title, author, create_time, views)
        values (#{id},#{title},#{author},#{createTime},#{views});
    </insert>
</mapper>

5、mybatis核心配置文件,下划线驼峰自动转换

 <settings>
<!--        标准的日志工厂实现-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
<!--        开启驼峰转换-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

5、测试 初始化博客方法

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

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

        mapper.addBlog(blog);

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

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

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

        session.close();
    }

3、if语句

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

  //查询博客
    List<Blog> queryBlogIF(Map map);

BlogMapper.xml

<select id="queryBlogIF" parameterType="map" resultType="blog">

        select * from mybatis.blog where 
        <if test="title != null">
        <!--从map里取titl的值-->
             title =#{title}
        </if>
        <if test="author !=null">
           and author = #{author}
        </if>
    </select>

测试

 @Test
    public void queryBlogIF() {
        //不加map就查询所有,有map就查询单个的
        SqlSession session = MybatisUtils.getSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
//        map.put("title", "sql");  l
        map.put("author", "狂神说");
        List<Blog> blogs = mapper.queryBlogIF(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
    }

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

4、where

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

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

5、choose

    List<Blog> queryBlogChoose(Map map);

 <select id="queryBlogChoose" parameterType="map" resultType="blog">
        select * from mybatis.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>
@Test
    public void queryBlogChoose() {
        //不加map就查询所有,有map就查询单个的
        SqlSession session = MybatisUtils.getSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
//        map.put("title", "sql");
//        map.put("author", "狂神说");
        map.put("views", 9999);
        List<Blog> blogs = mapper.queryBlogChoose(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
    }

6、set

set元素会动态前置SET关键词。如果title为null,set标签会在使用中把逗号,自动进行删除

 //更新博客
    int updateBlog(Map map);
<update id="updateBlog" parameterType="map">
        update mybatis.blog
        <set>
            <if test="title !=null">
                title = #{title},
            </if>
            <if test="author != null">
                author =#{author}
            </if>
        </set>
        where id = #{id}
    </update>
 @Test
    public void queryBlogChoose() {
        //不加map就查询所有,有map就查询单个的
        SqlSession session = MybatisUtils.getSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
        map.put("title", "Java如此简单2");
        map.put("author", "狂神说");
        map.put("id", "7b63381272ea49448db4eb0d686c53ee");
        mapper.updateBlog(map);
        session.close();
    }

update mybatis.blog SET title = ?, author =? where id = ?

所谓的动态SQL语句,本质还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码

7.sql片段

可以提取一个代码出来,用include引用,实现代码的复用

   <sql id="if-title-author">
        <if test="title != null">
            title =#{title}
        </if>
        <if test="author !=null">
            and author = #{author}
        </if>
    </sql>
    <select id="queryBlogIF" parameterType="map" resultType="blog">
        select * from mybatis.blog
        <where>
           <include refid="if-title-author"></include>
        </where>
    </select>

注意事项:最好基于单表来定义SQL片段
不要存在where标签

8、foreach

 //查询1-2-3号的博客
    List<Blog> queryBlogForeach(Map map);
<!-- select * from mybatis.blog where  1=1 and (id=1 or id =2 or id =3)
     我们现在传递一个万能的map,这个map可以存在一个集合 -->
    <select id="queryBlogForeach" parameterType="map" resultType="blog">
        select * from mybatis.blog
<!--  当里面的内容都不成立where自动省略,查询所有的blog-->
        <where>
            <!--        要传进来的id    遍历出来的id-->
                 <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                            id =#{id}
                 </foreach>
        </where>
    </select>
 @Test
    public void queryBlogForEach() {
        SqlSession session = MybatisUtils.getSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        HashMap hashMap = new HashMap();
        ArrayList<Integer> ids = new ArrayList<>();
        ids.add(1);
        ids.add(2);
        hashMap.put("ids", ids);

        List<Blog> blogs = mapper.queryBlogForeach(hashMap);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        session.close();
    }

动态sql就是在拼接sql语句,我们只要保证sql的正确性,按照sql的格式,去排列组合就可以了

面试高频:

  • Mysql引擎
  • InnoDb
  • 索引
  • 索引优化

十三、缓存

简介

1、什么是缓存 [ Cache ]?

存在内存中的临时数据。

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

2、为什么使用缓存?

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

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

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

4、Mybatis缓存

MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。

MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存

默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)

二级缓存需要手动开启和配置,他是基于namespace级别的缓存。

为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

1、一级缓存

一级缓存也叫本地缓存:

与数据库同一次会话期间查询到的数据会放在本地缓存中。

以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库

//根据id查询用户
User queryUserById(@Param("id") int id);
<select id="queryUserById" resultType="user">
  select * from user where id = #{id}
</select>
@Test
public 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();
}

1.1结果分析
在这里插入图片描述

1.1一级缓存失效的四种情况
一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;

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

1、sqlSession不同

@Test
public 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相同,查询条件不同

@Test
public 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);
<update id="updateUser" parameterType="map">
  update user set name = #{name} where id = #{id}
</update>
@Test
public 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相同,手动清除一级缓存

@Test
public 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

2、二级缓存

二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

基于namespace级别的缓存,一个名称空间,对应一个二级缓存;

工作机制

一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;

如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;

新的会话查询信息,就可以从二级缓存中获取内容;

不同的mapper查出的数据会放在自己对应的缓存(map)中;
1、开启全局缓存
mybatis-config.xml

    <settings>
<!--        显示的开启全局缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>

2、要使用的二级缓存的Mapper中开启

<!--    在当前Mapper.xml中开启二级缓存-->
    <cache />

也可以自定义参数

<!--    在当前Mapper.xml中开启二级缓存-->
    <cache
            eviction="FIFO"
            flushInterval="60000"
            size="512"
            readOnly="true"/>
            这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
@Test
    public void test() {
        SqlSession session = MybatisUtils.getSession();
        SqlSession session2 = MybatisUtils.getSession();

        UserMapper mapper = session.getMapper(UserMapper.class);
        UserMapper mapper2 = session2.getMapper(UserMapper.class);

        User user = mapper.queryUserById(2);
        System.out.println(user);
        session.close();//只有会话**提交或者关闭**以后,一级缓存中的数据才会转到二级缓存中
        System.out.println("=============");
        User user2 = mapper2.queryUserById(2);
        System.out.println(user2);
        System.out.println(user==user2);

        session2.close();
    }

只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据

查出的数据都会被默认先放在一级缓存中

只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中

错误:只用会报错,需要将实体类序列化

<!--    在当前Mapper.xml中开启二级缓存-->
    <cache />

将实体类序列化

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

结果:

Opening JDBC Connection
Created connection 1434041222.
==>  Preparing: select * from mybatis.user where id = ? 
==> Parameters: 2(Integer)
<==    Columns: id, name, pwd
<==        Row: 2, 沿海合法, 12444
<==      Total: 1
User(id=2, name=沿海合法, pwd=12444)
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5579bb86]
Returned connection 1434041222 to pool.
=============
Cache Hit Ratio [com.yhn.dao.UserMapper]: 0.5
User(id=2, name=沿海合法, pwd=12444)
true

3、缓存原理图

在这里插入图片描述
在这里插入图片描述

4、ehcache自定义缓存

第三方缓存实现–EhCache: 查看百度百科

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

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

<dependency>
   <groupId>org.mybatis.caches</groupId>
   <artifactId>mybatis-ehcache</artifactId>
   <version>1.1.0</version>
</dependency>

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

十四、测试

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String userCode;//用户编码
    private String userName;
    private String userPassword;
    private Date birthday;
    private Integer gender;//性别
    private String phone;
    private String address;  //地址
    private Integer userRole; //用户角色
    private Integer createdBy; //创建者
    private Date creationDate;//创建时间
    private Integer modifyBy;//更新者
    private Date modifyDate;//更新时间


}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Provider {
    private Integer id;
    private String proCode; //供应商编码
    private String proName;//供应商名称
    private String proDesc;//供应商描述
    private String proContact;//供应商联系人
    private String proPhone; //供应商电话
    private String proAddress;
    private String proFax;//供应商传真
    private Integer createBy; //创建者
    private Date creationDate;//创建时间
    private Integer modifyBy;//更新者
    private String modifyDate;//更新时间
}

编写mapper和配置文件

public interface UserMapper {
    //通过userCode获取User
    User getLoginUser(@Param("userCode") String userCode) throws Exception;

    //增加用户信息
    int add(User user) throws Exception;


    //通过条件查询-userList
    List<User> getUserList(@Param("userName") String userName, @Param("userRole") Integer userRole,
                           @Param("currentPageNo") Integer currentPageNo, @Param("pageSize") Integer pageSize) throws Exception;

    //通过userId删除user
    int deleteUserById(@Param("id") Integer delId) throws Exception;

    //通过userId获取user
    User getUserById(@Param("id") Integer id) throws Exception;

    //修改用户信息
    int modify(User user) throws Exception;

    //修改当前用户密码
    int updatePwd(@Param("id") Integer id, @Param("userPassword") String userPassword) throws Exception;


}

com.yhn.dao.user.UserMapper.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">
<mapper namespace="com.yhn.dao.user.UserMapper">
    <select id="getLoginUser" resultType="user">
        select * from smbms_user where userCode= #{userCode}
    </select>

    <insert id="add" parameterType="user" >
        insert into smbms_user set id=#{id},userCode=#{userCode},
        userName=#{userName},userPassword=#{userPassword},birthday=#{birthday},
        gender=#{gender},phone=#{phone},address=#{address},userRole=#{userRole},
        createdBy=#{createdBy},creationDate=#{creationDate},modifyBy=#{modifyBy},modifyDate=#{modifyDate}
    </insert>

    <select id="getUserList" parameterType="String" resultType="User">
        select * from smbms_user
        <where>
            <if test="userName != null">
                userName = #{userName}
            </if>
            <if test="userRole !=null">
               and  userRole = #{userRole}
            </if>
        </where>
        limit #{currentPageNo},#{pageSize}
    </select>

    <delete id="deleteUserById" parameterType="_int">
        delete from smbms_user where id=#{id}
    </delete>

    <select id="getUserById" parameterType="_int" resultType="user">
        select * from smbms_user where id=#{id}
    </select>

    <update id="modify" parameterType="user">
        update smbms_user
        <set>
            <if test="userName != null">
                userName=#{userName},
            </if>
            <if test="phone !=null">
                phone =#{phone},
            </if>
            <if test="userCode != null">
              userCode =#{userCode}
            </if>
        </set>
        <where>
            id=#{id}
        </where>
    </update>

    <update id="updatePwd" parameterType="String" >
        update  smbms_user
        <set>
            <if test="userPassword !=null">
                userPassword = #{userPassword},
            </if>
        </set>
        <where>
            id= #{id}
        </where>
    </update>

</mapper>

ProviderMapper

public interface ProviderMapper {
    //增加用户信息
    int add(Provider provider) throws Exception;

    //通过条件查询-providerList
    List<Provider> getProviderList(@Param("proName") String proName, @Param("proCode") String proCode,
                                   @Param("currentPageNo") Integer currentPageNo, @Param("pageSize") Integer pageSize) throws Exception;

    //获取供应商列表
    List<Provider> getProList() throws Exception;

    //通过条件查询-供应商表记录数
    int getProviderCount(@Param("proName") String proName, @Param("proCode") String proCode) throws Exception;

    //通过供应商id删除供应商信息
    int deleteProviderById(@Param("id") Integer delId) throws Exception;

    //根据provider id获取供应商信息
    Provider getProviderById(@Param("id") Integer id) throws Exception;

    //修改供应商
    int modify(Provider provider) throws Exception;
}

ProviderMapper.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">
<mapper namespace="com.yhn.dao.provider.ProviderMapper">
    <insert id="add" parameterType="provider">
        insert into smbms_provider
        (id,proCode,proName,proDesc)
        values
        (#{id},#{proCode},#{proName},#{proDesc})
    </insert>

    <select id="getProviderList" parameterType="String" resultType="Provider">
        select * from smbms_provider
        <where>
            <if test="proName !=null">
                proName =#{proName}
            </if>
             <if test="proCode != null">
                and  proCode =#{proCode}
             </if>
        </where>
        limit #{currentPageNo},#{pageSize}
    </select>

    <select id="getProList" resultType="Provider">
        select * from smbms_provider
    </select>

    <select id="getProviderCount" resultType="_int" >
        select count(*) from smbms_provider
        <where>
            <if test="proName != null">
                proName=#{proName}
            </if>
            <if test="proCode != null">
             and   proCode ={proCode}
            </if>
        </where>
    </select>
    <select id="getProviderById" parameterType="String" resultType="provider">
        select * from smbms.smbms_provider
        <where>
            id=#{id}
        </where>
    </select>

    <update id="modify" parameterType="provider">
        update smbms.smbms_provider
        <set>
            <if test="proCode !=null">
                proCode=#{proCode},
            </if>
            <if test="proName !=null">
                proName=#{proName},
            </if>
        </set>
        <where>
            id=#{id}
        </where>
    </update>
</mapper>
public interface BillMapper {
    /**
     * 根据供应商Id查询订单数量
     * @param providerId
     * @return
     */
    //根据供应商Id查询订单数量
    public int getBillCountByProviderId(@Param("providerId") Integer providerId);


    /**
     * //增加订单
     * @param bill
     * @return
     */
    public int add(Bill bill);
    //通过查询条件获取供应商列表-getBillList
    public List<Bill> getBillList(@Param("productName") String productName,
                                  @Param("providerId") String providerId,
                                  @Param("isPayment") String isPayment,
                                  @Param("startindex") Integer startindex,
                                  @Param("pageSize") Integer pageSize)throws Exception;

    //通过条件查询,查询供货商数量,模糊查询
    public int getBillCount(@Param("productName") String productName,
                            @Param("providerId") String providerId,
                            @Param("isPayment") String isPayment)throws Exception;

    //通过delId删除Bill
    public int deleteBillById(@Param("id") Integer id)throws Exception;

    //通过billId获取Bill
    public Bill getBillById(@Param("id") Integer id)throws Exception;

    //修改订单信息
    public int modify(Bill bill)throws Exception;

    //根据供应商Id删除订单信息
    public int deleteBillByProviderId(@Param("providerId") Integer providerId)throws Exception;

}
<?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.zp.dao.BillMapper">

    <!--根据供应商Id查询订单数量-->
    <select id="getBillCountByProviderId" resultType="int">
        select count(*)from smbms.smbms_bill
        where providerId=#{providerId}
    </select>

    <!--增加订单-->
    <insert id="add" parameterType="bill">
        insert into smbms.smbms_bill (id,billCode,productName,productDesc,productUnit,productCount
        ,totalPrice,isPayment,providerId,createdBy,creationDate) values
         (#{id},#{billCode},#{productName},#{productDesc},#{productUnit},#{productCount},
         #{totalPrice},#{isPayment},#{providerId},#{createdBy},#{creationDate})
    </insert>

    <!--通过条件查询,查询供货商数量,模糊查询-->
    <select id="getBillList" resultType="bill">
        select * from smbms.smbms_bill
        <where>
            <if test="productName !=null">
                and productName like concat ('%',#{productName},'%')
            </if>
            <if test="providerId !=null">
                and providerId like concat('%',#{providerId},'%')
            </if>
            <if test="isPayment !=null">
                and isPayment like concat ('%',#{isPayment},'%')
            </if>
            
        </where>
        limit #{startindex},#{pageSize}
    </select>

    <!--通过delId删除Bill-->
    <delete id="deleteBillById" parameterType="int">
        delete from smbms.smbms_bill
        <where>
            id=#{id}
        </where>
    </delete>

    <!--通过billId获取Bill-->
    <select id="getBillById" resultType="bill">
        select * from smbms.smbms_bill
        <where>
            id=#{id}
        </where>
    </select>

    <!--修改订单信息-->
    <update id="modify" parameterType="bill">
        update smbms.smbms_bill
        <set>
            <if test="productName !=null">
                productName=#{productName},
            </if>
            <if test="productDesc !=null">
                productDesc=#{productDesc},
            </if>
            <if test="productCount !=null">
                productCount=#{productCount},
            </if>
        </set>
        <where>
            id=#{id}
        </where>
    </update>
</mapper>
public interface RoleMapper {

    //获取角色列表
    public List<Role> getRoleList()throws Exception;

    //增加角色信息
    public int add(Role role)throws Exception;

    //通过Id删除Role
    public int deleteRoleById(@Param("id") String Id)throws Exception;

    //修改角色信息
    public int modify(Role role)throws Exception;

    //通过Id获取role
    public Role getRoleById(@Param("id") Integer id)throws Exception;

    //根据roleCode,进行角色编码的唯一性验证(统计count)
    public int roleCodeIsExist(@Param("roleCode") String roleCode)throws Exception;



}
<?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.zp.dao.RoleMapper">

    <select id="getRoleList" resultType="role">
    select * from smbms.smbms_role
    </select>

    <insert id="add" parameterType="role">
        insert into smbms.smbms_role (id,roleCode,roleName)
        values
        (#{id},#{roleCode},#{roleName})
    </insert>

    <delete id="deleteRoleById" parameterType="int">
        delete from smbms.smbms_role
        <where>
            id=#{id}
        </where>
    </delete>

    <update id="modify" parameterType="role">
        update smbms.smbms_role
        <set>
            <if test="roleCode !=null">
                roleCode=#{roleCode},
            </if>
            <if test="roleName !=null">
                roleName=#{roleName},
            </if>
        </set>
        <where>
            id=#{id}
        </where>
    </update>

    <select id="getRoleById" parameterType="int" resultType="role">
        select *from smbms.smbms_role
        <where>
            id=#{id}
        </where>
    </select>

    <select id="roleCodeIsExist" resultType="int">
        select count(*)from smbms.smbms_role
        <where>
            roleCode=#{roleCode}
        </where>
    </select>
</mapper>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值