Mybatis整体学习笔记-CRUD-配置解析-结果集映射-日志-注解开发-复杂环境-动态SQL-缓存

MyBatis

要多对官方文档进行学习

https://mybatis.org/mybatis-3/zh/index.html

简介

MyBatis

  • 持久层框架 Dao Access Objects
  • 定制化sql,存储过程,高级映射
  • 避免了所有JDBC代码,设置参数,获取结果集,都不用自己手写
  • 可以使用XML或注解,来配置和映射原生类型、接口和java的POJO为数据库记录
  • POJO(Plain Old Java Objects ,普通老式Java对象)
  • 原来叫iBatis

获得MyBatis

GitHub
Maven

持久化

持久化就是将程序的数据在 持久状态瞬时状态 转化的过程

内存:断电即失

数据库(JDBC),IO文件持久化

  • 之前的数据存储可能是直接通过IO存储在文本中,但是IO操作十分消耗资源,之后数据库诞生

内存太贵,所以数据要存在能持久存放的硬盘中

持久层

Dao层、service层、Controller层

完成持久化框架的代码块

层的界面十分明显

为什么需要MyBatis

方便

传统的jdbc太复杂,简化、框架、自动化

不用Mybatis也可以、只是更容易上手

优点:

  • 简单、小巧、灵活、

  • 解除SQL与程序的耦合

    • sql和程序分开,直接去改sql就行
  • 单元测试

    • sql代码分离
  • 提供映射标签

  • 提供XML标签,支持动态sql

主要是使用的人多

Spring、SpringMVC、SpringBoot

第一个MyBatis程序

思路:

  • 环境搭建
  • 导入Mybatis
  • 编写代码
    • 制作工具类utils
    • 制作JavaBean
    • 制作mapper
  • 测试

搭建环境

  1. 创建数据库

    		CREATE TABLE `mybatis`.`Untitled`  (
    		  `id` int(20) NOT NULL,
    		  `name` varchar(30) NULL,
    		  `pwd` varchar(30) NULL,
    		  PRIMARY KEY (`id`)
    		) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci;
    
    

    设置engine引擎为InnoDB,和Character字符编码为utf8

  2. 创建简单的Maven项目,删除src

  3. <dependencies>
            <!--Mybatis-->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.2</version>
            </dependency>
            <!--Mysql-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.11</version>
            </dependency>
            <!--Junit-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
  4. 加入依赖文本

  5. 主要根据文档进行

    1. https://mybatis.org/mybatis-3/zh/index.html

  6. 里面创建的子maven项目就不用频繁导包了

    1. image-20200815142600021

    2. <parent>
          <artifactId>Mybatis_Study</artifactId>
          <groupId>com.haoyun</groupId>
          <version>1.0-SNAPSHOT</version>
      </parent>
      
    3. 子模块的xml是继承父包的

编写Mybatis配置文件

工厂模式为核心的,构造出实例

  1. 在resource文件下创建mybatis-config.xml配置文件

  2. 配置内容去文档看

  3. <?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/mybatis?useSSL-true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
        <!--映射-->
        <mappers>
            <mapper resource="UserMapper.xml"/>
        </mappers>
    </configuration>
    
  4. 配置SSL安全连接,使用useUnicode编码,usecharacter字符编码utf8

创建实例SqlSession

  1. 官方给了一些代码,

  2. String resource = "org/mybatis/example/mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
  3. 把它封装为我们之前的工具类,utils,就是input配置文件,使用他的方法创建实例

  4. /*通过工厂方法生产Sqlsession实例*/
    public class MybatisUtils {
        private static final SqlSessionFactory sqlSessionFactory;
    
        static {
            /*获取sqlSessionFactory对象*/
            String resource = "src\\main\\resources\\mybatis-config.xml";
            InputStream inputStream = null;
            try {
                inputStream = Resources.getResourceAsStream(resource);
            } catch (IOException e) {
                e.printStackTrace();
            }
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        }
    
        /*获取sqlSession实例*/
        public static SqlSession getSqlSession() {
            return sqlSessionFactory.openSession();
        }
    
    }
    

创建Bean

使用Lombok

image-20200815155146383

这些方法就已经很够用了
NotNull还能自行判断空指针

image-20200815155614028

自动创建十分方便

添加maven依赖文本就行

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>

制作接口

Mapper

只要建立一个接口,不用实现,通过xml配置文件
语句可以通过XML定义也可以通过注解定义,Mybatis提供的所有特性都可以通过XML映射语言来实现

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

之前编写Dao层,需要写一个实现这个接口的类,每个方法里有不同的对数据进行操作的jdbc语句,现在不用写实现类,通过编写配置文件,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.haoyun.dao.UserDao">
    <!--对应mapper的方法作为id,和返回类型,这里最好和接口中定义泛型一致-->
    <select id="getUserList" resultType="com.haoyun.POJO.User">
        select * from mybatis.user
    </select>
</mapper>

整体结构

  • image-20200815220039901

测试

image-20200815220117624

编写规范,测试的是哪个位置的文件就写到哪,名字加个Test

public class UserDaoTest {
    @Test
    public void test(){
        /*创建sqlSession实例*/
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        
        /*第一种方法:执行sql,获取sql的对象,调用它定义的发方法*/
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        List<User> userList = userDao.getUserList();
        
        /*第二种发方法:*/
        //List<User> userList = sqlSession.selectList("com.haoyun.dao.UserDao.getUserList");

        for (User user : userList) {
            System.out.println(user);
        }
        /*关闭*/
        sqlSession.close();
    }
}

介绍了两种方法,第二种方法认识一下就好

总结:

  • image-20200815221109891

    • Builder只是用来创建SqlSessionFactory,读配置文件的,所以放到了static代码块中,只要加载一次就好

    • SqlSessionFactory,创建一次就一直存在,工厂模式,用来创建sqlsession实例,最简单的单例模式

    • sqlSession不是线程安全的

      • image-20200815221425710

      • 使用后要进行关闭,要求放到finally块中

      • 所以测试的代码应该这样改

      • public class UserDaoTest {
            @Test
            public void test() {
                /*创建sqlSession实例*/
                SqlSession sqlSession = MybatisUtils.getSqlSession();
                try {
                    /*第一种方法:执行sql,获取sql的对象,调用它定义的发方法*/
                    UserDao userDao = sqlSession.getMapper(UserDao.class);
                    List<User> userList = userDao.getUserList();
        
                    /*第二种发方法:*/
                    //List<User> userList = sqlSession.selectList("com.haoyun.dao.UserDao.getUserList");
                    
                    for (User user : userList) {
                        System.out.println(user);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    /*关闭*/
                    sqlSession.close();
                }
            }
        }
        

Mybatis常见错误

Mapper

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-54j8VAq0-1597893958664)(…/…/…/AppData/Roaming/Typora/typora-user-images/image-20200815211128717.png)]

Dao层接口的方法需要通过创建对应Mapper完成SQL语句的操作,

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.haoyun.dao.UserDao">
    <select id="getUserList" resultType="com.haoyun.POJO.User">
        select * from mybatis.user
    </select>
