zoukankan      html  css  js  c++  java
  • Mybatis

    Mybatis介绍

    • MyBatis 是一款优秀的持久层框架

    • 它支持定制化 SQL、存储过程以及高级映射。

    • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。

    • MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

    • MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。

    • 2013年11月迁移到Github。

    • 获得Mybatis

    • maven仓库:

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

    使用流程

    • 新建一个Maven项目
    • 导入依赖
    • 修改pom配置文件
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.example</groupId>
        <artifactId>Mybatis-Study</artifactId>
        <packaging>pom</packaging>
        <version>1.0-SNAPSHOT</version>
        <modules>
            <module>Mybatis-01</module>
        </modules>
    
        <dependencies>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.15</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.2</version>
            </dependency>
    
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
    
        </dependencies>
        <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>
    
    </project>
    
    
    
    • 编写mybatis的核心配置文件
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&amp;useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
        // 注册核心配置文件
        <mappers>
            <mapper resource="dao/UserMapper.xml"/>
        </mappers>
    </configuration>
    
    • 编写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,顾名思义,我们就可以从中获得 SqlSession 的实例了。
        // SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。
        public static SqlSession  getSqlSession(){
            return sqlSessionFactory.openSession();
        }
    
    }
    
    • 编写接口
    • 编写mapping配置文件
    <?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">
        <!--namespace=绑定一个对应的Dao/Mapper接口-->
        <mapper namespace="dao.UserDao">
    <!--select查询语句-->
        <select id="getUserList" resultType="pojo.User">
            select * from mybatis.user
        </select>
    </mapper>
    

    CRUD

    • namespace:namespace中的包名要和 Dao/mapper 接口的包名一致!

    • select、insert、update、delete

    选择,查询语句;

    • id : 就是对应的namespace中的方法名;
    • resultType:Sql语句执行的返回值!
    • parameterType : 参数类型!
    • 增删改需要提交事务

    • 使用map传递参数

        //万能的Map
        int addUser2(Map<String,Object> map);
    
    
    
        <!--对象中的属性,可以直接取出来    传递map的key-->
        <insert id="addUser" parameterType="map">
            insert into mybatis.user (id, pwd) values (#{userid},#{passWord});
        </insert>
    
        SqlSession sqlSession = MybatisUtils.getSqlSession();
    
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
        Map<String, Object> mp = new HashMap<String, Object>();
        mp.put("userid", 2);
        mp.put("userName", "Kawhi Leonard");
        mp.put("password", "223333");
    
        int res = mapper.addUser2(mp);
        if(res > 0) {
            System.out.println("Success!");
        }
    
        sqlSession.commit();
        sqlSession.close();
    
    • 模糊查询
    1. Java代码执行的时候,传递通配符 % %

      List<User> userList = mapper.getUserLike("%李%");
      
    2. 在sql拼接中使用通配符!

      select * from mybatis.user where name like "%"#{value}"%"
      

    配置

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

    • Environments: 可以配置成多种环境,但SQLSessionFactory实例只能选择一种环境

    默认的事务管理器: JDBC, 连接池:POOLED

    • properties:实现引用的配置文件、优先使用外部文件、可增加属性配置

    db.properties:

        driver=com.mysql.cj.jdbc.Driver
        url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useSSL=true&useUnicode=true&characterEncoding=utf8&useSSL=true
        username=root
        password=root
    

    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>
        <properties resource="db.properties"/>
    
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="${driver}"/>
                    <property name="url" value="${url}"/>
                    <property name="username" value="${username}"/>
                    <property name="password" value="${password}"/>
                </dataSource>
            </environment>
        </environments>
    
        <mappers>
            <mapper resource="dao/UserMapper.xml"/>
        </mappers>
    </configuration>
    
    • typeAliases:别名, java类型设置一个短名字,用于减少完全限定名的冗余
    1. 给实体类起别名: 实体类较少
    <typeAliases>
        <typeAlias type="pojo.User" alias="User"/>
    </typeAliases>
    
    1. 指定一个包名:实体类较多
      扫描实体类的包,默认的别名就是这个类的类名,首字母小写
    <typeAliases>
        <package name="pojo"/>
    </typeAliases>
    

    可在第二种方式上加注解

    @Alias("user")
    public class User{}
    
    • 设置
    • mappers 映射器
    1. resource: 推荐使用
    2. class: 接口实现类的完全限定类名
      注意:接口和mapper配置文件必须同名、必须在同一个包内
    3. name: 将包内的映射器接口实现全部注册为映射器
      注意:接口和mapper配置文件必须同名、必须在同一个包内
    • 生命周期和作用域:错误使用会导致严重并发问题

    start -> SqlSessionFactoryBuilder -> SqlSessionFactory -> SqlSession -> mapper -> end

    1. SqlSessionFactoryBuilder一旦创建,就不再用了, 局部变量
    2. SqlSessionFactory -> 数据库连接池 :运行期间一直存在、没有理由丢弃他或重新创建另外一个实例, 最佳作用域:应用作用域,使用单例模式或静态单例模式
    3. SqlSession -> 连接到连接池的一个请求,不能被共享,用完需要关闭

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

        <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" parameterType="int" resultMap="UserMap">
            select * from mybatis.user where id = #{id};
        </select>
    

    日志

    日志工厂:用于排错

    • LOG4J
       <settings>
           <setting name="logImpl" value=""/>
       </settings>
    
    • STDOUT_LOGGING :标准
        <settings>
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>
    

    分页:减少数据处理量

    limit(推荐使用)

        List<User> getUserByLimit(Map<String, Integer> mp);
    
    <select id="getUserByLimit" parameterType="map"  resultType="User">
            select * from mybatis.user limit #{startIndex}, #{pageSize};
        </select>
    

    rowBounds

    不再使用SQL实现分页

    1. 接口

      //分页2
      List<User> getUserByRowBounds();
      
    2. mapper.xml

      <!--分页2-->
      <select id="getUserByRowBounds" resultMap="UserMap">
          select * from  mybatis.user
      </select>
      
    3. 测试

      @Test
       public void getUserByRowBounds() {
           SqlSession sqlSession = MybatisUtils.getSqlSession();
      
       //RowBounds实现
           RowBounds rowBounds = new RowBounds(1, 2);
      
       //通过Java代码层面实现分页
           List<User> userList = sqlSession.selectList("com.kuang.dao.UserMapper.getUserByRowBounds",null,rowBounds);
      
           for (User user : userList) {
               System.out.println(user);
           }
      
           sqlSession.close();
       }
      

    使用注解开发

    • 面向接口开发:定义与实现的分离
    • 使用注解映射简单的语句会使代码变得简洁,但对于复杂的语句,建议使用xml映射语句
    • 本质是利用了反射机制
    • 底层是代理模式

    Mybatis执行流程

    Resources获取全局配置文件 -> 实例化SQLSessionFactoryBuilder构造器 -> 解析配置文件流 -> Configuration所有配置信息 -> SQLSessionFactory实例化 ->transaction事务管理 -> 创建executor执行器 -> 创建SQLSession -> 实现CRUD -> 提交事务or回滚

    使用注解实现CRUD

    @Select("select * from user")
    List<User> getUsers();
    
    // 多个基本数据类型的参数必须使用@Params
    @Select("select * from user where id = #{id}")
    User getUserById(@Param("id") int id);
    
    @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);
    
    @Delete("delete from user where id = #{uid}")
    int deleteUser(@Params("uid") int id);
    
    public class MybatisUtils {
        private static SqlSessionFactory sqlSessionFactory;
        static {
            try {
                String resource = "mybatis-config.xml";
                InputStream inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 自动提交事务
        public static SqlSession getSqlSession() {
            return sqlSessionFactory.openSession(true);
        }
    }
    

    @Params()注解:

    • 基本类型参数需要加上
    • 引用类型不用加
    • 如果只有一个基本类型、可以忽略
    • 在Sql中引用的就是@Params中设置的属性

    lombok

    @Data:无参构造,get、set、tostring、hashcode,equals
    @AllArgsConstructor
    @NoArgsConstructor
    @EqualsAndHashCode
    @ToString
    @Getter
    

    复杂查询环境搭建

    多对一关系:多个学生对应一个老师

    方式一:按照查询嵌套处理
    • 查询所有学生
    • 根据每个查询出来的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">
        select * from teacher where id = #{id};
    </select>
    
    方式二:按照结果嵌套处理
    <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>
    

    一对多关系:一个老师拥有多个学生

    按照结果嵌套
    <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 = #{id};
    </select>
    
    <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="getTeacher2" resultMap="TeacherStudent2">
        select * from teacher where id = #{id};
    </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 student where tid = #{tid};
    </select>
    
    • 关联-association
    • 集合-collection
    • javaType:指定实体类属性的类型
    • ofType: 用来指定映射到List或集合中的pojo实体类

    动态SQL:根据不同的条件生成不同是SQL

    • IF
    <select id="queryBlogIF" parameterType="map" resultType="Blog">
        select * from blog where 1=1
        <if test="title != null">
            and title = #{title}
        </if>
        <if test="author">
            and author = #{author}
        </if>
    </select>
    
    • where
    <select id="queryBlogIF" parameterType="map" resultType="Blog">
        select * from blog
        <where>
            <if test="title != null">
                and title = #{title}
            </if>
            <if test="author">
                and author = #{author}
            </if>
        </where>
    </select>
    
    • choose:相当于java中的switch
    • set:智能增删逗号
    • sql:代码复用,抽取公共部分
    <sql id="if-title-author">
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author">
            and author = #{author}
        </if>
    </sql>
    
    
    <select id="queryBlogIF" parameterType="map" resultType="blog">
        select * from blog
        <where>
            <include refit="if-title-author"></include>
        </where>
    </select>
    
    • foreach:对一个集合进行遍历
    select * from user where 1=1 and 
      <foreach item="id" collection="ids"
          open="(" separator="or" close=")">
            #{id}
      </foreach>
    (id=1 or id=2 or id=3)
    
    
    <!--
            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>
            <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                id = #{id}
            </foreach>
        </where>
    </select>
    

    缓存

    查询 : 连接数据库 ,耗资源!
    一次查询的结果,给他暂存在一个可以直接取到的地方!--> 内存 : 缓存

    1. 什么是缓存 [ Cache ]?

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

      • 减少和数据库的交互次数,减少系统开销,提高系统效率。
    3. 什么样的数据能使用缓存?

      • 经常查询并且不经常改变的数据。【可以使用缓存】

    缓存失效的情况:

    1. 查询不同的东西
    2. 增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
    3. 查询不同的Mapper.xml
    4. 手动清理缓存!

    小结:一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段!

    一级缓存就是一个Map。

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

    步骤:

    1. 开启全局缓存

      <!--显示的开启全局缓存-->
      <setting name="cacheEnabled" value="true"/>
      
    2. 在要使用二级缓存的Mapper中开启

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

      也可以自定义参数

      <!--在当前Mapper.xml中使用二级缓存-->
      <cache  eviction="FIFO"
             flushInterval="60000"
             size="512"
             readOnly="true"/>
      

    小结:

    • 只要开启了二级缓存,在同一个Mapper下就有效
    • 所有的数据都会先放在一级缓存中;
    • 只有当会话提交,或者关闭的时候,才会提交到二级缓冲中!
    • 缓存原理
    1. 每一个SQLSession都有一级缓存, 第一次查询访问数据库、放到一级缓存中
    2. 当SQLSession关闭时,其中的一级缓存放入Mapper的二级缓存中

    缓存顺序:

    1. 先看二级缓存有没有
    2. 再看一级缓存有没有
    3. 查数据库
  • 相关阅读:
    matlab cell
    matlab linux 快捷键设置——有问题还是要解决
    latex 小结
    TOJ 1258 Very Simple Counting
    TOJ 2888 Pearls
    HDU 1248 寒冰王座
    TOJ 3486 Divisibility
    TOJ 3635 过山车
    TOJ 1840 Jack Straws
    HDU 4460 Friend Chains
  • 原文地址:https://www.cnblogs.com/Hot-machine/p/13320964.html
Copyright © 2011-2022 走看看