Mybatis

注意:

  1. 实体类需要默认构造器
  2. 实体类的属性名要么跟数据库的列名保持一致,要么使用resultMap
  3. 在映射中,property对应的java属性需要区分大小写,column不分大小写
  4. 在xml配置sql语句需要换行时,需要在行首至少加一个空格,不然在执行中sql语句或许会因为少空格导致语句混乱
  5. resultMap的column,如果取了别名的话,也要改成别名。column其实就是最后生成的表的列名,以结果为值。
  6. 在注解开发中,无论需不需要xml或者有没有使用xml开发,但是在resources对应的路径下有对应的xml配置文件,都会报错,这是在Mybatis在内部加载中会出现的。要解决的话,把配置文件删掉或者换到另一个地方。不过一般来说一个dao要么使用全注解开发,要么使用全xml开发,所以不太会出现这种问题
  7. 不要忘记提交commit事务

Mybatis框架
第一天 :Mybatis入门

  1. Mybatis的概述
  2. Mybatis入门案例
  3. . 自定义Mybatis(这里的主要目的是)是为了让大家了解Mybatis的执行细节

第二天 :Mybatis的单表CRUD操作

  1. Mybatis的参数和返回值
  2. Mybatis的dao编写
  3. Mybatis配置的细节(几个标签的使用)

第三天:Mybatis的深处和多表

  1. Mybatis的连接池
  2. Mybatis的事务控制及设计的方法
  3. Mybatis的多表查询

第四天 :Mybatis的缓存和注解开发

  1. Mybatis中的加载时间
  2. Mybatis中的一级缓存和二级缓存
  3. Mybatis的注解开发

前言

1、什么是框架?
它是我们软件开发中的一套解决方案,不同的框架解决的是不同的问题
使用框架的好处:
                    框架封装了很多细节,使开发者可以使用极简的方式实现功能。大大提高开发效率。

2、三层架构
在这里插入图片描述
在这里插入图片描述

3、持久层技术解决方案
在这里插入图片描述

Mybatis框架概述

dddd
432432
Mybatis的环境搭建
在这里插入图片描述

1、导入Mybatis框架的jar包依赖
在这里插入图片描述

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

2、导入mysql的依赖

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>

3、导入日志log4j的依赖

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

4、导入测试junit的依赖

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>

创建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">

<!-- mybatis的主配置文件 -->
<configuration>
    <!-- 配置环境 -->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(连接池) -->
            <dataSource type="POOLED">
                <!-- 配置连接数据库的4个基本信息-->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=utf8"/>
                <property name="username" value="laodu1"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 指定映射配置文件的位置,映射配置文件值的是每个dao独立的配置文件 -->
    <mappers>
        <mapper resource="com/itheima/dao/IUserDao.xml"/>
    </mappers>
</configuration>

创建Mybatis的映射配置文件

<!-- 先导入协议 -->
<?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.itheima.dao.IUserDao">

	        <!-- 配置查询所有
            id:要配置的方法名
            resultType : 要封装的类型
		    -->
    <select id="findAll" resultType="com.itheima.domain.User">
        select * from user
    </select>
</mapper>

在resources导入log4j文件

log4j.rootCategory=debug, CONSOLE, LOGFILE

log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

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

Mybatis的入门案例

    public static void main(String[] args) throws Exception{
        //1、读取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2、创建一个SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3、使用工厂生产一个SqlSession对象
        SqlSession session = factory.openSession();
        //4、使用Sqlsession创建Dao接口的代理对象
        IUserDao userDao = session.getMapper(IUserDao.class);
        //5、使用代理对象执行方法
        List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }
        //6、释放资源
        session.close();
        in.close();
    }

Mybatis基于注解的入门案例

	在方法前面加入@Select注解
    @Select("select * from user")
    List<User> findAll();
    <!-- 指定映射配置文件的位置,映射配置文件值的是每个dao独立的配置文件
         如果使用注解来配置,此处应该使用class属性指定被注解的dao全限定类名
        -->
    <mappers>
        <mapper class="com.itheima.dao.IUserDao"/>
    </mappers>