</mapper>

这是对UserDao接口,制作的Mapper.xml,对getUserList方法执行这条sql语句,返回类型为User
这个mapper制作好后需要到mybatis-config.xml去注册mapper

<?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/mybatis?useSSL-true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/haoyun/dao/UserMapper.xml"/>
    </mappers>
</configuration>

选择resource的位置
image-20200815213107480

解决了这个问题会出现第二个问题

Maven导出的问题,Maven是注重规定的,xml文件就要放在resource文件夹下,要不然无法导出

image-20200815213252084

target文件夹下没有UserMapper.xml

就会报错

image-20200815213631377

可能是这个文件出错
没有找到这个资源文件
查看target后发现的确没有

这时候有两种方法

  • 添加Maven依赖文本,让maven支持其他文件夹也能导出xml文件
  • 放到resource文件夹下,改好路径

Maven导出问题

  • 添加依赖文本,搜索maven导出

    • https://www.cnblogs.com/yuqiliu/p/12059614.html

    •     <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>
      
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8qW54oVK-1597893958675)(https://gitee.com/haoyunlwh/Typoraimage/raw/master/img/image-20200815214037843.png)]

    • 运行成功,第一条说这个url太老了,建议换新,这种方法也没什么人用了

  • 放入resource文件夹下

    • 更改mybatis-config.xml中的mapper路径

    •     <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
          <mappers>
              <mapper resource="UserMapper.xml"/>
          </mappers>
      
      
    • 顺便把第一个问题解决

    • image-20200815214546249

    • 运行成功,所以最好把配置文件放在resource文件夹下

CRUD

  • 增、删、改、查
  • 四个功能的流程都是一样的
    1. 添加Mapper接口的方法
    2. 编写Mapper.xml配置文件映射
      1. 编写对应功能的sql语句,给入参数
    3. 调用

Insert

  • 添加Mapper接口方法

  • /*添加一个用户*/
        public int addUser(User user);
    
  • 在Mapper.xml下添加对应语句

  • 在命名空间下添加

  • <insert id="addUser" parameterType="com.haoyun.POJO.User" >
        insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd});
    </insert>
    
  • 使用insert标签,参数类型,使用 #{} 编写参数引用

  • 测试

    • 多加了一个插件GenerateAllSetter,自动生成Set方法

    • image-20200816105117997

    • image-20200816105429235

    • @Test
      public void addUserTest(){
          SqlSession sqlSession = MybatisUtils.getSqlSession();
          UserMapper mapper = sqlSession.getMapper(UserMapper.class);
          User user = new User();
          user.setId(5);
          user.setName("haoyun");
          user.setPwd("1234");
          mapper.addUser(user);
          /*提交事务,不加提交不了*/
          sqlSession.commit();
          sqlSession.close();
      }
      
  • 之后都是类似的操作

Delete

  • /*删除一个用户*/
    public int deleteUser(int id);
    
  • <delete id="deleteUser" parameterType="int">
        delete  from mybatis.user where id = #{id}
    </delete>
    

Update

  • /*修改一个用户,根据Id,修改名字*/
    public int updateUser(User user);
    
  • <update id="updateUser" parameterType="com.haoyun.POJO.User">
        update mybatis.user set name = #{name},pwd = #{pwd} where id = #{id}
    </update>
    

Select

  • /*根据id查找用户*/
    public User getUserById(int id);
    
  • <select id="getUserById" parameterType="int" resultType="com.haoyun.POJO.User">
        select * from mybatis.user where id = #{id}
    </select>
    

总结

  • 选择好参数类型和返回值类型
  • 编写在namespace命名空间标签下
  • mybatis-config.xml要编写mapper.xml的映射
  • sql语句接收参数使用**#{}**
  • 增删改操作要加commit提交

使用Map传参

  • 不同点就是配置时的不同点和传参时不同

  • /*使用map传递参数插入*/
    public int addUserMap(Map<String,Object> map);
    
  • <insert id="addUserMap" parameterType="com.haoyun.POJO.User" >
        insert into mybatis.user (id,name,pwd) values (#{User_ID},#{User_name},#{User_pwd});
    </insert>
    
  • @Test
    public void addUserMapTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("User_ID",4);
        map.put("User_name","map");
        map.put("User_pwd","12345");
        int i = mapper.addUserMap(map);
        sqlSession.commit();
        sqlSession.close();
    
    }
    
  • Map传递参数,直接在Sql中取出key即可 parameterType = “map”

  • 对象传递参数,直接在sql中取对象的属性即可 parameterType = “Object”

  • 只有一个基本类型参数情况下,可以直接在sql中取到,一个参数,都不用加parameterType

  • 多个参数使用Map,或注解

  • 拓展:模糊查询

    • 通过传递字符串中带通配符 实现模糊查询,或者sql拼接

    • /*模糊查询*/
      List<User> USER_LIST(String value);
      
    • <select id="USER_LIST" resultType="com.haoyun.POJO.User">
          select  * from user where name like "%"#{value}"%"
      </select>
      
    • @Test
      public void selectTest(){
          SqlSession sqlSession = MybatisUtils.getSqlSession();
          UserMapper mapper = sqlSession.getMapper(UserMapper.class);
          List<User> users = mapper.USER_LIST("张");
          for (User user : users) {
              System.out.println(user);
          }
          sqlSession.close();
      
      }
      
    • image-20200816171218564

    • 这里就是只有一个参数连parameterType都不用写

    • List<User> users = mapper.USER_LIST("%张%");
      
    • <select id="USER_LIST" resultType="com.haoyun.POJO.User">
          select  * from user where name like #{value}
      </select>
      
    • 或者直接拼接字符串

配置解析

主要了解的配置

image-20200816173248259

  • configuration配置
  • properties 属性
  • settings 设置
  • typeAliases 类型别名
  • typeHandlers 类型处理器
  • objectFactory对象工厂
  • plugins 插件
  • environments 环境配置
  • environment 环境变量
  • transactionManager 事务管理器
  • dataSource数据源
  • databaseIdProvider数据库厂商标识
  • mappers 映射器

环境配置(environments)

<?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>
    <!--可以写多套环境配置,s复数,可以编写多套配置环境,但是default只能选择一套 -->
    <environments default="Test">
        <!--这样就会选择Test的环境配置-->
        <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/mybatis?useSSL-true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>

        <environment id="Test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL-true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>

    </environments>
    <!--映射-->
    <mappers>
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>

可以配置多套环境,但还是只能选择一个环境,在default处切换

事务管理器(transactionManager)

  • Mybatis中的事务管理器分两种类型

    • JDBC
      • 使用了JDBC的提交和回滚设置,依赖于从数据源得到的连接来管理事务作用域
    • Managed
      • 这个配置几乎没做什么,不提交不回滚,默认情况下会关闭连接,一些容器并不希望关闭,通过设置来阻止
  • 如果正在使用Spring和Mybatis就不用配置这些东西了

  • <transactionManager type="JDBC"/>
    

