MyBatis

MyBatis

0、环境配置

  • jdk: 1.8
  • maven: 3.6.3
  • mysql: 8.0.24
  • mybatis: 3.5.7

##1、简介

1. 1、什么是mybatis

  • MyBatis 是一款优秀的***持久层框架***,它支持自定义 SQL、存储过程以及高级映射。
  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
  • MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了[google code](https://baike.baidu.com/item/google code/2346604),并且改名为MyBatis。
  • 2013年11月迁移到Github

获取MyBatis

  • maven仓库:https://mvnrepository.com/search?q=mybatis

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>
    
  • github: https://github.com/mybatis/mybatis-3

  • 官方文档: https://mybatis.org/mybatis-3/zh/index.html

###1. 2、持久化

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

为什么需要持久化

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

1. 3、持久层

Dao层

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

1. 4、为什么需要mybatis?

  • 帮助程序员把数据存储到数据库中
  • 传统的jdbc代码太复杂。简化,框架,自动化

2、搭建一个Mybatis程序

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

2.1、环境搭建

  • 创建数据库,表

  • 创建项目

    1. 建一个普通maven项目

    2. 删除src目录(将该项目作为当前项目的父工程,通过引用到模块,不用重复导入依赖)

    3. 导入maven依赖

      <!--导入依赖-->
          <dependencies>
              <!--mybatis-->
              <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
              <dependency>
                  <groupId>org.mybatis</groupId>
                  <artifactId>mybatis</artifactId>
                  <version>3.5.7</version>
              </dependency>
              <!--mysql驱动-->
              <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>8.0.24</version>
              </dependency>
              <!--junit-->
              <dependency>
                  <groupId>junit</groupId>
                  <artifactId>junit</artifactId>
                  <version>4.13.1</version>
                  <scope>test</scope>
              </dependency>
          </dependencies>
      
    4. 新建maven模块

2. 2、创建一个模块

  • 编写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>
        <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="1507298022"/>
                </dataSource>
            </environment>
        </environments>
        
        <!--每一个Mapper.xml 都需要在mybatis核心配置文件中注册-->
        <mappers>
            <mapper resource="com/feng/dao/UserMapper.xml"/>
        </mappers>
    </configuration>
    
  • 编写mybatis工具类

    public class MybatisUtil {
    
        public static SqlSessionFactory sqlSessionFactory;
    
        static {
            try {
                // 获取SqlSessionFactory对象
                String resource = "mybatis-config.xml";
                InputStream inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        // 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
        // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
        // 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
        public static SqlSession getSqlSession(){
            return sqlSessionFactory.openSession();
        }
    }
    

2. 3、编写代码

  • 实体类

    public class User {
        private Integer id;
        private String name;
        private String paw;
    }
    
  • Dao接口

    public interface UserDao {
        List<User> getUserList();
    }
    
  • 接口实现类(UserMapper.xml/UserDao.xml 代替了原来的UserDaoImpl)

    <?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="com.feng.dao.UserDao">
        <!--查询语句 id=需要重写的方法  resultType=将结果封装到一个对应的类中-->
        <select id="getUserList" resultType="com.feng.pojo.User">
            select * from mybatis.user
        </select>
    </mapper>
    

2. 4、测试

可能会遇到的错误类型:Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource org/mybatis/example/BlogMapper.xml (配置文件没有注册)

  • 测试代码

    public class UserDaoTest {
        @Test
        public void test(){
            // 得到SqlSession对象
            SqlSession sqlSession = MybatisUtil.getSqlSession();
    
            try {
                // 获取接口对象
                UserDao mapper = sqlSession.getMapper(UserDao.class);
                // 调用方法
                List<User> userList = mapper.getUserList();
                for (User user : userList){
                    System.out.println(user);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 关闭资源
                sqlSession.close();
            }
        }
    }
    
  • 防止资源导出失败问题

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

    1. 配置文件没注册
    2. 绑定接口错误
    3. 方法名不对
    4. 返回类型不对
    5. maven导出资源问题

3、CRUD

CRUD: Create Read Updata Delete (增删改查)

<mapper namespace="com.feng.dao.UserMapper">
    <select id="getUserById" resultType="com.feng.pojo.User" parameterType="Integer">
        select * from mybatis.user where id = #{id}
    </select>
</mapper>

3. 1、参数信息:

  1. namespace:命名空间,接口的全类名
  2. id:当前接口的对应方法
  3. resultType:返回结果的类型(实体类),增删改没有
  4. parameterType:返回值类型,基本数据类型可以省略

3. 2、select

<select id="getUserById" resultType="com.feng.pojo.User" parameterType="Integer">
    select * from mybatis.user where id = #{id}
</select>

3. 3、insert

<!--parameterType 的参数必须全类名-->
<insert id="insertUser" parameterType="com.feng.pojo.User">
    insert into mybatis.user(id, name, paw) values (#{id}, #{name}, #{paw})
</insert>

3. 4、updata

<!--int 的参数类型可以不写-->
<update id="updateUser" parameterType="com.feng.pojo.User">
    update mybatis.user set name = #{name}, paw = #{paw} where id = #{id}
</update>

3. 5、delete

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

3. 6、测试

@Test
public void insertUser(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    mapper.insertUser(new User(1004, "丰", "1231223"));
    // 增删改需要提交事务
    sqlSession.commit();
    sqlSession.close();

注意点:增删改需要提交事务

3. 7、万能的map

在我们的实体类或数据库中的表的字段过多时,可以使用Map, 使用该方法可以不用到实体类,将对应的参数封装到Map中

// 对应的接口
int insertUser2(Map<String, Object> map);
<!--参数类型使用的是map, 字段名不用于数据库中的字段名一致, 对应添加的字段名为对应map的key-->
<insert id="insertUser2" parameterType="map">
    insert into mybatis.user(id, name, paw) values (#{userId}, #{UserName}, #{UserPaw})
</insert>
@Test
public void insertUser2(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Map<String, Object> map = new HashMap<>();
    map.put("userId", 1005);
    map.put("UserName", "02");
    map.put("UserPaw", "123456");
    mapper.insertUser2(map);
    // 增删改需要提交事务
    sqlSession.commit();
    sqlSession.close();
}
  • Map传递参数,直接在sql中取出key即可: parameterType=“map”
  • 对象传递参数,直接在对象中取对应的属性即可: parameterType=“Object”
  • 只有一个基本数据类型传递参数,可以直接在sql中取得
  • 多个参数,需要用到Map 或 注解

3. 8、模糊匹配

  • java执行代码时,传递通配符

    List<User> userLike = mapper.getUserLike("%五%");
    
  • 在sql中拼接通配符

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

4、配置文件

4. 1、configuration(配置)

4. 2、环境配置(environments)

  • MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中

  • 不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

  • MyBatis 的默认事务管理器(transactionManager):JDBC

  • 数据库连接池(dataSource):POOLED

4. 3、属性(properties)

  • 属性可以在外部进行配置,并可以进行动态替换。
  • 你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置
  1. 编写一个配置文件(jdbc.properties)

    driver=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
    username=root
    password=1507298022
    
  2. 在核心配置(mtbatis-config.xml)中引入

     <!--引入外部文件-->
    <properties resource="jdbc.properties">
        <!--设置属性,优先级低于文件中读取出来的优先级-->
        <property name="username" value="dev_user"/>
        <property name="password" value="F2Fa3!33TYyg"/>
    </properties>
    
  3. 动态配置属性值

    <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
    </dataSource>
    

4. 4、类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。后面使用到该类名时,可以直接通过别名引入。

<typeAliases>
    <!--方式一,通过引用指定类-->
    <typeAlias alias="User" type="com.feng.pojo.User"/>
    <!--方式二,通过引用包,直接使用类名调用,若实体类有注解@Alias("xxx"),则使用xxx调用-->
    <package name="com.feng.pojo"/>
</typeAliases>

4. 5、设置(settings)

官网说明: https://mybatis.org/mybatis-3/zh/configuration.html#settings

配置方法:

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

4. 6、映射器(mappers)

SQL 映射语句。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。

  • 方式一: 常用

    <!-- 使用相对于类路径的资源引用 -->
    <mappers>
      <mapper resource="com/feng/dao/UserMapper.xml"/>
    </mappers>
    
  • 方法二

    <!-- 使用映射器接口实现类的完全限定类名 -->
    <mappers>
      <mapper class="com.feng.dao.UserMapper"/>
    </mappers>
    
  • 方式三

    <!-- 将包内的映射器接口实现全部注册为映射器 -->
    <mappers>
      <package name="org.mybatis.builder"/>
    </mappers>
    

    注意:方式二和方式三要求,类名与对应的xml文件名相同,并且需要在同一个包下

4. 7、生命周期和作用域

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

SqlSessionFactoryBuilder
  • 一旦创建了 SqlSessionFactory,就不再需要它了。
  • 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)
SqlSessionFactory
  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
  • SqlSessionFactory 的最佳作用域是应用作用域。
  • 最简单的就是使用单例模式或者静态单例模式。
  • 可以想象为数据库连接池。
SqlSession
  • 链接到链接池的一个请求。
  • 每个线程都应该有它自己的 SqlSession 实例。
  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
  • 用完之后,马上关闭,防止资源被占用。

5、结果映射

  • resultMap 元素是 MyBatis 中最重要最强大的元素。
  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

解决属性名和字段名不一致问题(resultMap)

<!--resultMap映射,将属性和对应的字段联系起来(解决属性名与字段名不一致的问题)-->
<resultMap id="UserMap" type="User">
    <result property="password" column="paw"/>
</resultMap>

<!--查询语句 id=需要重写的方法-->
<select id="getUserList" resultMap="UserMap">
    select * from mybatis.user
</select>
  • resultMap标签: 将 type 中的实体类映射到 id 属性值中,需要与select标签中的resultMap属性值对应
    1. property:对象的属性名
    2. column:数据库表的字段名

6、日志

在 settings 标签中设置 logImpl 属性的值

  • SLF4J
  • LOG4J(deprecated since 3.5.9) (需要掌握)
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING (需要掌握)
  • NO_LOGGING

6. 1、STDOUT_LOGGING

idea自带的日志信息

<settings>
    <!--配置日志信息-->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

6. 2、LOG4J(常用)

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

    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  2. 配置log4j

    <setting name="logImpl" value="LOG4J"/>
    
  3. 建立 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/shun.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
    

    常用的Log4j 的日志信息

    static Logger logger = Logger.getLogger(UserDaoTest.class);
    @Test
    public void log4j(){
        logger.info("info进入了log4jTest");
        logger.debug("debug进入了log4jTest");
        logger.error("error进入了log4jTest");
    }
    

7、分页

7. 1、使用 limit 分页(sql层面)

  • 编写接口方法

    // 需要两个参数(StartIndex, pageSize),多个参数可以用map封装
    List<User> getLimitUser(Map<String, Integer> map);
    
  • 在对应的配置文件中编写sql语句

        <!--分页-->
        <select id="getLimitUser" parameterType="map" resultType="User">
            select * from mybatis.user limit #{startIndex}, #{pageSize}
        </select>
    
  • 测试

    @Test
    public void getLimitUser(){
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Map<String, Integer> map = new HashMap<>();
        map.put("startIndex", 1);
        map.put("pageSize", 3);
        List<User> limitUser = mapper.getLimitUser(map);
        System.out.println(limitUser);
        sqlSession.close();
    }
    

7. 2、RowBounds分页(java层面)

  • 编写接口方法

    // RowBounds分页
    List<User> getRowBoundsUserList();
    
  • 配置文件

    <!--使用RowBounds分页-->
    <select id="getRowBoundsUserList" resultMap="UserMap">
        select * from mybatis.user
    </select>
    
  • 测试

    @Test
    public void getRowBoundsUserList(){
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        RowBounds rowBounds = new RowBounds(1, 3);
        List<User> User = sqlSession.selectList("com.feng.dao.UserMapper.getRowBoundsUserList", null, rowBounds);
        System.out.println(User);
        sqlSession.close();
    }
    

7. 3、分页插件(PageHelper)

需要时,查看文档: https://pagehelper.github.io/

8、使用注解开发

  • 它们映射的语句可以不用 XML 来配置,而可以使用 Java 注解来配置。
  • 使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心。
  • 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
  1. 注解在接口上实现

    @Select("select * from user")   // 在注解中写sql语句
    List<User> getUserList();
    
  2. 在核心配置文件中绑定接口

    <mappers>
        <!--<mapper class="com.feng.dao.UserMapper"/>-->
        <package name="com.feng.dao"/>
    </mappers>
    

本质:反射机制

底层:动态代理

8. 1、CRUD

  • 接口

        // 方法存在多个参数时,所有的参数前面都要加上 @Param("xx") 注解
        @Select("select * from user where id = #{id}")
        User getUserById(@Param("id") Integer id);
    
        @Insert("insert into user(id, name, paw) values (#{id}, #{name}, #{password})")
        int insertUser(User user);
    
        @Update("update user set name = #{name}, paw = #{password} where id = #{id}")
        int updateUser(User user);
    
        // 参数为一个时,可以省略@Param("xx")
        @Delete("delete from user where id = #{id}")
        int deleteUser(Integer id);
    

关于@Param() 注解

  • 基本数据类型与String类型需要加上
  • 引用数据类型不用加,字段名需要与属性名一致
  • 如果只有一个基本数据类型,可以不用加,建议加上
  • 在sql中引用的是@Param()中设置的属性名

8. 2、Lombok

作用:使用注解的方式自动生成实体类的get,set,toString 等方法,使得代码不那么臃肿

使用:1.安装Lombok插件 2. 导入Lombok包(使用maven)

9、多表处理

创建studentteacher

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');
  • 多个学生,对应一个老师
  • 对于学生而言,多个学生,关联一个老师(多对一) 关联
  • 对于老师而言,一个老师,有很多学生(一对多) 集合

9. 1、多对一处理

测试环境搭建

  1. 创建实体类 Teacher, Student
  2. 创建对应的Mapper接口,每一个实体类对应一个Mapper接口
  3. 创建对应的Mapper.xml配置文件
  4. 在核心配置文件中绑定注册我们的Mapper接口或xml文件
  • sql语句

    select s.id, s.name, s.tid, t.name
    from mybatis.student s join mybatis.teacher t on t.id = s.tid
    
  • 按照查询嵌套处理

    <!--思路:
            1、查询出所有的学生信息
            2、根据查询出来的学生的tid,查出对应的老师
        -->
    <!--按照查询嵌套处理-->
    <select id="getStudent" resultMap="StudentTeacher">
        select * from mybatis.student
    </select>
    <resultMap id="StudentTeacher" type="Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <!--复杂的类型,需要单独处理 对象:association     集合:collection-->
        <!--select  嵌套查询,与下面一个select的id对应(映射)-->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    <select id="getTeacher" resultType="Teacher">
        select * from mybatis.teacher
    </select>
    
  • 按照结果嵌套处理

    <!--按照结果嵌套处理-->
    <select id="getStudent2" resultMap="StudentTeacher2">
        select s.id sid, s.name sname, t.id tid, t.name tname
        from mybatis.student s join mybatis.teacher t on 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="id" column="tid"/>
            <result property="name" column="tname"/>
        </association>
    </resultMap>
    

9. 2、一对多处理

  • sql

    select t.id, t.name, s.id, s.name
    from mybatis.student s join mybatis.teacher t
    on t.id = s.tid where t.id = 1;
    
  • 接口方法

    Teacher getTeacher(@Param("tid") Integer id);
    
  • 按查询嵌套处理

    <select id="getTeacher" resultMap="TeacherStudent">
        select * from mybatis.teacher where id = #{tid}
    </select>
    <resultMap id="TeacherStudent" type="Teacher">
        <result property="id" column="id"/>
        <collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudent"/>
    </resultMap>
    <select id="getStudent" resultType="Student">
        select  * from mybatis.student
    </select>
    
  • 按照结果嵌套处理

    <select id="getTeacher2" resultMap="TeacherStudent2">
        select t.id tid, t.name tname, s.id sid, s.name sname
        from mybatis.student s join mybatis.teacher t
        on t.id = s.tid where t.id = #{tid}
    </select>
    <resultMap id="TeacherStudent2" 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>
    

9. 3、小结

  • 关联:association (多对一)
  • 集合:collection (一对多)
  • javaType:用来指定实体类的中的属性类型
  • ofType:用来指定映射到集合中的pojo类型,泛型中的约束类型

10、动态sql

根据条件动态调整sql语句(sql拼接)

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

10. 1、环境搭建

  • 创建数据表

    CREATE TABLE blog(
    	id INT(10) NOT NULL COMMENT '博客id',
    	title VARCHAR(50) NOT NULL COMMENT '博客标题',
    	author VARCHAR(30) NOT NULL COMMENT '博客作者',
    	create_time datetime NOT NULL COMMENT '创建时间',
    	`view` int(30) NOT NULL COMMENT '浏览量'
    );
    
  • 创建实体类

    public class Blog {
        private int id;
        private String title;
        private String author;
        private Date createTime;
        private int view;
    }
    
  • 编写对应的Mapper接口和Mapper.xml文件

10. 2、if, where

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

10. 3、choose、when、otherwise

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

10. 4、trim (where, set)

<!--自定义分割-->
<trim prefix="SET" suffixOverrides=",">
  <!--这里的代码与where/set相同-->
</trim>
<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>

所谓的动态sql,本质还是SQL语句,只是在sql层面去执行了逻辑代码

10. 5、foreach

<select id="queryBlogFor" parameterType="map" resultType="Blog">
    select * from mybatis.blog
    <where>
        <if test="ids != null">
            <foreach collection="ids" item="id" open="id in (" close=")" separator=",">
                #{id}
            </foreach>
        </if>
    </where>
</select>

万能的map集合中放有一个集合/数组,遍历出来的即为这个集合/数组中的值

  • cllection:当前存放集合/数组的map对应的key
  • item:遍历出来的值放入item中
  • open/close:拼接以什么开头,以什么结尾
  • separator:以什么分割

10. 6、SQL片段

如果有需要多次使用的sql标签,可以将其抽取出来,方便复用

<!--抽取出sql标签的公共部分-->
<sql id="if_id_author">
    <if test="id != null">
        id = #{id}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</sql>

<select id="queryBlogIf" parameterType="map" resultType="Blog">
    select * from mybatis.blog
    <where>
        <!--include表签引用-->
        <include refid="if_id_author"></include>
    </where>
</select>

11、mybatais缓存

  • MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。

  • 默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。

  • 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行()。

  • 映射语句文件中的所有 select 语句的结果将会被缓存。

  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。

  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。

  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。

  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。

  • 以后如果需要获取相同的数据,直接在缓存中拿,就不用再去查数据库了。

11. 1、一级缓存(本地缓存)

作用域:数据库同一次会话期间,当前SqlSession内有效,sqlSession.close()后,清空当前缓存

一级缓存是默认开启的,sqlSession.clearCache() 手动清除缓存

11. 2、二级缓存(全局缓存)

  • 因为二级缓存的作用域太低了,所以有了二级缓存
  • 基于namespace级别的缓存
  • 工作机制
    • 一个会话查询出一条数据,这个数据不会马上保存到二级缓存中,而是在一级缓存中
    • 当当前会话关闭,这个会话缓存就没了,此时,保存在当前一级缓存的内容才会加载到二级缓存中
    • 新的会话查询信息,会先从二级缓存中取相关内容,如果没有,再去一级缓存中去拿,再没有,就去查数据库
    • 不同的mapper查出来的数据会放在自己的缓存中(map)

开启二级缓存,可以自定义参数

<cache
  eviction="FIFO"    // 输入输出缓存
  flushInterval="60000"     // 60秒刷新一次缓存
  size="512"      // 最多可以存储结果对象或列表的 512 个引用
  readOnly="true"/>  // 返回的对象被认为是只读的

若没有配置 eviction=“FIFO” ,还需要将实体类序列化

11. 3、自定义缓存

ehcache: 需要的时候查文档 https://www.ehcache.org/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值