手写Dao实现类

public class UserDaoImpl implements IUserDao {
    SqlSessionFactory sessionFactory = null;

    public UserDaoImpl(SqlSessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public List<User> findAll() {
        SqlSession session = sessionFactory.openSession();
        List<User> user = session.selectList("com.itheima.dao.IUserDao.findAll");//通过namespace的全限定类名+方法名定义到配置的sql语句
        session.close();
        return user;
    }
}
        //1、读取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2、创建一个SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3、使用工厂生产一个SqlSession对象
        IUserDao userDao = new UserDaoImpl(factory);
        List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }
        //6、释放资源
        in.close();

入门案例的分析

在这里插入图片描述

Mybatis开发流程图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Mybatis的参数和返回值

**单表CRUD操作** 映射文件

<?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.itheima.dao.IUserDao">
    <!-- 查询所有-->
    <select id="findAll" resultType="com.itheima.domain.User">
        select * from user;
    </select>

    <!-- 保存用户-->
    <insert id="saveUser" parameterType="com.itheima.domain.User">
        <!-- 插入操作后,获取插入数据的id -->
        <!-- selectKey 将插入数据库的自增id传进对象的Id中
                keyProperty :实体类对对象的属性,要与对象的属性名对应
                keyColumn : 数据库的类名
                resultType : 返回类型
                order : 执行的时间,after(插入后执行)
         -->
        <selectKey keyProperty="id" keyColumn="id" resultType="Integer" order="AFTER">
            select last_insert_id();
        </selectKey>
        insert into user(username, address, sex, birthday) values(#{username}, #{address}, #{sex}, #{birthday});
    </insert>

    <!-- 更新用户-->
    <update id="updateUser" parameterType="com.itheima.domain.User">
        update user set username=#{username}, address=#{address}, sex=#{sex}, birthday=#{birthday} where id=#{id};
    </update>

    <!-- 删除用户-->
    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id = #{id}
    </delete>

    <!-- 根据id查询用户-->
    <select id="findById" parameterType="Integer" resultType="com.itheima.domain.User">
        select * from user where id = #{id}
    </select>
    
    <!-- 根据Id模糊查询 -->
    <select id="findByName" parameterType="String" resultType="com.itheima.domain.User">
   <!--         select * from user where username like #{username} -->
            select * from user where username like '%${value}%' <!--必须得是value,固定搭配-->
    </select>


    <!-- 获取用户的总记录条数-->
    <select id="findTotal" resultType="Integer">
        select count(id) from user;
    </select>
</mapper>

测试类

package com.itheima.test;

import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;

/**
 * 测试Mybatis的CRUD操作
 */
public class MybatisTest {
    private InputStream in;
    private SqlSession sqlSession;
    private IUserDao userDao;

    @Before//用于在测试方法执行之前执行
    public void init() throws IOException {
        //1、读取配置文件,生成字节流输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2、获取SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3、获取SqlSession对象
        sqlSession = factory.openSession();
        //4、获取dao代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After//用于在测试方法之后执行
    public void destory() throws Exception{
        //提交事务
        sqlSession.commit();

        sqlSession.close();
        in.close();
    }

    /**
     * 测试查询所有
     */
    @Test
    public void testfindAll() throws IOException {
        //5、执行查询所有方法
        List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }
    }

    /**
     * 测试保存操作
     */
    @Test
    public void testSave() throws IOException{
        User user = new User();
        user.setUsername("mybatis last insertid");
        user.setAddress("北京市顺义区");
        user.setSex("男");
        user.setBirthday(new Date());
        //5、执行保存方法
        userDao.saveUser(user);

        System.out.println(user);

    }

    @Test
    public void testUpdate() throws IOException{
        User user = new User();
        user.setId(41);
        user.setUsername("老王更新啦");
        user.setAddress("北京市顺义区");
        user.setSex("女");
        user.setBirthday(new Date());
        //5、执行更新方法
        userDao.updateUser(user);

    }

    @Test
    public void testDelete() throws IOException{
        //5、执行删除方法
        userDao.deleteUser(44);

    }

    @Test
    public void testFindOne() throws IOException{
        //5、执行查询一个方法
        User user = userDao.findById(41);
        System.out.println(user);

    }

    /**
     * 测试模糊查询操作
     * @throws IOException
     */
    @Test
    public void testFindByName() throws IOException{
//        List<User> users = userDao.findByName("%王%");
        List<User> users = userDao.findByName("王");
        for (User user : users) {
            System.out.println(user);
        }

    }

    /**
     * 查询总记录条数
     * @throws IOException
     */
    @Test
    public void testFindToal() throws IOException{
        System.out.println(userDao.findTotal());

    }
}

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

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

映射配置文件

    <!-- 根据Id模糊查询 -->
    <select id="findUserByVo" parameterType="com.itheima.domain.queryVo" resultType="com.itheima.domain.User">
        select * from user where username like #{user.username}
    </select>

实体类

package com.itheima.domain;

public class queryVo {
    User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

接口增加方法

    /**
     * 根据queryVo中的条件查询
     * @param vo
     * @return
     */
    List<User> findUserByVo(queryVo vo);

测试类

    @Test
    public void testFindByVo() throws IOException{
//        List<User> users = userDao.findByName("%王%");
        queryVo vo = new queryVo();
        User user = new User();
        user.setUsername("%王%");
        vo.setUser(user);
        List<User> users = userDao.findUserByVo(vo);
        for (User user2 : users) {
            System.out.println(user2);
        }
    }

解决实体类属性和数据库列名不对应的两种方式:
一、起别名:

select id as userId, username as userName, address as userAddress, sex as userSex, birthday as userBirthday from user

二、配置查询结果的列名和实体类的属性名的对应关系(resultMap)

    <resultMap id="userMap" type="com.itheima.domain.User">
	    <!-- property : 实体类的属性(需要区分大小写)
 			column : 数据库的列名(不需要区分大小写)
 		-->
        <!-- 主键字段的对应 -->
        <id property="userId" column="id"></id>
        <!-- 非主键字段的对应 -->
        <result property="userName" column="username"></result>
        <result property="userAddress" column="address"></result>
        <result property="userSex" column="sex"></result>
        <result property="userBirthday" column="birthday"></result>
    </resultMap>

    <!-- 查询所有-->
    <select id="findAll" resultMap="userMap">
        select * from user;
    </select>

Mybatis的dao编写

各种方法的实现类写法

package com.itheima.dao.Impl;

import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import java.util.List;

public class UserDaoImpl implements IUserDao {
    private SqlSessionFactory factory;
    public UserDaoImpl(SqlSessionFactory factory) {
        this.factory = factory;
    }

    public List<User> findAll() {
        //1、根据factory获取Sqlsession对象
        SqlSession session = factory.openSession();
        //2、调用Sqlsession中的方法,
        List<User> users = session.selectList("com.itheima.dao.IUserDao.findAll");//参数就是能获取配置信息的key
        //3、释放资源
        session.close();
        return users;
    }

    public void saveUser(User user) {
        //1、根据factory获取Sqlsession对象
        SqlSession session = factory.openSession();
        //2、调用Sqlsession中的方法,
        session.insert("com.itheima.dao.IUserDao.saveUser", user);
        //3、提交事务
        session.commit();
        //4、释放资源
        session.close();
    }

    public void updateUser(User user) {
        //1、根据factory获取Sqlsession对象
        SqlSession session = factory.openSession();
        //2、调用Sqlsession中的方法,
        session.update("com.itheima.dao.IUserDao.updateUser", user);
        //3、提交事务
        session.commit();
        //4、释放资源
        session.close();
    }

    public void deleteUser(Integer UserId) {
        //1、根据factory获取Sqlsession对象
        SqlSession session = factory.openSession();
        //2、调用Sqlsession中的方法,
        session.update("com.itheima.dao.IUserDao.deleteUser", UserId);
        //3、提交事务
        session.commit();
        //4、释放资源
        session.close();
    }

    public User findById(Integer userId) {
        //1、根据factory获取Sqlsession对象
        SqlSession session = factory.openSession();
        //2、调用Sqlsession中的方法,
        User user = session.selectOne("com.itheima.dao.IUserDao.findById", userId);
        //4、释放资源
        session.close();
        return user;
    }

    public List<User> findByName(String username) {
        return null;
    }

    public int findTotal() {
        return 0;
    }
}

package com.itheima.test;

import com.itheima.dao.IUserDao;
import com.itheima.dao.Impl.UserDaoImpl;
import com.itheima.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;

/**
 * 测试Mybatis的CRUD操作
 */
public class MybatisTest {
    private InputStream in;
    private IUserDao userDao;

    @Before//用于在测试方法执行之前执行
    public void init() throws IOException {
        //1、读取配置文件,生成字节流输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2、获取SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3、使用工厂对象,创建dao对象
        //4、获取dao代理对象
        userDao = new UserDaoImpl(factory);
    }

    @After//用于在测试方法之后执行
    public void destory() throws Exception{
        in.close();
    }

    /**
     * 测试查询所有
     */
    @Test
    public void testfindAll() throws IOException {
        //5、执行查询所有方法
        List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }
    }

    /**
     * 测试保存操作
     */
    @Test
    public void testSave() throws IOException{
        User user = new User();
        user.setUsername("mybatis last insertid2222");
        user.setAddress("北京市顺义区");
        user.setSex("男");
        user.setBirthday(new Date());
        //5、执行保存方法
        userDao.saveUser(user);

        System.out.println(user);

    }

    @Test
    public void testUpdate() throws IOException{
        User user = new User();
        user.setId(41);
        user.setUsername("老王更新啦");
        user.setAddress("北京市顺义区");
        user.setSex("女");
        user.setBirthday(new Date());
        //5、执行更新方法
        userDao.updateUser(user);

    }

    @Test
    public void testDelete() throws IOException{
        //5、执行删除方法
        userDao.deleteUser(50);

    }

    @Test
    public void testFindOne() throws IOException{
        //5、执行查询一个方法
        User user = userDao.findById(41);
        System.out.println(user);

    }

    /**
     * 测试模糊查询操作
     * @throws IOException
     */
    @Test
    public void testFindByName() throws IOException{
//        List<User> users = userDao.findByName("%王%");
        List<User> users = userDao.findByName("王");
        for (User user : users) {
            System.out.println(user);
        }

    }

    /**
     * 查询总记录条数
     * @throws IOException
     */
    @Test
    public void testFindToal() throws IOException{
        System.out.println(userDao.findTotal());

    }

}

Mybatis配置的细节(几个标签的使用)

properties标签的使用

    <!-- 配置properties -->
    <properties>
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=utf8"/>
        <property name="username" value="laodu1"/>
        <property name="password" value="123456"/>
    </properties>

    <!-- 配置环境-->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置连接池-->
            <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>

另外添加properties文件来使用properties标签
注意:这里的properties文件里的&不用改成&,不然会报错

    <!-- 引入外部配置文件 -->
    <properties resource="jdbcConfig.properties"></properties>
    <!-- 配置环境  -->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
jdbc.username=laodu1
jdbc.password=123456

别名的配置以及Mappers的优化

    <!-- 使用typeAliases配置别名,它只能配置domain中类的别名  -->
    <typeAliases>
        <!-- typeAlises用于配置别名,type属性指定的是实体类全限定类名,alises属性指定别名, 当指定了别名就不再区分大小写
        <typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>-->

        <!-- 用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名不在区分大小写 -->
        <package name="com.itheima.domain"/>
    </typeAliases>

    <mappers>
        <!-- 配置映射文件的位置-->
        <!-- package标签是用于指定dao接口所在的包, 当指定之后,就不需要再写mapper以及resource或者class  -->
<!--        <mapper resource="com/itheima/dao/IUserDao.xml"></mapper>-->
        <package name="com.itheima.dao"/>
    </mappers>

Mybatis的连接池

在这里插入图片描述
Mybatis中的连接池:
Mybatis连接池提供了3中方法的配置:
配置的位置:
主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式:
type属性的取值:
POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
UNPOOLED 采用传统的获取连接的方式,虽然也实现了javax.sql.DataSource接口,但是没有使用池的思想
JNDI 采用服务器提供的JNDI技术实现,来获取Datasource对象,不同的服务器拿到的DataSource是不一样的
注意:如果不是web获取maven的war工程,是不能使用的
我们课程中使用的是tomcat服务器,采用的连接池就是dbcp连接池。

Mybatis的事务控制及设计的方法

where的两种创建方式(where标签+if标签)
这里要注意了,if标签里的test用的是sql的语法,不能用&&,只能用and

    <!-- 根据条件查询 -->
    <select id="findUserByCondition" resultMap="userMap" parameterType="com.itheima.domain.User">
<!--        select * from user where 1=1 -->
<!--        <if test="username != null">-->
<!--            and username = #{username}-->
<!--        </if>-->
    select * from user where username = #{username}
    </select>
    <select id="findUserByCondition" resultMap="userMap" parameterType="com.itheima.domain.User">

        select * from user
        <where>
            <if test="username != null">
                and username = #{username}
            </if>
            <if test="sex != null">
                and sex = #{sex}
            </if>
        </where>

    </select>

foreach标签

    <!-- 根据queryvo中的Id集合实现查询用户列表 -->
    <select id="findUserInIds" resultMap="userMap" parameterType="com.itheima.domain.queryVo">
        select * from user
        <where>
            <if test="ids != null and ids.size() > 0"></if>
            <!--
                collection : 代表要遍历的集合元素,注意编写时不要写#{}
                open : 代表语句的开始部分
                close : 代表结束部分
                item : 代表遍历集合的每个元素,生成的遍历名
                seperator : 代表分隔符
             -->
                <foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
                    #{uid}
                </foreach>
        </where>
    </select>

了解的内容(抽取重复代码块)

    <!-- 了解的内容:抽取重复的sql语句 -->
    <sql id="defaultUser">
        select * from user;
    </sql>

    
    <!-- 查询所有-->
    <select id="findAll" resultType="user">
        <include refid="defaultUser"></include>
       <!-- select * from user; -->
    </select>

Mybatis的多表查询

在这里插入图片描述
在这里插入图片描述
新建表account
在这里插入图片描述
创建外键和user表对应
在这里插入图片描述





一对一

的查询,对于每个account都能同时查到对应的user:

(1)方法一:

在account实体类中加入一个user对象,并加入get和set方法(从表实体应该包含一个主表实体的应用)

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    private User user;

将account里的所有属性全都和列名对应,

    <!-- 定义封装account和user的resultMap -->
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>

        <!-- 一对一的关系映射:配置封装user的内容 -->
        <association property="user"  javaType="user">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="address" column="address"></result>
            <result property="sex" column="sex"></result>
            <result property="birthday" column="birthday"></result>
        </association>
    </resultMap>

结果展示:
在这里插入图片描述
(2)方法二:

直接创建一个有所有属性的类,直接进行封装

public class AccountUser extends Account {
    private String username;
    private String address;
}
    <!-- 查询所有账户,并且带有用户名称和地址信息 -->
    <select id="findAllAccount" resultType="accountuser">
        select a.*, u.username, u.address  from account a, user u where u.id = a.uid
    </select>

一对多

(查询user时,同时出现对应的所有account信息):

一对多关系映射,主表实体应该包含从表实体的集合引用

public class User implements Serializable {
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    //一对多关系映射,主表实体应该包含从表实体的集合引用
    private List<Account> accounts;
}

使用左外连接和对应的resultMap

    <!-- 定义User的resultMap -->
    <resultMap id="userAccountMap" type="User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>

        <!-- 配置User对象中accounts集合的映射 -->
        <!-- ofType : 要写全限定类名(这里是因为起了别名)-->
        <collection property="accounts" ofType="account">
            <id property="id" column="aid"></id>
            <result property="uid" column="uid"></result>
            <result property="money" column="money"></result>
        </collection>


    </resultMap>


    <!-- 查询所有-->
    <select id="findAll" resultMap="userAccountMap">
        select * from user u LEFT OUTER JOIN account a on u.id = a.uid
    </select>

在数据库中查询是这样的:
在这里插入图片描述
可以看到46有两个重复的

而在查询的时候是这样的
在这里插入图片描述
Mybatis会自动将重复的信息封装在一起

多对多

多对多其实就是两个一对多
在这里插入图片描述

查询角色的信息同时获取用户的信息

public class Role implements Serializable {
    private Integer roleID;
    private String roleName;
    private String roleDesc;

    //多对多的关系映射 :  一个角色可以赋予多个用户
    private List<User> users;
}
    <!-- 定义role表的resultMap  -->
    <resultMap id="roleMap" type="role">
        <id property="roleID" column="rid"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
        
        <collection property="users" ofType="user">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="address" column="address"></result>
            <result property="sex" column="sex"></result>
            <result property="birthday" column="birthday"></result>
        </collection>
    </resultMap>


    <!-- 查询所有 -->
    <select id="findAll" resultMap="roleMap">
        select r.ID as rid, r.ROLE_NAME, r.ROLE_DESC, u.* from role r
 LEFT OUTER JOIN user_role ur on r.id = ur.rid
 LEFT OUTER JOIN user u on ur.uid = u.id
    </select>

在Mysql中找出来是这样的:
在这里插入图片描述
查找结果是这样的:
在这里插入图片描述
相反的,查询用户的信息同时获取角色的信息

主表实体应该包含从表实体的集合引用

public class User implements Serializable {
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    private List<Role> roles;
}

定义配置文件,更改sql语句

    <!-- 定义User的resultMap -->
    <resultMap id="userAccountMap" type="User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>

        <collection property="roles" ofType="role">
            <id property="roleID" column="rid"></id>
            <result property="roleName" column="role_name"></result>
            <result property="roleDesc" column="role_desc"></result>
        </collection>

    </resultMap>


    <!-- 查询所有-->
    <select id="findAll" resultMap="userAccountMap">
        select u.*, r.ID as rid, r.ROLE_NAME, r.ROLE_DESC from user u
 LEFT OUTER JOIN user_role ur on u.id = ur.uid
 LEFT OUTER JOIN role r on ur.rid = r.id
    </select>

Mysql查询结果:

在这里插入图片描述

Mybatis查询结果:
在这里插入图片描述

Mybatis中的加载时间

在这里插入图片描述
在这里插入图片描述
一对一的延迟加载:由于是延迟加载,user没必要封装,应该在需要用的时候再封装,所以加入select属性,表示查询用户的唯一标志。其实也就是当已经有了account时,然后需要user的时候,会自动调用userdao的findById方法,根据column(这里的column不能省略)的值来查询,也就是根据当前account的uid属性来查询user。

        <!-- 一对一的关系映射:配置封装user的内容
              select 属性指定的内容:查询用户的唯一标志
        -->
        <association property="user" column="uid" javaType="user" select="com.itheima.dao.IUserDao.findById">
        </association>
    </resultMap>

此时延迟查询还没开,需要到Mybatis的官网的XML配置的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>
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=utf8"/>
            <property name="username" value="laodu1"/>
            <property name="password" value="123456"/>
        </properties>


    <settings>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

    <typeAliases>
        <package name="com.itheima.domain"/>
    </typeAliases>



    <!-- 配置环境-->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置连接池-->
            <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>
        <package name="com.itheima.dao"/>
    </mappers>
</configuration>

注意,这里的settings标签,必须要到configuration标签下的最上层,不然会报错
结果对比-------未开启延迟查询:
在这里插入图片描述
开启延迟查询时:
在这里插入图片描述
可以看到开启时,只有在查询到user对象时,才会进行查找。

一对多的延迟查询
本质上,和一对一时差不多的,除了association标签变成了collection标签,但是里面是还是用select 和 column实现。然后打开settings就行了

        <!-- 配置User对象中accounts集合的映射 -->
        <!-- ofType : 要写全限定类名(这里是因为起了别名)-->
        <collection property="accounts" ofType="account" select="com.itheima.dao.IAccountDao.findAccountByUid" column="id">
        </collection>

两种其实本质上都是将从表的对象的封装交给从表自己实现,所以select里的方法都是从表的。比如要查找user并且显示account的内容,那每次就根据user的某个属性,用accountdao的方法根据这个属性查找到对应的所有account


Mybatis中的一级缓存和二级缓存

在这里插入图片描述
一级缓存
在这里插入图片描述

    public void testFirstLevelCache() {
        User user1 = userDao.findById(41);
        System.out.println(user1);

//        sqlSession.close();

        sqlSession.clearCache();//此方法也可以清空缓存

        //再次获取SqlSession对象
//        sqlSession = factory.openSession();
//
//        userDao = sqlSession.getMapper(IUserDao.class);

        User user2 = userDao.findById(41);
        System.out.println(user2);

        System.out.println(user1 == user2);
    }

*一级缓存SqlSession范围的缓存,当调用SqlSession的修改,添加,删除,commit(), close()等方法时,就会清空一级缓存。
*

二级缓存:
在这里插入图片描述
按照者3步骤
在Mybatis官网的XML配置的settings里找到在这里插入图片描述
在SqlMapConfig里使用settings标签配置

    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

在这里插入图片描述
在画红线的地方进行配置
注意
在这里插入图片描述
SqlSessionFactory里存放的是数据,当查询时,它会根据数据建立一个对象,所以两次查询得到的对象是不相等的,但是值是相等的。

    @Test
    public void testFirstLevelCache() {
        SqlSession sqlSession1 = factory.openSession();
        IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
        User user1 = dao1.findById(41);
        System.out.println(user1);

        sqlSession1.close();//一级缓存消失


        SqlSession sqlSession2 = factory.openSession();
        IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
        User user2 = dao2.findById(41);
        System.out.println(user2);
        sqlSession2.close();


        System.out.println(user1 == user2);
    }

这两次查询的结果:
在这里插入图片描述
可以看到的确是只进行了一次查询,但是两个对象不相等。

Mybatis的注解开发

在之前的xml配置中,我们都需要至少这两个配置文件:

在这里插入图片描述

所谓的注解开发,SqlMapConfig还是必不可少的,只是IUserDao.xml不需要了。
注解一共有4个标签:@Select @Insert @Update @Delete,对应了CRUD几个操作。
1、查询

public interface IUserDao {
    /**
     * 查询所有用户
     * @return
     * 在Mybatis中针对 CRUD一共有4个注解
     * @Select @Insert @Update @Delete
     */
    @Select("select * from user")
    List<User> findAll();
}

    <!-- 如果使用注解来配置,此处应该使用class属性指定被注解的dao全限定类名
        -->
    <mappers>
        <mapper class="com.itheima.dao.IUserDao"/>
    </mappers>

这样注解开发就完成了。

下面这张图片你很好的展示了注解开发其实得到了所需的一切。
在底层配置中都是以namespace + id为key, resutlType + sql为value的
在这里插入图片描述
剩下三个标签的基本用法

package com.itheima.dao;

import com.itheima.domain.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

public interface IUserDao {
    /**
     * 查询所有用户
     *
     * @return 在Mybatis中针对 CRUD一共有4个注解
     * @Select @Insert @Update @Delete
     */
    @Select("select * from user")
    List<User> findAll();

    /**
     * 保存用户
     * @param user
     */
    @Insert("insert into user (username,address, sex, birthday) values(#{username}, #{address}, #{sex}, #{birthday})")
    void saveUser(User user);

    /**
     * 更新用户
     * @param user
     */
    @Update("update user set username=#{username}, sex=#{sex}, address=#{address}, birthday=#{birthday} where id = #{id}")
    void updateUser(User user);

    /**
     * 删除用户
     * @param userId
     */
    @Delete("delete from user where id = #{id}")
    void deleteUser(Integer userId);

    /**
     * 根据id查询用户
     * @param userId
     * @return
     */
    @Select("select * from user where id = #{id}")
    User fingById(Integer userId);

    /**
     * 根据用户名称模糊查询
     * @param username
     * @return
     */
//    @Select("select * from user where username like #{username}")
    @Select("select * from user where username like '%${value}%'")
    List<User> findUserByName(String username);

    /**
     * 查询总用户数量
     * @return
     */
    @Select("select count(*) from user")
    int findTotalUser();
}

测试类

package com.itheima.test;

import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;
import java.util.Date;
import java.util.List;

public class AnnotationCURDTest {
    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IUserDao userDao;

    @Before
    public void Init() throws Exception{
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        factory = new SqlSessionFactoryBuilder().build(in);
        session = factory.openSession();
        userDao = session.getMapper(IUserDao.class);
    }
    @After
    public void destory() throws Exception {
        session.commit();
        session.close();
        in.close();
    }

    @Test
    public void testSave() {
        User user = new User();
        user.setUsername("mybatis annotation");
        user.setSex("男");
        user.setAddress("北京市昌平区");
        user.setBirthday(new Date());

        userDao.saveUser(user);
    }

    @Test
    public void testUpdate() {
        User user = new User();
        user.setId(52);
        user.setUsername("mybatis annotation22222222");
        user.setSex("男");
        user.setAddress("北京市昌平区");
        user.setBirthday(new Date());

        userDao.updateUser(user);
    }

    @Test
    public void testDelete() {

        userDao.deleteUser(52);
    }

    @Test
    public void testFindOne() {
        User user = userDao.fingById(41);
        System.out.println(user);
    }

    @Test
    public void testFindByName() {
        List<User> users = userDao.findUserByName("王");
        for (User user : users) {
            System.out.println(user);
        }
    }

    @Test
    public void testFindTotal() {
        int total = userDao.findTotalUser();
        System.out.println(total);
    }

}

注解的resultMap写法
在这里插入图片描述
@ResultMap(“userMap”)也是可以的。
一对一的注解配置:和xml配置一样,

    @Select("select * from account")
    @Results(id="accountMap", value={
    //id指的是这个属性是否是id
            @Result(id=true,column = "id",property = "id"),
            @Result(column = "uid", property = "uid"),
            @Result(column = "money", property = "money"),
            //column : 根据指定的column来查询,one=@One是一对一
            FetchType.EAGER是立即立即查询
            @Result(property = "user", column = "uid", one=@One(select="com.itheima.dao.IUserDao.findById", fetchType= FetchType.EAGER))
    })
    List<Account> findAll();

一对多的注解配置:

    @Select("select * from user")
    @Results(id="userMap", value={
            @Result(id=true, column="id", property = "id"),
            @Result(column="username", property = "username"),
            @Result(column="sex", property = "sex"),
            @Result(column="address", property = "address"),
            @Result(column="birthday", property = "birthday"),
            @Result(property = "accounts", column = "id",
                    many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
                    //指的是延迟查询
                            fetchType = FetchType.LAZY))
    })
    List<User> findAll();

一级缓存是默认打开的
二级缓存注解打开方式:

@CacheNamespace(blocking = true)
public interface IUserDao {}

在接口名上方注释就可以用了。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值