数据源(dataSource)

连接数据库,dbcp,c3p0、druid

主要用于连接数据库,三个数据源类型

  • unpooled 没数据库连接池
  • pooled 数据库连接池 默认的
  • JNDI 正常连接
<dataSource type="POOLED">

属性(properties)

  • 可以通过properties来实现引用配置文件

  • Demo

    • 编写一个配置文件
    • 引入
  • 编写配置文件,创建在resource文件夹下

  • driver  = com.mysql.cj.jdbc.Driver
    url= jdbc:mysql://localhost:3306/mybatis?useSSL-true&useUnicode=true&characterEncoding=UTF-8
    username  =root
    password = 123456
    
  • 在mybatis-config.xml中引入配置文件

  •     <mappers>
            <mapper resource="UserMapper.xml"/>
        </mappers>
        <properties></properties>
    </configuration>
    
  • 假如插入在了末尾,configuration是会报错的

  • image-20200816221412770

  • 提示着插入顺序有问题,应该,先插入properties,然后是settings、、、、、

  • <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <!--引入外部配置文件-->
        <properties resource="db.properties"/>
        <!--可以写多套环境配置,s复数,可以编写多套配置环境,但是default只能选择一套 -->
        <environments default="Test">
            <!--这样就会选择Test的环境配置-->
            <environment id="Test">
                <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>
    
        <!--映射-->
        <mappers>
            <mapper resource="UserMapper.xml"/>
        </mappers>
    </configuration>
    
  • value就需要这样引用

  • 还可以在properties标签中添加键值对,

  • <properties resource="db.properties">
        <property name="username" value="root"/>
        <property name="password" value="123"/>
    </properties>
    
  • 这里在xml中添加了键值对,但是配置是错误的,但是还能通过测试,所以写在db.properties中的配置文件的优先级是比较高的

  • 已经学了三种方法

    • 直接写
    • 写在properties中引入
    • 在properties标签中添加键值对

类型别名(typeAliases)

就是设置一个短的名字,比如有些名字,要带上很长的包名,能减少冗长的编写

编写的位置要注意

image-20200816223458529

<typeAliases>
    <typeAlias type="com.haoyun.POJO.User" alias="User"/>
</typeAliases>
  • 也可以指定一个包名,Mybatis会去指定的包名下搜索,JavaBean

  • <typeAliases>
        <package name="com.haoyun.POJO"/>
    </typeAliases>
    
  • 写参数类型的时候写小写,mybatis就会通过包名路径去找,写大写也没所谓

还可以使用注解

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Alias("hello")
public class User {
    private int id;
    private String name;
    private String pwd;
}

使用Alias注解将User的别名设置成了hello

image-20200816224400730

就找不到那个JavaBean了

Java的内建类型也是有别名的,都不区分大小写

image-20200816224603008

比如返回值是个int,可以写成 _int

image-20200816224653217

设置(settings)

有很多的设置,不用全部记录,不懂的去翻文档就行,简单记录几个

  • image-20200816225236918
  • 开启缓存,懒加载
    • 懒加载之前在jsp说过,就是为了让页面启动的更快,让一些不是很重要的操作之后再加载,先让界面加载出来,程序会看着比较流畅,让对象延迟加载
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5qR2vWIp-1597893958698)(…/…/…/AppData/Roaming/Typora/typora-user-images/image-20200816225503876.png)]
  • 自动生成主键
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fi83Ar4M-1597893958699)(https://gitee.com/haoyunlwh/Typoraimage/raw/master/img/20200816225558.png)]
  • 驼峰命名转换,再Java中使用的比较多的就是驼峰命名,但是在数据库中,字段名通常使用 下划线 来隔开单词,因为在Oracle数据库中,所有字段名都是大写的格式,只有通过中间加入下划线才能提高可读性,这是下划线命名的由来,可以将数据库列名映射为驼峰命名

其他设置

  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins插件
    • mybatis-generator-core
      • 能自动根据数据库生成mybatis,sql文件,完成crud工作
    • mybatis-plus
      • 还能简化mybatis操作,crud都不用写,只要写一些复杂的操作
    • 通用mapper
      • 注册绑定mapper文件
      • image-20200816230455968
      • 四种方法,第二种不要用,第三种需要配置文件和Mapper接口同名,还必须要在同一个包下,第四种,选择存放的包,但是和第三种的情况是一样的,如果配置文件和mapper不同名就会出问题,建议使用第一种方法

生命周期和作用域

  • 生命周期、和作用域,是至关重要的,因为错误的使用会导致非常严重的 并发问题
  • image-20200816234447211
  • SqlSessionFactoryBuilder:
    • 一旦创建了SqlSessionFactory,就不需要他了
    • 放在局部变量
  • SqlSessionFactory
    • 可以想想为数据库连接池
    • 应该一直存在,多次创建浪费资源,在高并发环境下,容易崩溃
    • 全局作用域
  • SqlSession
    • 每个线程都应该有自己的SqlSession,可以理解为连接到连接池的一个请求
    • sqlSession实例不是线程安全,因此不能被共享
    • 最佳的作用域,不能放在一个类的静态域,甚至一个类的实例变量也不行,
    • 需要关闭,否则占用资源

ResultMap结果集映射

解决属性名和字段名不一致的问题,之前是实体类和数据库字段一致的,现在测试不一致的情况

这会涉及到一个mybatis自动映射的问题,和手动映射的方法

  • 数据库字段和实体类名字不同,产生的错误

  • image-20200817091825858

  • @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @ToString
    @Alias("hello")
    public class User {
        private int id;
        private String name;
        private String password;
    }
    
  • 字段名pwd不同,运行产生的结果也不同,这里顺便玩了下设置别名

  • @Test
    public void Test(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User userById = mapper.getUserById(1);
        System.out.println(userById);
        sqlSession.close();
    }
    
  • 运行出来的结果pwd字段没有查询到

  • image-20200817092249108

  • <mapper namespace="com.haoyun.dao.UserMapper">
        <select id="getUserById" parameterType="int" resultType="hello" >
            select * from mybatis.user where id = #{id}
        </select>
    </mapper>
    
  • 最蠢的解决方法就是设置sql语句的别名

  • <select id="getUserById" parameterType="int" resultType="hello" >
        select id,name,pwd  password from mybatis.user where id = #{id}
    </select>
    
  • 当然用的不是这种方法

  • 现在resultType是具体的类型

实现resultMap

  • 结果集映射

  • 将数据库的字段,映射成Java实体类的属性

  • <mapper namespace="com.haoyun.dao.UserMapper">
        <resultMap id="UserMap" type="hello">
            <result column="id" property="id"/>
            <result column="name" property="name"/>
            <result column="pwd" property="password"/>
        </resultMap>
        <select id="getUserById" parameterType="int" resultMap="UserMap" >
             select * from mybatis.user where id = #{id}
    
        </select>
    </mapper>
    
  • 这是比较简单的,要编写比较难的就需要在resultMap进行嵌套

  • 对于简单的语句并不用写resultMap语句,有些是自己已经映射好了的,比如id和id,name和name字段名和属性名都是一样的,只要写不一样的就好

  • <resultMap id="UserMap" type="hello">
        <result column="pwd" property="password"/>
    </resultMap>
    
  • 已经自动将字段名映射到对应的实体类属性上了

  • 如果世界总是这么简单就好了

之后讲一对多和多对一的例子,会更需要resultMap

日志

日志工厂

如果一个数据库操作出现了异常,要进行排错,日志是一个好工具

曾经是:sout,debug

现在是日志工厂image-20200817103210998

在mybatis-config.xml中的settings有这么一个设置,logImpl

  • SL4J 重点
  • LOG4J
  • LOG4J2
  • JDK_LOGGING Java自带的日志输出
  • STDOUT_LOGGING 工具包日志输出
  • NO_LOGGING 不输出日志

mybatis中具体使用哪一个日志实现,在设置中设定

stdout_logging标准日志输出

添加logImpl设置

设置在mybatis-config.xml中的设置需要按照规则顺序来
忘记了顺序能故意打错,等报错看

image-20200817104153286

要放在properties和typeAliases的中间

添加设置的时候最好直接从官方文档复制,不要靠脑子记,打错任何一点都会报错

logImpl设置文档

image-20200817105226536

比如这一段logImpl打错了,表面是不会报错的,运行的时候就会出问题

image-20200817105317505

到了这里还很难找

image-20200817105343600

下面一句会有The setting LogImpl is not know,看到下面才能知道他给的错误提示

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

所以不要打错,多一个空格都不行

image-20200817110542624

  • 从opening开始读,打开jdbc连接
  • 创建connection
  • 设置自动提交为false
  • 预编译sql
  • 传递的参数parameter 1个 integer类型
  • 查询的列
  • 查询的行
  • 总共有几条记录
  • 最后才是结果
  • returned connection 返回到数据库连接池pool

Log4J

可以将日志输出到控制台、文件、GUI组件

  • 可以设置每一条的输出格式
  • 设置日志级别
  • 开于通过配置文件配置

主要操作

  1. 导入包

  2. <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  3. 添加配置文件,面向百度编程,搜索log4j的properties

  4. #将等级得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/haoyun.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
    
  5. <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    
  6. image-20200817214013044

  7. 主要内容还是类似的

简单使用

  • 导入包的时候要注意不要导错包了,要导入apache的log4j

  • public class UserMapperTest {
        static Logger logger = Logger.getLogger(UserMapperTest.class);
    
  • image-20200817214441811

  • public void log4jTest(){
        logger.info("info:Test");
        logger.debug("debug:Test");
        logger.error("error:Test");
    }
    
  • image-20200817214926730

  • 打开看日志文件

  • image-20200817215058024

  • image-20200817215031963

  • 就是比较清晰明了,工作中要多使用

  • image-20200817220327707

  • 还有其他的日志级别,自己去学习

分页实现

  • 两种方法实现分页

    • 直接修改sql语句
    • 使用RowBounds
  • 修改sql语句

    • /*实现分页*/
      List<User> getUserByLimit(Map<String,Integer> map);
      
    • 这里使用到Map作为结果集映射,使用map传递参数,比较方便

    • <select id="getUserByLimit" parameterType="map" resultMap="UserMap">
          select * from  mybatis.user limit #{startIndex},#{pageSize}
      </select>
      
    • @Test
      public void getUserByLimitTest(){
          SqlSession sqlSession = MybatisUtils.getSqlSession();
          UserMapper mapper = sqlSession.getMapper(UserMapper.class);
          HashMap<String, Integer> limitMap = new HashMap<String, Integer>();
          limitMap.put("startIndex",0);
          limitMap.put("pageSize",2);
          List<User> userByLimit = mapper.getUserByLimit(limitMap);
          for (User user : userByLimit) {
              System.out.println(user);
          }
          sqlSession.close();
      }
      
    • image-20200817223123814

  • 使用RowBounds实现分页

    • 虽然没有直接修改sql来的快,但是比较复合java面向对象的概念

    • 通过代码的层面实现分页

    • /*通过RowBounds实现分页*/
      List<User> getUserByRowBounds();
      
    • <select id="getUserByRowBounds" resultMap="UserMap">
          select * from mybatis.user
      </select>
      
    • @Test
      public void getUserByRowBoundsTest(){
          SqlSession sqlSession = MybatisUtils.getSqlSession();
          RowBounds rowBounds = new RowBounds(0,2);
          List<User> userByLimit = sqlSession.selectList("com.haoyun.dao.UserMapper.getUserByRowBounds", null, rowBounds);
      
          for (User user : userByLimit) {
              System.out.println(user);
          }
          sqlSession.close();
      }
      
  • 还有分页插件比如:PageHelper

    • 要使用就去看文档就行

注解开发

Mybatis使用的比较多的是配置文件,之后是使用的比较多的是注解开发

面向接口编程

解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵循共同的标准,使得开发变得容易,规范性更好,

  • 面向对象系统中,系统各种功能可能是由许许多多的不同对象协作完成,各个对象内部如何实现对系统涉及人员来说不重要

  • 对象之间协作是关键,同类之间的通信,模块之间的通信,是在系统设计之初要考虑的,面向接口编程就是按照这种思想来编程

关于接口的理解:

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

三个面向区别:

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

注解开发

  1. 在接口上编写注解

  2. 在mybatis-config.xml设置mapper

  3. 测试

  4. 底层原理是注解和反射和动态代理

  5. 在接口上编写注解

  6. public interface UserMapper {
        @Select("select * from user")
        List<User>getUsers();
    }
    
    1. 那个user是idea特有的写法,要连接上了才能这么写
  7. 将mapper映射到配置文件

  8. <!--映射-->
    <mappers>
        <mapper class="com.haoyun.dao.UserMapper"/>
    </mappers>
    
  9. 测试

  10. @Test
    public void Test(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.getUsers();
        for (User user : users) {
    
            System.out.println(user);
        }
        sqlSession.close();
    }
    
  11. image-20200818100445449

  12. 得出结果password没查出来,因为字段名和JavaBean类中的属性名不一致,暂时只能修改Bean类中的属性名,没法用resultMap,所以注解只能完成一些简单的操作,最好还是通过配置文件来操作

  13. 总结:

    1. 通过DeBug功能,可以看到里面的本质是通过反射机制来实现的
    2. image-20200818101017583
    3. 配置的一些值都会被注入到sqlSession中去
    4. image-20200818101126067
    5. 能反射到UserMapper就能解析到里面的接口,里面的注解,底层是使用的动态代理来完成注解的工作

Mybatis执行流程

image-20200818112947902

有些过程是内部的

注解实现CRUD

增删改都需要commit提交事务,一般通过SqlSession.commit()提交,还可以在创建SqlSession时就设置自动提交

  • public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession(true);
    }
    
  • 之前创建的工具类,openSession有选项能设置自动提交

  • public interface SqlSessionFactory {
        SqlSession openSession();
    
        SqlSession openSession(boolean var1);
    
  • 对应的接口,往下查看实现的方法

  • public SqlSession openSession(boolean autoCommit) {
        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, autoCommit);
    }
    
  • boolean 是autoCommit,自动提交,看到这里就行,具体的操作怎么实现还能继续往下探究

  • public interface UserMapper {
        @Select("select * from user where id =#{id} and name = #{name}")
        User getUserById(@Param("id") int ida,@Param("name") String namea);
    }
    
  • 然后再mybatis-config.xml中进行注册

  • <mappers>
        <mapper class="com.haoyun.dao.UserMapper"/>
        <!--需要配置的接口多了可以使用通配符-->
        <mapper class="com.haoyun.dao.*"/>
    </mappers>
    
  • 使用@Param传递参数,可以不用跟形参名称一致,但是要跟上面注解的名称一致

  • 其他的CRUD操作将@Select换成Update、Insert、Delete就行,参数要对上

  • @Test
    public void Test(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User userById = mapper.getUserById(3,"map");
        System.out.println(userById);
        sqlSession.close();
    }
    
  • 测试

  • 注解还可以和xml配置文件配合来使用

  • 接口层通过注解转递参数

  • int deleteById(@Param("deleteParamId") int id);
    
  • UserMapper.xml通过 #{} 来接收参数

    • #{} 和 KaTeX parse error: Expected 'EOF', got '#' at position 10: {} 的区别,#̲可以防止sql注入,不行
  • <mapper namespace="com.haoyun.dao.UserMapper">
        <delete id="deleteById" parameterType="int">
            delete from user where id = #{deleteParamId}
        </delete>
    </mapper>
    
  • 需要注意的是要在主配置文件中注册

  • <mappers>
        <mapper resource="UserMapper.xml"/>
    </mappers>
    

复杂环境(一对多,多对一)实现

  • 一对多
  • 多对一

多个学生和一个老师的问题

image-20200818140945757

不同的角度分析问题,

  • 对于学生而言,关联,多个学生关联一个老师(多对一)

  • 对于老师而言,集合,一个老师,有多个学生(一对多 )

在resultMap设置中还有其余一些设置

  • association 一个复杂类型的关联
  • collection 一个复杂类型的集合
  • image-20200818141539221

环境搭建

  1. 添加student表和teacher表

    1. [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HWpac04r-1597893958735)(…/…/…/AppData/Roaming/Typora/typora-user-images/image-20200818150653053.png)]
    2. 要设置外键
  2. image-20200818153628192

    1. 模块结构

    2. 实体类、实体类接口,接口映射xml文件,核心配置文件、工具类

      1. 实体类

      2. @Data
        @AllArgsConstructor
        @NoArgsConstructor
        public class Student {
            private int id;
            private String name;
            private Teacher teacher;
        }
        
      3. 实体类映射接口

      4. public interface TeacherMapper {
            @Select("select * from teacher where id = #{tid}")
            List<Teacher> getTeacher(@Param("tid") int id);
        }
        
      5. mapper.xml文件

      6. <?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.haoyun.dao.TeacherMapper">
        </mapper>
        
      7. 核心配置文件

      8. <mappers>
            <mapper resource="StudentMapper.xml"/>
            <mapper resource="TeacherMapper.xml"/>
        </mappers>
        
      9. 简单测试

      10. @Test
        public void teacher(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
            List<Teacher> teacher = mapper.getTeacher(2);
            for (Teacher teacher1 : teacher) {
                System.out.println(teacher1);
            }
            sqlSession.close();
        
        }
        

多对一的处理

  • sql可以直接写出来,
select * from student,teacher where teacher.id=student.tid

有两种形式:

  • 按照查询嵌套处理
  • 按照结果嵌套处理

如何写入方法思路:

  1. 查询所有学生信息
  2. 根据查询出来的学生的tid,寻找对应的老师寻找对应的老师

解决问题,主要流程:

  • JavaBean类设置别名好操作
  • 编写mapper.xml文件
    • 使用resultMap的association设置
  • 注册核心文件
  • 测试

按照查询嵌套处理

  1. 设置别名

    1. @Data
      @AllArgsConstructor
      @NoArgsConstructor
      @Alias("Student")
      public class Student {
          private int id;
          private String name;
          private Teacher teacher;
      }
      
    2. @Data
      @AllArgsConstructor
      @NoArgsConstructor
      @Alias("Teacher")
      public class Teacher {
          private int id;
          private String name;
      }
      
  2. 编写mapper.xml文件

    1. <?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.haoyun.dao.StudentMapper">
      
          <resultMap id="StudentTeacher" type="Student">
              <!--映射名称  返回值类型-->
      <!--        <result property="name" column="name"/>-->
              <!--属性名和字段名同名的可以不用写了-->
              <!--association  关联-->
              <!--collection  集合-->
              <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
              <!--这里的property是一个对象,通过tid不能直接获取,javaType设置这个对象的类型,然后加入一个查询getTeacher-->
          </resultMap>
      
          <select id="getStudent" resultMap="StudentTeacher">
              select * from student
          </select>
      
          <select id="getTeacher" resultType="Teacher">
              <!--这个子查询的#引用因为只有一个,所以名字随便写也会自动匹配-->
              select  * from teacher where  id  =#{id}
          </select>
      
      </mapper>
      
    2. 有点类似子查询,按照查询嵌套处理

  3. 添加核心配置文件映射

    1. <mappers>
          <mapper resource="StudentMapper.xml"/>
          <mapper resource="TeacherMapper.xml"/>
      </mappers>
      
  4. 测试

    1. @Test
      public void teacher(){
          SqlSession sqlSession = MybatisUtils.getSqlSession();
          StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
          List<Student> student = mapper.getStudent();
          for (Student student1 : student) {
              System.out.println(student1);
          }
          sqlSession.close();
      
      }
      
    2. image-20200818191356284

按照结果嵌套处理

  1. 嵌套处理

    1. <resultMap id="StudentTeacher2" type="Student">
          <result property="id" column="sid"/>
          <result property="name" column="sname"/>
      
          <association property="teacher" javaType="Teacher">
              <!--在Student,javaBean中,教师是用属性名teacher的,类型为Teacher,这里面嵌套一个结果集映射-->
              <result property="name" column="tname"/>
          </association>
          
      </resultMap>
      
      <select id="getStudent2" resultMap="StudentTeacher2">
          select s.id sid,s.name sname,t.name tname
          from teacher t,student s
          where s.tid = t.id;
      </select>
      
  2. 测试

    1. 结果是一样的
  3. 总结:

    1. 多对一查询方式主要也就两种
      1. 子查询
      2. 联表查询

一对多的处理

  • 重点就是在配置TeacherMapper.xml上
  1. 编写JavaBean

    1. @Data
      @AllArgsConstructor
      @NoArgsConstructor
      @Alias("Teacher")
      public class Teacher {
          private int id;
          private String name;
          private List<Student> students;
      
      }
      
    2. 一个老师对应多个学生,学生是一个集合用list装起来

    3. @Data
      @AllArgsConstructor
      @NoArgsConstructor
      @Alias("Student")
      public class Student {
          private int id;
          private String name;
          private int tid;
      }
      
    4. 学生的一些属性

  2. 编写TeacherMapper.xml文件时要先理清关系,要做的是个一个老师有哪些学生,所以先定位哪个老师

    1. image-20200818214803299

    2. 这样就能查出老师id为1的老师有哪些学生

    3. image-20200818214840807

    4. 需要传递一个老师对应的id

    5. 所以编写接口的时候需要传递id

    6. List<Teacher> getTeacherById(@Param("id")int id);
      
    7. <mapper namespace="com.haoyun.dao.TeacherMapper">
          <resultMap id="TeacherStudent" type="Teacher">
              <result property="id" column="tid"/>
              <result property="name" column="tname"/>
              
              <collection property="students" ofType="Student">
                  <result property="id" column="sid"/>
                  <result property="name" column="sname"/>
                  <result property="tid" column="tid"/>
              </collection>
              
          </resultMap>
      
          <select id="getTeacherById" resultMap="TeacherStudent">
              select s.id sid,s.name sname,t.name tname,t.id tid
              from teacher t,student s
              where s.tid=t.id and t.id = #{id}
          </select>
      
      
      
      </mapper>
      
    8. 因为这条select语句采用了很多别名,所以编写map的时候需要多加几个结果集映射

    9. 这是按照结果嵌套来处理的

  • 按照查询嵌套

  • <select id="getTeacherById2" resultMap="TeacherStudent2">
        select * from teacher where id = #{id}
    </select>
    <resultMap id="TeacherStudent2" type="Teacher">
        <collection property="students" javaType="ArrayList"  ofType="Student" select="student" column="id"/>
    </resultMap>
    
    <select id="student" resultType="Student">
        select  * from student where tid = #{id}
    </select>
    
  • 查询嵌套,先查询一个再查询一个

小结:

  • 关联 association(多对一)
  • 集合 collection(一对多)
  • javaType & ofType
    • javaType 用来指定实体类中属性的类型
    • ofType 用来指定映射到List或者集合中的POJO类型(List),泛型中的约束类型
  • 注意点
  • 保证sql的可读性
  • 注意属性名和字段问题
  • 使用日志好进行错误排查,建议使用LOG4J

慢SQL,比较慢,就是少自己写,多搜集点效率高的sql语句,到时遇到了直接用

面试高频:

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

动态SQL

官方文档已经写的非常详细

动态Sql是指根据不同的条件生成不同的SQL语句

之前是手动拼接的,拼接sql语句是很麻烦的

四个标签:

  • if
  • choose(when,oherwise)
  • trim(where,set)
  • foreach

动态SQL环境搭建

  • 数据库
  • maven导包
  • 实体类
  • Mapper.xml
  • 核心配置文件
    • setting驼峰命名转换
  • 工具类
    • IDutils通过UUID实现随机ID

都是比较常用的套路

开始

  1. 创建数据库

    1. image-20200819215457734
  2. Maven导包

    1. <dependencies>
          <dependency>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
              <version>8.0.11</version>
          </dependency>
          <dependency>
              <groupId>org.mybatis</groupId>
              <artifactId>mybatis</artifactId>
              <version>3.5.2</version>
          </dependency>
          <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>4.12</version>
              <scope>test</scope>
          </dependency>
          <dependency>
              <groupId>log4j</groupId>
              <artifactId>log4j</artifactId>
              <version>1.2.17</version>
          </dependency>
          <dependency>
              <groupId>org.projectlombok</groupId>
              <artifactId>lombok</artifactId>
              <version>1.18.12</version>
          </dependency>
      </dependencies>
      
  3. 编写POJO实体类

    1. @Data
      @NoArgsConstructor
      @Alias("Blog")
      public class Blog {
          private String id;
          private String title;
          private String author;
          private Date createTime;
          //添加驼峰命名映射setting
          /*这个名字会映射成create_time*/
          /*mapUnderscoreToCamelCase*/
          private int views;
      }
      
  4. 编写接口以及接口映射

    1. 编写好实体类就写好对应的接口,接口写好写映射,然后把映射添加到核心配置文件中去

    2. public interface blogMapper {
          @Select("select * from blog")
          List<Blog> getblogs();
          int addBook(Blog blog);
      }
      
    3. 暂时写了两个接口,一个使用注解一个使用xml

    4. <?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.haoyun.dao.blogMapper">
      
          <insert id="addBook" parameterType="Blog">
              insert into mybatis.blog (id,title,author,create_time,views)
               values (#{id},#{title},#{author},#{createTime},#{views});
          </insert>
      
      </mapper>
      
    5. 因为添加了驼峰命名的设置,所以这里传递参数是填写的createTime

  5. 编写核心配置文件

    1. 使用的是外部配置文件

    2. driver  = com.mysql.cj.jdbc.Driver
      url= jdbc:mysql://localhost:3306/mybatis?useSSL=false
      -TRUE&useUnicode=true&characterEncoding=UTF-8
      username  =root
      password = 123456
      
    3. <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE configuration
              PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-config.dtd">
      <configuration>
          <!--引入外部配置文件-->
          <properties resource="db.properties"/>
          <settings>
              <setting name="mapUnderscoreToCamelCase" value="true"/>
          </settings>
          <typeAliases>
              <package name="com.haoyun.POJO"/>
          </typeAliases>
          <!--可以写多套环境配置,s复数,可以编写多套配置环境,但是default只能选择一套 -->
          <environments default="Test">
              <!--这样就会选择Test的环境配置-->
              <environment id="Test">
                  <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>
      
          <!--映射-->
          <mappers>
              <mapper class="com.haoyun.dao.blogMapper"/>
              <mapper resource="blogMapper.xml"/>
      
          </mappers>
      
      </configuration>
      
    4. 要注意setting标签编写的位置

    5. 设置 mapUnderscoreToCamelCase为true,名字不要打错

    6. https://mybatis.org/mybatis-3/zh/configuration.html#settings

    7. image-20200819212337706

  6. 编写工具类

    1. MybatisUtil这个工具类使用过好多次了,用来创建sqlsession

    2. public class MybatisUtil {
          private static SqlSessionFactory sqlSessionFactory = null;
      
          static {
              InputStream inputStream = null;
      
              try {
                  inputStream = Resources.getResourceAsStream("mybatis-config.xml");
              } catch (FileNotFoundException e) {
                  e.printStackTrace();
              } catch (IOException e) {
                  e.printStackTrace();
              }
              sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
          }
          public static SqlSession getSqlSession(){
              return sqlSessionFactory.openSession();
          }
      }
      
    3. 再写一个随机生成ID的,使用UUID方法

    4. public class IDutils {
          public static String getId(){
              return UUID.randomUUID().toString().replaceAll("-","");
          }
      }
      
    5. 将 “-” 替换为 “” 空

    6. 1a19b16580bb46588863d96fa4822d4f测试得到的id

  7. 环境整体结构

    1. image-20200819221313389

    2. 测试

    3. public class blogTest {
          @Test
          public void Test() {
              SqlSession sqlSession = MybatisUtil.getSqlSession();
              blogMapper mapper = sqlSession.getMapper(blogMapper.class);
              List<Blog> getblogs = mapper.getblogs();
              for (Blog getblog : getblogs) {
                  System.out.println(getblog);
              }
              sqlSession.close();
          }
      
          @Test
          public void TestInsert() {
              SqlSession sqlSession = MybatisUtil.getSqlSession();
              blogMapper mapper = sqlSession.getMapper(blogMapper.class);
      
              Blog blog = new Blog();
              blog.setId(IDutils.getId());
              blog.setTitle("TestTitle");
              blog.setAuthor("TestAuthor");
              //生成时间记得选择util包的Date
              blog.setCreateTime(new Date());
              blog.setViews(1200);
      
              mapper.addBook(blog);
              sqlSession.commit();
      
              sqlSession.close();
          }
      
          @Test
          public void IDutilsTest() {
              System.out.println(IDutils.getId());
          }
      
      }
      
    4. image-20200819221516689

动态SQL之IF语句

就是再Mapper.xml中添加if判断,实现sql的拼接,可以判断是否传递了对应列的参数来指定拼接对应的sql

流程:

  1. 添加接口方法

  2. 设置mapper.xml

    1. 加入if判断
  3. 测试

  4. 添加接口发方法

    1. List<Blog> queryBlogIF(Map map);
      
  5. 设置mapper.xml

    1.     <select id="queryBlogIF" 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>
      
    2. 参数类型为map,返回值类型为Blog,这个添加了重命名

    3. 为了保证if条件都不符合,不进行拼接sql语句的情况,不能写成这样,这样拼接上是直接跟着and或or的,所以在if外要包上一层

    4. ​ select * from blog where

    5. 如果传递了参数,进行判断,然后拼接sql

  6. 测试

    1. @Test
      public void queryBlogIFTest(){
          SqlSession sqlSession = MybatisUtil.getSqlSession();
          blogMapper mapper = sqlSession.getMapper(blogMapper.class);
          HashMap hashMap = new HashMap();
          hashMap.put("title","sadfasdf");
          hashMap.put("author","l");
          List<Blog> blogs = mapper.queryBlogIF(hashMap);
          for (Blog blog : blogs) {
              System.out.println(blog);
          }
          sqlSession.close();
      
      }
      
    2. 结果

    3. image-20200820000023219

    4. 可以选择传入一个空的hashMap,查询的就是全表的信息

trim(where,set)

trim 切除

这个主要的功能就是,拼接字符串,然后删除语句后缀的逗号

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

根据编写内容不同选择不同的标签,这里是更新,所以加入了set标签,使用方法也是一样的,只是这里拼接到where的时候会取除末尾的标签中语句的逗号,当然,两个if都没用上,会直接报错的,因为set之后直接拼接where,造成语法错误

@Test
public void queryBlogIFTest(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    blogMapper mapper = sqlSession.getMapper(blogMapper.class);
    HashMap hashMap = new HashMap();
    hashMap.put("title","sadsdf");
    hashMap.put("author","test");
    hashMap.put("id","1");

    mapper.updateBlog(hashMap);
    sqlSession.commit();
    sqlSession.close();
}

image-20200820011834196

最后author末尾的逗号是被消除了

choose(when,otherwise )

choose 选择

when 什么情况下

otherwise 否则

规则有点像switch,只去匹配case中的一个值,唯一不同的就是otherwise必须添加参数,如果走了where就不会走otherwise,where都没走就会走otherwise,执行顺序是从上往下执行的,先判定上面的where

choose语句

    <select id="queryBlogChoose" parameterType="map" resultType="Blog">
        select * from blog
        <where>
            <choose>
                <when test="title != null">
                    and title = #{title}
                </when>
                <when test="author!= null">
                    and author = #{author}
                </when>
                <otherwise>
                    and views = #{views}
                </otherwise>
            </choose>
        </where>
    </select>

多种情况测试

  1. 不填参数

  2. @Test
    public void queryBlogIFTest(){
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        blogMapper mapper = sqlSession.getMapper(blogMapper.class);
        HashMap hashMap = new HashMap();
        //hashMap.put("title","sadfasdf");
        //hashMap.put("author","l");
        //hashMap.put("views","55");
        List<Blog> blogs = mapper.queryBlogChoose(hashMap);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        sqlSession.close();
    }
    
  3. image-20200820003308068

  4. 自动的添加了views条件但是parameter为null

  5. 没查询出来

  6. hashMap.put("views",10);
    
  7. image-20200820003727498

  8. 所以前面的when没有参数就会添加otherwise中的语句

  9. 加入中间when值

  10. hashMap.put("author","l");
    hashMap.put("views",10);
    
  11. 加入了中间的author when判断和otherwise判断

  12. image-20200820004121140

  13. 但是只执行了when的语句,otherwise并没有执行

  14. when优先级

  15. hashMap.put("title","sadfasdf");
    hashMap.put("author","l");
    hashMap.put("views",10);
    
  16. 三条都加入了

  17. image-20200820004240099

  18. 但是只执行了第一条

  19. 所以第一条的优先级最高

总结:

  • 什么参数都不加的情况下会选择otherwise中的语句
  • 第一个多条when条件,从上往下判断,执行排在上面的
  • 只选择一条,包括otherwise

共同的父亲trim

image-20200820012342128

如果前置有set关键字,就会删除无关的逗号,prefix 前缀

image-20200820012513803

指定的是一个where,如果存在就会删除指定的内容

image-20200820012714201

这是定制化sql用的一些东西

SQL片段

有一些重复的语句,可以提取出代码片段,实现sql语句的复用

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

<select id="queryBlogIF" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <include refid="if_title_author"/>
    </where>
</select>

最好sql片段不要编写太复杂的事情,要不然重用效率就变低了

不要存在where标签,where是要根据条件语句来查询的加入就不好实现复用

foreach

<select id="queryBlogForEach" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
            id = ${id}
        </foreach>
    </where>
</select>

传入一个map类型参数,返回值是一个Blog类型

collection 传递进一个集合,集合的名称为ids,那么用map传递参数的时候就要对应ids名进行传参

里面的每个元素是id,传递了集合就会进行自动的遍历集合,传递参数进id中,

open开始,加入where是为了解决select * from where 1=1 后面必须跟指定条件成立的语句,要不然动态sql没有进入foreach就会变成 select * from where 导致语句错误,加入where标签,还能根据判断去除不必要的and或or

这里就是open开始,填装了一个and (,注意,and和( 之间是有一个空格的,要不然where会识别不出来

在日志中查看得出的结果就是这样

image-20200820082236707

本来用来连接其他条件的语句,不连接其他条件,and没有被去除,导致语句出错

close foreach结束时添加

separator 分隔符,用来分隔集合中元素的分隔符

最后给集合元素传参

@Test
public void queryBlogForEachTest(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    blogMapper mapper = sqlSession.getMapper(blogMapper.class);
    HashMap hashMap = new HashMap();
    ArrayList<String> arrayList = new ArrayList<String>();
    arrayList.add("1");
    arrayList.add("2");
    arrayList.add("3");
    hashMap.put("ids",arrayList);
    List<Blog> blogs = mapper.queryBlogForEach(hashMap);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    sqlSession.commit();
    sqlSession.close();
}

总结:

动态sql,本质还是sql,只是在sql层面,去执行一个逻辑代码,按照sql格式进行拼接,排列组合,去除不必要的字符

先在Mysql中写出完整的sql,再去修改实现动态sql,实现通用

多去看官方文档

怎么使用普通的sql查询还是不够的,需要多去了解更深层次的知识

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

Mybatis缓存

什么是缓存:

连接数据库是很耗资源的,将查询出来的结果存放在一个可以直接取到的地方–>内存:缓存

要查询相同数据时候直接走缓存区执行,就不用再连接数据库了

用户把查询的数据放在内存中,就不用反复去查询

缓存主要解决高并发系统的性能问题

三高问题:

  • 高并发
  • 靠可用
  • 高性能

image-20200820091021342

中间的是服务器,右边的是数据库

读操作是比较容易的,为了让读操作更快,以前使用过一个叫做Memacache的服务,作为缓存实现,将常用的数据存放再Memacache服务器中,读取的时候就快一些,然后数据库还会涉及到读写的问题,多个数据库如何读写,写操作,怎么保证数据库的一致性,读写性能要求不同,如何分配数据库进行读写,哪些数据库作为读数据库,哪些作为写数据库,分工进行

适合使用缓存的数据是那些不经常改变的数据

多数时间都是在进行读操作

Mybatis缓存

  • mybatis,通过配置来定义缓存,可以提升查询效率
  • 默认定义了两级缓存:一级缓存和二级缓存
    • 默认情况,一级缓存开启,(sqlsession级别的缓存,称为本地缓存)
    • 二级缓存需要手动开启和配置,基于namespace级别的缓存,命名空间
    • 为了提高扩展性,mybatis定义了缓存接口,Cache,可以通过实现Cache接口来自定义二级缓存

一级缓存

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

主要步骤还是和之前差不多,因为一级缓存是默认的,所以不用进行什么配置

@Select("select * from blog where id = #{id}")
List<Blog> selectBlog(Map map);

void updateBlog(Map map);

添加两个接口进行测试

@Test
public void SqlSessionCacheTest(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    blogMapper mapper = sqlSession.getMapper(blogMapper.class);
    HashMap hashMap = new HashMap();
    hashMap.put("id","1");
    Blog blog2 = mapper.selectBlog(hashMap);
    System.out.println(blog2);
    hashMap.put("id","1");
    Blog blog1 = mapper.selectBlog(hashMap);
    System.out.println(blog1);
    System.out.println(blog1 == blog2);
    sqlSession.close();
}

image-20200820095557412

从connection创建到将connection放置回pool的过程中,只进行了一次查询,而且判断这两个对象,是完全相同的,使用的是同一个索引

image-20200820095825673

查询id 1和id 2时就不一样的了,查询的不是同一个条件,所以查询了两次,这就是cache的区别,对查询相同的数据直接从缓存中拿

image-20200820100057068

遇到增删改操作之后缓存会被刷新,因为数据变更了,肯定要刷新,还会被不定期刷新

测试

  • @Test
    public void SqlSessionCacheTest(){
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        blogMapper mapper = sqlSession.getMapper(blogMapper.class);
        HashMap hashMap = new HashMap();
        hashMap.put("id","1");
        Blog blog2 = mapper.selectBlog(hashMap);
        System.out.println(blog2);
        hashMap.put("title","asdf");
        hashMap.put("author","zcv");
        hashMap.put("id","2");
        mapper.updateBlog(hashMap);
        sqlSession.commit();
        //记得要提交
        hashMap.put("id","1");
        Blog blog1 = mapper.selectBlog(hashMap);
        System.out.println(blog1);
        System.out.println(blog1 == blog2);
        sqlSession.close();
    }
    
  • image-20200820101240050

  • 缓存失效情况

    • 查看不同的东西

    • 增删改操作,改变原来的数据,必定刷新缓存

    • 查询不同的mapper.xml

    • 手动清理缓存

      • sqlSession.clearCache();
        
      • 加这一句就手动清理缓存

    • 一级缓存是默认开启的,也无法关闭

二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低,
  • 基于namespace级别的缓存,一个命名空间,对应一个二级缓存
  • 工作机制:
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
    • 如果会话关闭,一级缓存中的数据被保存到二级缓存中
    • 新建的会话查询信息,就可以从二级缓存中获取内容
    • 不同的mapper查出的数据会放在自己对应的缓存中

步骤:

  • 核心配置文件开启全局缓存

    • <settings>
          <setting name="mapUnderscoreToCamelCase" value="true"/>
          <setting name="logImpl" value="LOG4J"/>
          <setting name="cacheEnabled" value="true"/>
      </settings>
      
    • 虽然是默认开启的,但是显示一下

    • image-20200820102731482

  • 配置Mapper.xml

  • <mapper namespace="com.haoyun.dao.blogMapper">
    <!--    <cache/>-->
        <cache eviction="FIFO"
               flushInterval="600000"
               size="512"
               readOnly="true"/>
    
        <insert id="addBook" parameterType="Blog" flushCache="true">
    
  • 可以详细设置,也可以直接全局设置,对应的标签也能设置是否要加入缓存

  • image-20200820102527597

  • 测试就不测试了,多个sqlsession之间是能共用缓存的

  • 遇到错误提示,未序列化

  • 提示内容

    • Caused by:java.io.NotSerializableException:POJO对象
  • 解决方法就是去序列化POJO类

  • public class Blog implements Serializable {
    

小结:

只要开启了二级缓存在同一个mapper下就有效

所有的数据都会放在一级缓存中,只有当前会话提交,或者关闭的时候,才会提交到二级缓存中

缓存原理

image-20200820104043501

  • 先去二级缓存找,二级缓存没有再去一级缓存找,最后找数据库

  • 查询优化

    •     <insert id="addBook" parameterType="Blog" flushCache="false">
      
    • 比如这个插入太频繁了,根本用不到缓存,所以把它设置未false,不加入缓存

自定义缓存-ehcache

Ehcache是一种广泛使用的开源java分布式缓存,主要面向通用缓存

感知不强

流程:

  • 导包
  • 添加配置文件
  • 设置mapper.xml

这个就不整了,知道就行,反正之后也不会用

之后都是使用Redis数据库来做缓存

Redis是非关系型数据库,是K-V键值对形式的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值