Java:62-Mybatis高级介绍

Mybatis高级介绍

Mybatis 高级查询:
ResutlMap 属性 :
建立对象关系映射 :
/*
resultType 
如果实体的属性名与表中字段名一致,将查询结果自动封装到实体类中 
 
ResutlMap 
如果实体的属性名与表中字段名不一致,可以使用ResutlMap 实现手动封装到实体类中 

因为不指定的话,是根据对应变量名称和get,set方法操作的,但是表的名称总会有可能会改变
那么可能对应的变量,set,get都匹配不了,所以,我们需要一种可以指向的操作
让他们按照我的指向进行封装,而不是默认的指向
*/

/*
不管什么set,还是get,可能不同版本是不同的说明,但我们只需要规范(满足驼峰)即可不会出现错误
*/

<?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.lagou.mapper.UserMapper">

    <!--根据id查询用户-->
    <select id="findUserById" parameterType="int" resultType="user">
        select * from user where id = #{id}
    </select>

    <!-- resultMap:手动配置实体属性与表中字段的映射关系,完成手动封装-
    指向对应的唯一标识的映射,读取到的一系列数据会被这个标识进行确定位置,从而完成映射
    从这里可以得出,读取映射文件,不只是读取一次,然后操作一次,也可以全部读取,将数据封装好,进行操作
    这里就是如此,封装的对应MappedStatement对象(即类),每个元素基本就是一个MappedStatement对象

    -->
    <select id="findAllResultMap" resultMap="userResultMap">
            select * from User
    </select>

    <!-- id唯一标识,可以随便写,type也受别名影响,与resultType不同的是,resultType是用来默认封装
    而type是自定义封装,会先判断默认封装,如果默认封装找不到了,则会看自定义封装
当数据库改变时,我们可以将resultType改为type,这样就可以自定义封装了
由于数据库的字段,最终都会是大小写忽略的过来判断,所以下面column是可以进行大小写的替换的,但我们通常都是小写
就如数据库也默认小写一样,所以像这样没有判断的基本大小写忽略

    -->
    <resultMap id="userResultMap" type="user">
<!--手动配置映射关系-->
        <!--id:用来配置主键-->
        <id property="id" column="id"/>
        <!--result:用来配置普通字段-->
        <result property="username" column="username"/>
        <!--property可以是变量的小写,但不能是对应的大写(相同自然可以,这里的说明只是对小写变大写来说的),否则报错,column可以大小写忽略
因为对应字段基本上是小写字段(当然,若操作了别名,那么就是按照别名的,而不是字段了),但是也要注意,这里可能存在idea的错误出现,比如你的变量是大写的,这里的property也操作大写,他显示报错,但是其实运行没有问题,因为是相同的,只是idea认为他可能有错误,即只要大写就错误,一般是首字母大写才会出现,如果存在set方法,并且property首字母没有大写,那么操作set方法,否则就是赋值,并且set存在时,且变量也存在,若变量值与set值是一样的(在驼峰规则下),那么对应就不会出现显示错误(前提首字母没有大写),也就是说,他建议你的写法与驼峰一样,并且变量也最好一样(虽然可以首字母大写,但是变量一般首字母是小写的)
-->
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"></result>
<!-- 大小写通常是忽略的,有些情况下,如判断下,基本是对应的小写,如#{},和property都是判断变量是否符合-->
    </resultMap>




</mapper>
package com.lagou.test;

import com.lagou.domain.User;
import com.lagou.mapper.UserMapper;
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.Test;

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

/**
 *
 */
public class MybatisText {

    /*
    Mybatis的dao层mapper代理方式测试
     */

  @Test
  public void test1() throws IOException {
      InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
      SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
      SqlSession sqlSession = build.openSession();
      //这里就不是使用方法来调用了,而是直接使用mapper代理对象
      //代理对象,可以看成,通过接口方法,即会传入对应的Class类,来进行解析
      //一般的我们是直接调用对应读取方法,传入自己写的参数,即进行sql的获得信息
      //这里我们通过解析对应接口方法,来获得对应的sql信息
      //方法类路径+方法名称=sql路径传参
      //parameterType=接口方法参数
      //而方法返回值,是当作这个方法的返回值的,返回的是resultType的类型,所以也要一致才行
      UserMapper mapper = sqlSession.getMapper(UserMapper.class);
      //上面说明了对应参数,那么调用这个方法,会在其中,通过反射,进行参数传递
      //使得类似于实现类的操作(实际上就是帮我们创建一个类似实现类的对象)
      //这样就形成了我们只用接口来进行实现类的操作了,
      User userById = mapper.findUserById(6);
      System.out.println(userById);
      sqlSession.close();
  }

  @Test
    public void test2() throws IOException {
      InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
      SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
      SqlSession sqlSession = build.openSession();
      UserMapper mapper = sqlSession.getMapper(UserMapper.class);
      List<User> allResultMap = mapper.findAllResultMap();
      for (User user : allResultMap) {
          System.out.println(user);
      }
      sqlSession.close();
  }
}

多条件查询(三种) :
使用 #{arg0}-#{argn} 或者 #{param1}-#{paramn} 获取参数
使用注解,引入 @Param() 注解获取参数
使用pojo 对象传递参数
<?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.lagou.mapper.UserMapper">

    <!--根据id查询用户-->
    <select id="findUserById" parameterType="int" resultType="user">
        select * from user where id = #{id}
    </select>

    <!-- resultMap:手动配置实体属性与表中字段的映射关系,完成手动封装-
    指向对应的唯一标识的映射,读取到的一系列数据会被这个标识进行确定位置,从而完成映射
    从这里可以得出,读取映射文件,不只是读取一次,然后操作一次,也可以全部读取,将数据封装好,进行操作
    这里就是如此,封装的对应MappedStatement对象(即类),每个元素基本就是一个MappedStatement对象

    -->
    <select id="findAllResultMap" resultMap="userResultMap">
            select * from User
    </select>

    <!-- id唯一标识,可以随便写,type也受别名影响,与resultType不同的是,resultType是用来默认封装
    而type是自定义封装,会先判断默认封装,如果默认封装找不到了,则会看自定义封装
当数据库改变时,我们可以将resultType改为type,这样就可以自定义封装了
由于数据库的字段,最终都会是大小写忽略的过来判断,所以下面column是可以进行大小写的替换的,但我们通常都是小写
就如数据库也默认小写一样,所以像这样没有判断的基本大小写忽略

    -->
    <resultMap id="userResultMap" type="user">
<!--手动配置映射关系-->
        <!--id:用来配置主键-->
        <id property="id" column="id"/>
        <!--result:用来配置普通字段-->
        <result property="username" column="username"/>
        <!--property可以是变量的小写,但不能是对应的大写,column可以大小写忽略
因为对应字段基本上是小写字段-->
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"></result>
<!-- 大小写通常是忽略的,有些情况下,如判断下,基本是对应的小写,如#{},和property都是判断变量是否符号-->
    </resultMap>

    <!--多条件查询 1-->
    <select id="findByIdAndUsername1" resultMap="userResultMap">
      <!--select * from user where id = #{arg0} and username = #{arg1}-->

        select * from user where id = #{param1} and username = #{param2}
<!--一般情况下,若需要两个以及两个以上的参数(不是类),parameterType只能操作其中一个,基本上是左边(当然,这些是需要看规则的,具体情况是,如果使用其他等等框架或者其他的一些某些操作,如MPJ,那么可能会拿取后面的,具体可以百度),且多余的一般默认为null(除非是相同的开始参数,如都是param1,那么就是对应的值)
因为没有替换(需要在数据库里加上null,而不是null字符串,使用程序得到的),这时若在
数据库表里没有找到对应值
那么一般不会报错,与?不同,没有替换,那么就会当作就是这个#{username},所以报错的,而?,则必须替换,否则报错
即找到了,那么一般就会报错,因为例外一个没有进行替换


所以为了实现这个操作,那么就基本不能使用parameterType
我们可以使用使用 #{arg0}-#{argn} 或者 #{param1}-#{paramn} 获取参数
arg0代表传递参数的第一位参数,param1也是
一般传统的需要一些特殊操作,才可实现多参数的传递,而这里的代理,可以直接进行多参数的传递(实际上也是特殊的操
作)
特殊操作,可以理解为使用map集合和类
使得对应替换为集合替换,对于类,当然使用的是替换类
而单参数,当然替换的是对应参数(名字可以随便起,因为最后只会替换单参数,写的名称会直接覆盖,不会像类和map那样
识别)
但必须有,否则报错,因为没有替换的值(实际上会识别#{}是否有对应名称,没有就会报错,对所有方式都会进行,所以随
便写的就是为了防止这个错误)
可以理解为他们赋值给Object
而多参数,可以理解为一个map集合(基本所有元素都可以使用),所以上面的特殊操作,也可以看成是map集合的操作
我们替换的就是对应集合的数,即key的值,且没有大小写忽略
因为map集合的key基本不能是不一样的获得的
最后:parameterType,一般是可以不写的,即会自动识别对应类型,写了是为了好知道传入的参数是什么,基本只是一个识别,所以这里还需要注意一点,就是他写错了,可能在不匹配的情况下,会操作自动,所以这个参数基本上主要作用只是告诉你他应该传递什么,而并非一定是他,写错了是会对开发人员造成一定的混淆,所以有时候在不规范的情况下,建议不要写,而规范的前提下,写上有利于开发
-->
    </select>


    <!--多条件查询 2-->
    <select id="findByIdAndUsername2" resultMap="userResultMap">

    select * from user where id = #{id} and username = #{username}
            <!--
假设,有两个@Param("id"),那么一般后面的覆盖前面的,假如两个
只有一个有@Param("id"),那么有id时操作该值
否则就相当于没有操作,即他是一个别名-->
    </select>

    <!--多条件查询 3
    注意:id不能相同,否则报错
    -->
    <select id="findByIdAndUsername3" resultMap="userResultMap" parameterType="user">

        select * from user where id = #{id} and username = #{Username}
    </select>


</mapper>
package com.lagou.mapper;

import com.lagou.domain.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 *
 */
public interface UserMapper {

    /*
    根据id查询用户
     */
    public User findUserById(int id);

    /*
    查询所以用户
     */
    public List<User> findAllResultMap();

    /*
    多条件查询方式一

     */
    public List<User> findByIdAndUsername1(int id,String username);

    /*
        多条件查询方式二
这个@Param注解,将操作的参数进行名称设置(对应的#{}或者${}名称必须一致,即替换名称必须一致,否则报错)
无论是否是单个值(单个值名称随便改变的那个,也需要一致)
而不是用默认的arg和param进行map键的添加
虽然也可一起使用,在不是别名时,即@Param注解对应的别名时,则操作对应的默认arg和param
单个不可,因为@Param设置后,不可操作单个了
所以当传递这个参数时,会根据是否有该注解,进行判断,然后进行key添加,即这个标识是可以随便起的
之所以可以这样,是因为反射是可以反射出注解的
         */
   public List<User> findByIdAndUsername2(@Param("id")int id, @Param("username")String username);

    //还要注意的是,该注解会起作用是因为对应的mybatis进行了操作(接口与xml的对应),若写在其他不相关的包里面的类,他就相当于没有写,即不会起任何作用,比如我们寻常的service里面的接口,所以他可以写,可以不写,一般在service中不写


    /*
    多条件查询方式三
     */
   public List<User> findByIdAndUsername3(User user);





}

package com.lagou.test;

import com.lagou.domain.User;
import com.lagou.mapper.UserMapper;
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.Test;

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

/**
 *
 */
public class MybatisText {

    /*
    Mybatis的dao层mapper代理方式测试
     */

  @Test
  public void test1() throws IOException {
      InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
      SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
      SqlSession sqlSession = build.openSession();
      //这里就不是使用方法来调用了,而是直接使用mapper代理对象
      //代理对象,可以看成,通过接口方法,即会传入对应的Class类,来进行解析
      //一般的我们是直接调用对应读取方法,传入自己写的参数,即进行sql的获得信息
      //这里我们通过解析对应接口方法,来获得对应的sql信息
      //方法类路径+方法名称=sql路径传参
      //parameterType=接口方法参数
      //而方法返回值,是当作这个方法的返回值的,返回的是resultType的类型,所以也要一致才行
      UserMapper mapper = sqlSession.getMapper(UserMapper.class);
      //上面说明了对应参数,那么调用这个方法,会在其中,通过反射,进行参数传递
      //使得类似于实现类的操作(实际上就是帮我们创建一个类似实现类的对象)
      //这样就形成了我们只用接口来进行实现类的操作了,
      User userById = mapper.findUserById(6);
      System.out.println(userById);
      sqlSession.close();
  }

  @Test
    public void test2() throws IOException {
      InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
      SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
      SqlSession sqlSession = build.openSession();
      UserMapper mapper = sqlSession.getMapper(UserMapper.class);
      List<User> allResultMap = mapper.findAllResultMap();
      for (User user : allResultMap) {
          System.out.println(user);
      }
      sqlSession.close();
  }

  /*
  多条件查询:方式一
   */
    @Test
    public void test3() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allResultMap = mapper.findByIdAndUsername1(6, "自动提交事务");
        for (User user : allResultMap) {
            System.out.println(user);
        }
        sqlSession.close();
    }


    /*
    多条件查询:方式二
     */
    @Test
    public void test4() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allResultMap = mapper.findByIdAndUsername2(6, "自动提交事务");
        for (User user : allResultMap) {
            System.out.println(user);
        }
        sqlSession.close();
    }

    /*
   多条件查询:方式三
    */
    @Test
    public void test5() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user1 = new User();
        user1.setId(6);
        user1.setUsername("自动提交事务");
        List<User> allResultMap = mapper.findByIdAndUsername3(user1);
        for (User user : allResultMap) {
            System.out.println(user);
        }
        sqlSession.close();
    }
}

模糊查询 :
方式一:
 /*
 模糊查询:方式一
  */
    @Test
    public void test6() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> mapper1 = mapper.findByUsername("%自动%");
        //替换,实际上会帮我们自动添加单引号,之所以不自动添加双引号,是因为整体由双引号包括的
        //这也是sql注入的来源之一,而占位的会将sql注入的操作进行转义
        //当然,替换一般来说很严谨的,通常看类型进行是否添加引号(字符单,字符串双,sql中单和双基本是一样的操作,当然可能有所区别,所以一般情况下,底层通常都会用单,无论是字符串还是字符都是单)
        for (User user : mapper1) {
            System.out.println(user);
        }
        sqlSession.close();
    }
  <!--
    模糊查询:方式一 string不能写成String,没有封装String这个别名
    -->
    <select id="findByUsername" resultMap="userResultMap" parameterType="string">
        select * from user where username like #{username}
    </select>

/*
模糊查询:方式一
 */
    public List<User> findByUsername(String username);

方式二 :
 <!--
   模糊查询:方式二
   -->
    <select id="findByUsername2" resultMap="userResultMap" parameterType="string">
     <!--parameterType是基本数据类型或者String时,${}里面的值只能写value
         注意:#{}是占位符,即当找到这个时,会自动的添加单引号
替换后,sql语句最终在平台执行,且只能是单独的,不能被引号包括,否则报错
 ${}是sql原样拼接(无论是否单独都替换,不会出现替换报错,与#{}不一样,被引号包括时,会报错,即${}范围大)
即当找到这个时,不会加上单引号,由于不会加上单引号
所以拼接时基本不会进行引号转义,即替换时没有引号转义,所以会出现sql注入
         -->
        select * from user where username like '${value}'
<!--通过实验,value其实也可以进行随便写,可能特殊时候,只能写value吧
但要注意的是:它里面不能是对应的数字,如111,否则是不会进行替换的,即默认为null
所以一般返回没有数据(因为like不能直接的查出null)
-->
    </select>
    /*
模糊查询:方式二
*/
    @Test
    public void test7() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> mapper1 = mapper.findByUsername2("%自动%");
        for (User user : mapper1) {
            System.out.println(user);
        }
        sqlSession.close();
    }
 /*
模糊查询:方式二
 */
    public List<User> findByUsername2(String username);
#{}:表示一个占位符号
通过 #{} 可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,# {}可以有效防止sql 注入
#{} 可以接收简单类型值或 pojo 属性值
如果 parameterType 传输单个简单类型值, #{} 括号中可以是value 或其它名称
${}:表示拼接sql 串
通过 ${} 可以将 parameterType 传入的内容拼接在 sql 中且不进行jdbc 类型转换,会出现 sql 注入问题
${} 可以接收简单类型值或 pojo 属性值
如果 parameterType 传输单个简单类型值, ${} 括号中基本只能是value(实际上也是可以随便写的,但数字好像不可以替换)
当然了,进行替换的,基本不能空的,否则会检查报错
Mybatis 映射文件深入:
我们很多时候有这种需求,向数据库插入一条记录后
希望能立即拿到这条记录在数据库中的主键值(比如自增时,没有传递对应主键,即不知道对应主键是对少,但要获得对应主键)
实际上由于只是获得对应语句,所以也是有可能出现查询,实现删除语句的
useGeneratedKeys :
  <!--添加用户:获得返回主键:方式一
    useGeneratedKeys="true" 声明返回主键,帮你找到自增当时的主键,并赋值给对应id,只作用与自增和insert元素
    其他元素可以发现,没有这个属性,且注意:若表不是自增的,使用这个属性,会检查报错
    若是自增的,他会利用自增量来计算最终主键的,因为数据库表的自增是可以根据自增当前量(起始值)来计算获得
 keyProperty="id" 把返回主键的值,封装到实体的id 属性中,可以是对应变量的小写

注意:只适用于主键自增的数据库,mysql 和 sqlserver 支持,oracle 不支持

    -->
<insert id="saveUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">

    insert into user(username,birthday,sex,address) values (#{username},#{birthday},#{sex},#
    {address})
</insert>

  /*
添加用户:返回主键方式一
*/
    @Test
    public void test8() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user = new User();
        user.setUsername("自动提交事务");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddress("北京");
        System.out.println(user);
        mapper.saveUser(user);
        //通过自增起始值计算主键,从而获得对应主键
        System.out.println(user);

        sqlSession.commit();
        sqlSession.close();


    }
/*
添加用户:获得返回主键方式一
 */
    public void saveUser(User user);

    /*
注意:只适用于主键自增的数据库,mysql 和sqlserver 支持,oracle 不行
selectKey :
在这之前,我要介绍一个sql函数
select last_insert_id();
-- last_insert_id()函数可以获得最后一次添加的内容的主键,但是自己添加的不算,也就是直接添加的不算
-- 只能是因为自动添加的最后一个,所以也通常操作自增
-- 自增也使得该字段必须是主键,所以整体来说是获得最后一次添加内容的主键
 <!--添加用户:获得返回主键:方式二

   -->
    <insert id="saveUser2" parameterType="user" >
<!--
selectKey 适用范围广,支持所有类型数据库,实际上就是再次执行语句,多用于获得主键
当然也可以执行其他语句,如计算平均值等等,将对应值进行赋值
其中KeyColumn是可以省略的,实际上只是看执行语句的结果,因为会自动识别,写上他,可以知道这里是干什么的
且基本只能操作单个数,即类和集合等等,基本操作不了
 keyColumn="id" 指定主键列名
 keyProperty="id" 指定主键封装到实体的 id 属性中
 resultType="int" 指定赋值元素类型,必须正确,否则报错,因为只有确定类型,才可进行赋值,否则不知道是数字还是
字符串
 而使得整型出现字母了,
 order="AFTER" 设置在 sql 语句执行后,执行此语句
 order="BEFORE" 设置在 sql 语句执行前,执行此语句
-->

<selectKey order="AFTER" keyColumn="id" keyProperty="id" resultType="int">
<!--这个方式,会覆盖掉useGeneratedKeys方式,即不会执行useGeneratedKeys方式了,因为判断的条件,只能是一个
即是selectKey,找到这个,不执行useGeneratedKeys方式,否则执行useGeneratedKeys方式
-->
    select last_insert_id();
</selectKey>
<!--selectKey只能在insert元素使用,可以发现,其他元素里没有这个标签
我们也知道,要获得最后一次插入的主键,自然的这个方法需要后调用,所以设置order="AFTER"表示后调用
即下面sql语句调用完后,执行这个语句
这时oracle也就可以使用了,一般要将AFTER改成BEFORE
-->
        insert into user(username,birthday,sex,address) values (#{username},#{birthday},#{sex},#
        {address})
    </insert>
  /*
添加用户:返回主键方式二
*/
    @Test
    public void test9() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user = new User();
        user.setUsername("热热热");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddress("北京");
        System.out.println(user);
        mapper.saveUser2(user);
		//也是赋值给对应变量
        System.out.println(user);

        sqlSession.commit();
        sqlSession.close();


    }
/*
添加用户:获得返回主键方式二
 */
    public void saveUser2(User user);
    //useGeneratedKeys可以看成是这个的简单操作,或者说是mysql内部有这样的操作


动态 SQL :
当我们要根据不同的条件,来执行不同的 sql 语句的时候,需要用到动态sql

在这里插入图片描述

在这之前,我们知道,getParameter()方法,在没有指定参数,返回null
有指定参数,没写值的为"“(空值),一般不会出现没有参数,那么通常都是”"(空值)
那么先思考,如何通过好的办法,进行上面三种组合的sql语句
一般的,我们可能会这样想
String sql = "select * from user where " --where后面加个空格,防止连接上
if(name != ""){
sql += "name=" + name
}
if(price != ""){
sql += "and price=" + price
}
if(color != ""){
sql += "and color=" + color
}
-- 像上面的操作,但是上面有一个缺点,当name为""时,其他拼接就会有问题因为where后面不能接and
-- 我们可以这样写,在where后面加上'1'='1'或者true
-- 使得可以加上and不影响,这样操作true和false的结果,任然在后面sql上
-- 那么就可以使得全部都可以由and了
-- 即
String sql = "select * from user where true" --true后面加个空格,防止连接上
if(name != ""){
sql += "and name=" + name
}
if(price != ""){
sql += "and price=" + price
}
if(color != ""){
sql += "and color=" + color
}
-- 这样就可以实现拼接了
但是我们现在在Mybatis里,如何在配置文件里进行这样的操作呢
动态 SQL 之< if >:
根据id 和username 查询,但是不确定两个都有值
  /*
       动态sql的if标签:多条件查询
     */
    public List<User> findByIdAndUsernameIf(User user);
<!--动态sql的if标签:多条件查询
    -->
    <select id="findByIdAndUsernameIf" resultType="user" parameterType="user">
        select * from user
        <!--test里面就是表达式
        where标签:相当于where 1=1,即where true,但是如果没有条件的话,不会拼接上where关键字
        实际上就是当出现一个条件成立,那么就会添加上相当于where true的语句
        否则不会添加,那么就是select * from user了,这样就没有多余的代码了
        -->
        <where> <!--当只有一个参数,且没有指定时(如注解@Param的指定),那么这个id可以写成iddddd,都可以-->
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="username != null">
                and username = #{username}
            </if>
        </where>
        <!--读取的配置文件会将这些if的对应属性值和内容进行替换拼接
        这里说明一下:对应替换,是使用get方法,若没有get方法则直接赋值
        且无论是get后面名称还是直接赋值,首字母大小写忽略
        其中表达式里的对应同样变量也会替换,如id,username
        替换之前,会将对应信息变成字符串
所以无论对应类型是什么,都是字符串之间的值比较
        这里我们使用null来判断的,实际上前端传递的大多数都是""
        但是这里可以看到引号之间不好变化(单引号和双引号只能分开,使得单引号和双引号有一个不能判断)
        所以一般的我们都是null,实际上后台传递的基本是null(""会进行处理,一般自己处理,与null一起通过test来处理)
因为字符串默认null(int是Integer的)
        若直接的int,可能后台会进行处理
        但基本上都是类,如果出现了,设置一下即可('')
一般来说if里面通常不能操作表别名的对应,比如bpc是表,name是字段,除非这个bpc是参数名称,且里面有name,也就是parameterType的名称
如果对应的parameterType没有写,那么我们写上的就是对应的参数值(也就是对应方法的参数值,如果有@Param注解,那么就是其里面的值),如果是类,那么通常操作其对应的get方法,当然,如果可以的话,可以使用.的操作来直接获得,而不操作get方法
        -->
    </select>
  /*
动态sql的if标签:多条件查询
*/
    @Test
    public void test10() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user = new User();
        user.setId(49);
        user.setUsername("热热热");


        List<User> byIdAndUsernameIf = mapper.findByIdAndUsernameIf(user);
        for (User user1 : byIdAndUsernameIf) {
            System.out.println(user1);
        }

        sqlSession.close();


    }
当然也有一个这个方式
<!-- 
 choose 标签相当于swtich 语句 
 when 标签相当于case 语句 ,即只会添加一个,因为只会执行一个when,即那个符合先执行,从上到下
即有一个成立,那么choose结束,且是从上到下的执行,若都不成立,则执行otherwise,最后结束choose
 otherwise 标签相当于default 语句 
--> 
<select id="findByIdAndUsernameChoose" parameterType="user" resultType="user"> 
 
 SELECT * FROM `user` 
 
 <where> 
 <choose> 
 <when test="id != null"> 
     AND id = #{id} 
 
 </when> 
 <when test="username != null"> 
 
 AND username = #{username} 
 
 </when> 
 <otherwise> 
 
 AND 1=1 
 
 </otherwise> 
 </choose> 
 </where> 
</select>
这个方式不怎么用,因为基本只能添加一个sql语句,所以了解就可
动态 SQL 之< set >:
动态更新user 表数据,如果该属性有值就更新,没有值不做处理
就如在回显时,删除对应值,使得对应值为空,这时进行修改了,所以我们可以防止这种情况
    /*
       动态sql的foreach标签:多值查询:数组
   */
    public List<User> findByArray(Integer[] ids);
 /*
       动态sql之set: 动态更新
   */
    @Test
    public void test11() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 当前返回的 其实是基于UserMapper所产生的代理对象:底层:JDK动态代理 实际类型:proxy
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user = new User();
        user.setId(3);
        user.setUsername("子慕最帅");
        user.setAddress("北京海淀区拉勾网");

        mapper.updateIf(user);

        sqlSession.commit();

        sqlSession.close();

    }
<!--动态sql之set : 动态更新-->
    <update id="updateIf" parameterType="user">
        update user
        <!--<set> : 在更新的时候,会自动添加set关键字,还会去掉最后一个条件的逗号
        set标签其他元素基本上都可以使用,实际上无论是什么标签,都是读取存放对应数据,使用字符串代码执行,从而
获得对应sql语句
        最后去掉逗号
虽然可以直接使用if,但是主要的,是逗号的去除,所以我们也是使用set的
        -->
        <set>
            <if test="username != null">
                username = #{username},
            </if>
            <if test="birthday != null">
                birthday = #{birthday},
            </if>
            <if test="sex != null">
                sex = #{sex},
            </if>
            <if test="address != null">
                address = #{address},
            </if>

        </set>
            <!--可以看出,读取的数据其实也是按照顺序来的
            即会通过结束标签进行全部读取位置,读取完,拼接后,才会进行后续读取连接
            -->
        where id = #{id}
    </update>
动态 SQL 之< foreach >
foreach 主要是用来做数据的循环遍历
例如: select * from user where id in (1,2,3) 在这样的语句中,传入的参数部分基本是依靠foreach 遍历才能实现
因为上面说的,基本都是判断,不是循环
集合方式:
/*
       动态sql的foreach标签:多值查询:集合
   */
        public List<User> findByList(List<Integer> ids);
    
  /*
     动态sql之foreach: 多值查询
 */
    @Test
    public void test12() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 当前返回的 其实是基于UserMapper所产生的代理对象:底层:JDK动态代理 实际类型:proxy
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<Integer> ids = new ArrayList<>();
        ids.add(11);
        ids.add(6);
        ids.add(2);

        List<User> users = mapper.findByList(ids);
        //虽然多参数基本是map集合操作,但这只是对于替换来说,因为需要key
        //而不用替换的,基本就可以使用list集合来进行直接数据封装
        //因为最终的,就是将数据进行拼接而已,具体什么方式,由Mybatis决定
        for (User user : users) {
            System.out.println(user);
        }


        sqlSession.close();

    }


    
    <!--动态sql的foreach标签:多值查询:根据多个id值查询用户-->
    <select id="findByList" parameterType="list" resultType="user">
<!--list在Mybatis中也有别名-->
        select * from user
        <where>
            <!--
                collection : 代表要遍历的集合元素,通常写collection或者list 其他也可以,具体看百度,如array则是使用数组
				而之所以可以写collection,是因为多态
				但是若有多个参数,那么需要指定了,即他这里相当于获取参数的方式而已(相当于#{},或者${}),否则会报错的
				之所以存在集合,是因为in里面是可以即是数字,也是汉字的
                open : 代表语句的开始部分
                close : 代表语句的结束部分
            item : 代表遍历结合中的每个元素,生成的变量名,使用的#{}或者${}替换必须与这个名称一样,否则报错
                separator: 分隔符
             -->
          <foreach collection="collection" open="id in (" close=")" item="id" separator=",">
              #{id}
          </foreach>
        </where>

    </select>
数组方式:
/*
       动态sql的foreach标签:多值查询:数组
   */
    public List<User> findByArray(Integer[] ids);

 /*
  动态sql之foreach: 多值查询 :数组
*/
    @Test
    public void test13() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 当前返回的 其实是基于UserMapper所产生的代理对象:底层:JDK动态代理 实际类型:proxy
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        Integer[] ids = {2,6,11};

        List<User> byArray = mapper.findByArray(ids);
        for (User user : byArray) {
            System.out.println(user);
        }


        sqlSession.close();

    }
      <!--动态sql的foreach标签:多值查询:根据多个id值查询用户-->
    <select id="findByArray" parameterType="int" resultType="user">
<!--由这里看出,parameterType与替换联系,虽然不写没事-->
select * from user
        <where>
            <!--
                collection : 代表要遍历的集合元素,通常写collection或者list 其他也可以,具体看百度
		如array使用数组
		但是若有多个参数,那么需要指定了,即他这里相当于获取参数的方式而已(相当于#{},或者${}),否则会报错的
                open : 代表语句的开始部分
                close : 代表语句的结束部分
             item : 代表遍历结合中的每个元素,生成的变量名,使用的#{}或者${}替换必须与这个名称一样,否则报错
                separator: 分隔符
             -->
            <foreach collection="array" open="id in (" close=")" item="id" separator=",">
                #{id} 
            </foreach>
        </where>

    </select>
SQL 片段 :
映射文件中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
-- 如
select * from user
-- 可以看到很多sql里基本都是这个,若我们将user表改成users表,那么基本需要改变很多地方
-- 所以我们需要一个提取,只要改变一处,他们就都可以改变
<!--这个标签可以封装好一个语句,由于所以元素基本会封装到一个MappedStatement对象
所以可以知道,他们都是一步一步读取,经过一系列操作,将结果语句放到对应对象
当读取到sql元素时,那么就会将对应对象中的对应标签值进行替换,从而形成了改变
-->
<sql id="selectUser">
        select * from user
    </sql>
<!--引入方式,即改变方式,将对应的 select * from user改成下面的操作,将sql元素写在外面,使得读取到
即他们的位置是sql元素与其他元素平级,include与sql语句元素平级,否则的话,就会报错
-->
<include refid="selectUser"></include>
知识小结 :
/*
<select>:查询 
 
<insert>:插入 
 
<update>:修改 
 
<delete>:删除 
 
<selectKey>:返回主键 
 
<where>:where 条件 
 
<if>:if 判断 
 
<foreach>:for 循环 
 
<set>:set 设置 
 
<sql>:sql 片段抽取 

*/
Mybatis 核心配置文件深入:
plugins 标签 :
MyBatis 可以使用第三方的插件来对功能进行扩展,分页助手PageHelper 是将分页的复杂操作进行封装
使用简单的方式即可获得分页的相关数据
开发步骤:
导入通用PageHelper 的坐标 :
<!-- 分页助手 --> 
<dependency> 
 <groupId>com.github.pagehelper</groupId> 
 <artifactId>pagehelper</artifactId> 
 <version>3.7.5</version> 
</dependency> 
<dependency> 
 <groupId>com.github.jsqlparser</groupId> 
 <artifactId>jsqlparser</artifactId> 
 <version>0.9.1</version> 
</dependency>
在 mybatis 核心配置文件中配置PageHelper 插件:
  <!-- 顺序问题,需要放在environments元素前面-->
    <plugins>
        <!--通过反射,创建对应类,以及对应数据库类型的方式-->
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql"/>
            <!--dialect指定方言,因为不同数据库的对应分页操作不同,如mysql是limit-->
        </plugin>
    </plugins>
   /*
        核心配置文件深入:plugin标签:pageHelper
     */
    @Test
    public void test15() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 当前返回的 其实是基于UserMapper所产生的代理对象:底层:JDK动态代理 实际类型:proxy
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        // 设置分页参数
        // 参数1: 当前页
        // 参数2: 每页显示的条数
        PageHelper.startPage(1,2); //从第一页开始,也就是limit 0,2,也可以写成limit 2(默认0开始)
         //也要注意是页的,也就是说如果是startPage(3,2),那么就是limit 4,2,而不是limit 3,2
                 //这里是页,而不是起始位置
                         //最终查询两个数量
       //进行参数的传递,实际上操作了其他对象,即分页相关

        List<User> users = mapper.findAllResultMap();
        //通过反射,找到插件对应的类,执行对应类的方法,使得操作了其他对象(Page)
        //最后调用对应语句时,会操作那个其他对象
        //然后添加对应的limit的sql
        //如果不指定对应类型,那么就会报错
        for (User user : users) {
            System.out.println(user);
        }

        // 获取分页相关的其他参数
        PageInfo<User> pageInfo = new PageInfo<User>(users);
        //实际上也执行了对应的语句,只不过返回的是分页的(这时加上了对应limit的sql)
        //就是说在原来语句上进行操作返回,所以说,插件的那个配置地方,基本在原来sql后进行操作的
        System.out.println("总条数"+ pageInfo.getTotal());
        System.out.println("总页数"+ pageInfo.getPages());
        System.out.println("是否是第一页"+ pageInfo.isIsFirstPage());
/*
//其他分页的数据 
System.out.println("当前页:"+pageInfo.getPageNum()); 
System.out.println("每页显示长度:"+pageInfo.getPageSize()); 
System.out.println("是否最后一页:"+pageInfo.isIsLastPage()); 

*/

        sqlSession.close();

    }
知识小结 :
MyBatis 核心配置文件常用标签:
properties 标签:该标签可以加载外部的 properties 文件
typeAliases 标签:设置类型别名
environments 标签:数据源环境配置标签
plugins 标签:配置MyBatis 的插件
Mybatis 多表查询 :
数据库表关系介绍:
关系型数据库表关系分为:
/*
一对一 
 
一对多 
 
多对多 
*/
举例:
/*
人和身份证号就是一对一 
一个人只能有一个身份证号 
一个身份证号只能属于一个人 
 
用户和订单就是一对多,订单和用户就是多对一 
一个用户可以下多个订单 
多个订单属于同一个用户 
 
学生和课程就是多对多 
一个学生可以选修多门课程 
一个课程可以被多个学生选修 
 
特例 
从数据层面看,一个订单实际上也只是对应一个用户,对于表来说就是多对一
所以在Mybatis中,一个订单只从属于一个用户,将多对一看成了一对一 了
但无论是如何多表,我们的约束只是一个规范而已,所以约束也是可以不写的,但主键还是最好要
*/
数据库表:
注意:表有约束,直接复制,可能会创建不了,最好一个一个的复制执行
DROP TABLE IF EXISTS `orders`; 
CREATE TABLE `orders` ( 
 `id` int(11) NOT NULL AUTO_INCREMENT, 
 `ordertime` varchar(255) DEFAULT NULL, 
 `total` DOUBLE DEFAULT NULL, 
 `uid` INT(11) DEFAULT NULL, 
 PRIMARY KEY (`id`), 
 KEY `uid` (`uid`), 
 CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`) 
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; 
 
-- ---------------------------- 
-- Records of orders 
-- ---------------------------- 
INSERT INTO `orders` VALUES ('1', '2020-12-12', '3000', '1'); 
INSERT INTO `orders` VALUES ('2', '2020-12-12', '4000', '1'); 
INSERT INTO `orders` VALUES ('3', '2020-12-12', '5000', '2'); 
 
-- ---------------------------- 
-- Table structure for sys_role 
-- ---------------------------- DROP TABLE IF EXISTS `sys_role`; 
CREATE TABLE `sys_role` ( 
 `id` INT(11) NOT NULL AUTO_INCREMENT, 
 `rolename` VARCHAR(255) DEFAULT NULL, 
 `roleDesc` VARCHAR(255) DEFAULT NULL, 
 PRIMARY KEY (`id`) 
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; 
 
-- ---------------------------- 
-- Records of sys_role 
-- ---------------------------- 
INSERT INTO `sys_role` VALUES ('1', 'CTO', 'CTO'); 
INSERT INTO `sys_role` VALUES ('2', 'CEO', 'CEO'); 
 
-- ---------------------------- 
-- Table structure for sys_user_role 
-- ---------------------------- 
DROP TABLE IF EXISTS `sys_user_role`; 
CREATE TABLE `sys_user_role` ( 
 `userid` INT(11) NOT NULL, 
 `roleid` INT(11) NOT NULL, 
 PRIMARY KEY (`userid`,`roleid`), 
 KEY `roleid` (`roleid`), 
 CONSTRAINT `sys_user_role_ibfk_1` FOREIGN KEY (`userid`) REFERENCES `user` (`id`), 
 CONSTRAINT `sys_user_role_ibfk_2` FOREIGN KEY (`roleid`) REFERENCES `sys_role` (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
 
-- ---------------------------- 
-- Records of sys_user_role 
-- ---------------------------- 
INSERT INTO `sys_user_role` VALUES ('1', '1'); 
INSERT INTO `sys_user_role` VALUES ('2', '1'); 
INSERT INTO `sys_user_role` VALUES ('1', '2'); 
INSERT INTO `sys_user_role` VALUES ('2', '2'); 
package com.lagou.domain;

public class Orders {

    private Integer id;
    private String ordertime;
    private Double total;
    private Integer uid;

    // 表示当前订单属于那个用户 association
    //不要认为是多对一,就不用创建对于变量
    //因为无论怎么创建,都是为了更好的方便操作而已,所以可以将这个订单当成是对应用户的
    //即一般情况下,有自己对应的一和对应的多,都可以创建对应的信息,一的一般是类,多的一般是集合
    private User user;

    @Override
    public String toString() {
        return "Orders{" +
                "id=" + id +
                ", ordertime='" + ordertime + '\'' +
                ", total=" + total +
                ", uid=" + uid +
                ", user=" + user +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getOrdertime() {
        return ordertime;
    }

    public void setOrdertime(String ordertime) {
        this.ordertime = ordertime;
    }

    public Double getTotal() {
        return total;
    }

    public void setTotal(Double total) {
        this.total = total;
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }
}

package com.lagou.domain;

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

public class User  implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 表示多方关系:集合 : 代表了当前用户所具有的订单列表 collection
    private List<Orders> ordersList;

    // 表示多方关系:集合 : 代表了当前用户所具有的角色列表 collection
    private List<Role> roleList;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", ordersList=" + ordersList +
                ", roleList=" + roleList +
                '}';
    }

    public List<Role> getRoleList() {
        return roleList;
    }

    public void setRoleList(List<Role> roleList) {
        this.roleList = roleList;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public List<Orders> getOrdersList() {
        return ordersList;
    }

    public void setOrdersList(List<Orders> ordersList) {
        this.ordersList = ordersList;
    }
}

package com.lagou.domain;

public class Role {

    private Integer id;
    private String rolename;
    private String roleDesc;

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", rolename='" + rolename + '\'' +
                ", roleDesc='" + roleDesc + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRolename() {
        return rolename;
    }

    public void setRolename(String rolename) {
        this.rolename = rolename;
    }

    public String getRoleDesc() {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }
}

一对一(多对一)multitable :
一对一查询模型
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户

在这里插入图片描述

一对一查询语句:
SELECT * FROM orders o LEFT JOIN USER u ON o.`uid`=u.`id`;

在这里插入图片描述

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis_db?characterEncoding=utf8
#如果是本机,localhost:3306可以省略,但后面的/并没有省略,所以上面需要是/mybatis_db
jdbc.username=root
jdbc.password=123456
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <!--加载properties文件-->
    <properties resource="jdbc.properties"></properties>

    <!--设置别名-->
    <typeAliases>

        <package name="com.lagou.domain"/>

    </typeAliases>

    <!-- 顺序问题,需要放在environments元素前面-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql"/>
            <!--dialect指定方言,因为不同数据库的对应分页操作不同,如mysql是limit-->
        </plugin>
    </plugins>


    <environments default="development">


        <environment id="development">

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

    <!--引入映射配置文件-->
    <mappers>

        <package name="com.lagou.mapper"></package>
    </mappers>
</configuration>
<?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.lagou.mapper.OrderMapper">

    <!--一对一关联查询:查询所有订单,与此同时还要查询出每个订单所属的用户信息-->

    <resultMap id="orderMap" type="com.lagou.domain.Orders">
        <id property="id" column="id"/>
        <result property="ordertime" column="ordertime"/>
        <result property="total" column="total"/>
        <result property="uid" column="uid"/>


        <!--
        可以多次对应,包括collection,他们之间也可嵌套(包括自己),但是idea可能并没有解决嵌套的显示问题(如<collection property的值(他对应的result的不会),在嵌套的情况下会认为显示错误,可能解决了,需要看版本吧),所以与之前property的首字母大写一样,会出现显示错误,虽然并不影响运行
            association : 在进行一对一关联查询配置时,使用association标签进行关联
            通常操作的是对应实体类
                property="user" :要封装实体的属性名
                javaType="com.lagou.domain.User" 要封装的实体的属性类型
   				这个属性可以不写,因为没有的话,默认property的user类型,但写了,就会按照写的
其他的属性基本也是如此操作
而resultType的必须写,因为没有默认
				只识别resultType,没有的话,就会报错
        -->
        <association property="user" javaType="com.lagou.domain.User">
            <id property="id" column="uid"></id>
            <!--uid与用户表的id是一样的,如果写id的话,不知道是哪个表的id,一般取最左边的-->
            <result property="username" column="username"></result>
            <result property="birthday" column="birthday"></result>
            <result property="sex" column="sex"></result>
            <result property="address" column="address"></result>
<!--一般的,一个类或者集合类型的,在这个设置值若设置不了,通常会报错,所以这里需要这样-->
        </association>
    </resultMap>

    <!--想要完成映射封装,默认的封装基本封装不了Orders的user变量里面去,所以需要自定义封装-->
    <select id="findAllWithUser" resultMap="orderMap">
        SELECT * FROM orders o LEFT JOIN USER u ON o.uid = u.id
    </select>
    
    </mapper>
package com.lagou.mapper;

import com.lagou.domain.Orders;

import java.util.List;

public interface OrderMapper {

    /*
        一对一关联查询:查询所有订单,与此同时还要查询出每个订单所属的用户信息
     */

    public List<Orders> findAllWithUser();
    
    
}
package com.lagou.test;

import com.lagou.domain.Orders;
import com.lagou.domain.User;
import com.lagou.mapper.OrderMapper;
import com.lagou.mapper.UserMapper;
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.Test;

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

public class mybatisTest {

    /*
        一对一关联查询:查询所有订单,与此同时还要查询出每个订单所属的用户信息
     */
    @Test
    public void test1() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        SqlSession sqlSession = sqlSessionFactory.openSession();

        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);

        List<Orders> orders = mapper.findAllWithUser();

        for (Orders order : orders) {
            System.out.println(order);
        }

        sqlSession.close();


    }
}
一对多:
一对多查询模型
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单

在这里插入图片描述

一对多查询语句:
SELECT u.*,o.id oid,o.ordertime,o.total,o.uid FROM orders o RIGHT JOIN USER u ON o.uid = u.id;
-- 或者
SELECT *,o.id oid FROM USER u LEFT JOIN orders o ON u.`id` = o.`uid`
-- 其中前面的*就是代表所有了,那么后面的就是起一个别名
<?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.lagou.mapper.UserMapper">
    <!--一对多关联查询:查询所有的用户,同时还要查询出每个用户所关联的订单信息-->

    <resultMap id="userMap" type="com.lagou.domain.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>


        <!--
        可以多次对应,包括association,他们之间也可嵌套(包括自己)
            collection : 一对多使用collection标签进行关联
            通常操作的是集合
            ofType泛型类型
            注意:在上面相同的情况下,对应下面的信息会发在一个集合里
            因为会检查,具体描述,当上面的字段内容都相同时,进行去重,即对应的下面内容放一个集合(合并)
不相同,则不会,若上面使用了对应id标签,那么就会对id进行去重,一样的,下面内容放一个集合
这个集合是第一个查到的没有去重的集合,那么说明,当出现第二个满足去重的集合时
将下面的内容放入第一个集合中,形成合并,而不相同的,可以将两个表的主键去掉,自己实验一下,我通过实验
只有当上面没有对应id标签,且上面字段内容有不一样的,那么后台查询时,会出现两个相同的id(因为条件是判断id的)
        -->
        <collection property="ordersList" ofType="com.lagou.domain.Orders">
            <id property="id" column="oid"></id>
            <!--通过起别名,使得同样的字段,不同了-->
            <result property="ordertime" column="ordertime"/>
            <result property="total" column="total"/>
            <result property="uid" column="uid"/>
        </collection>

    </resultMap>


    <select id="findAllWithOrder"  resultMap="userMap">
       SELECT u.*,o.id oid,o.ordertime,o.total,o.uid FROM orders o RIGHT JOIN USER u ON o.uid = u.id
    </select>
</mapper>
package com.lagou.mapper;

import com.lagou.domain.User;

import java.util.List;

public interface UserMapper {

    /*
        一对多关联查询:查询所有的用户,同时还要查询出每个用户所关联的订单信息
     */
    public List<User> findAllWithOrder();
}
/*
       一对多关联查询:查询所有用户及关联的订单信息
    */
    @Test
    public void test2() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        SqlSession sqlSession = sqlSessionFactory.openSession();

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

        List<User> allWithOrder = mapper.findAllWithOrder();

        for (User user : allWithOrder) {
            System.out.println(user);
        }

        sqlSession.close();


    }

多对多:
多对多查询的模型
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
多对多查询的需求:查询用户同时查询出该用户的所有角色

在这里插入图片描述

多对多查询语句
SELECT u.*,r.id rid,r.rolename,r.roleDesc FROM USER u LEFT JOIN sys_user_role ur ON ur.userid = u.id
		     LEFT JOIN sys_role r ON ur.roleid = r.id
		     -- 可以在上一个查询结果前,再次进行查询,from起作用,连接,select然后查询
 <!--多对多关联查询:查询所有的用户,同时还要查询出每个用户所关联的角色信息-->

    <resultMap id="userRoleMap" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>

        <collection property="roleList" ofType="role">
                          <!--如果没有值,那么就是空值或者默认(真的没有值,不对的自然报错),而不是null-->
            <id column="rid" property="id"></id>
            <result column="rolename" property="rolename"></result>
            <result column="roleDesc" property="roleDesc"></result>
         </collection>

    </resultMap>

    <select id="findAllWithRole" resultMap="userRoleMap">
        SELECT u.*,r.id rid,r.rolename,r.roleDesc FROM USER u LEFT JOIN sys_user_role ur ON ur.userid 
        = u.id
		     LEFT JOIN sys_role r ON ur.roleid = r.id
    </select>


 /*
        多对多关联查询:查询所有的用户,同时还要查询出每个用户所关联的角色信息
     */
    public List<User> findAllWithRole();
  /*
     多对多关联查询:查询所有用户及关联的角色信息
  */
    @Test
    public void test3() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        SqlSession sqlSession = sqlSessionFactory.openSession();

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

        List<User> allWithRole = mapper.findAllWithRole();

        for (User user : allWithRole) {
            System.out.println(user);
        }

        sqlSession.close();


    }
小结:
MyBatis 多表配置方式 :
/*
一对一配置:使用<resultMap>+<association>做配置 
 
一对多配置:使用<resultMap>+<collection>做配置 
 
多对多配置:使用<resultMap>+<collection>做配置 
 
多对多的配置跟一对多很相似,难度在于SQL 语句的编写
*/
MyBatis 嵌套查询:
什么是嵌套查询 :
嵌套查询就是将原来多表查询中的联合查询语句拆成单个表的查询,再使用 mybatis 的语法嵌套在一 起
/*
如
需求:查询一个订单,与此同时查询出该订单所属的用户 
联合查询:
SELECT * FROM orders o LEFT JOIN USER u ON o.`uid`=u.`id`; 
嵌套查询:
先查询订单 
SELECT * FROM orders 
再根据订单uid 外键,查询用户 
SELECT * FROM `user` WHERE id = #{根据订单查询的 uid} 
最后使用mybatis,将以上二步嵌套起来 

*/
一对一嵌套查询 :
需求:查询一个订单,与此同时查询出该订单所属的用户
sql语句:
//SELECT * FROM orders
//SELECT * FROM user WHERE id = #{id}
 /*
         根据id查询用户
     */

    public User findById(Integer id);
   /*
        一对一嵌套查询:查询所有订单,与此同时还要查询出每个订单所属的用户信息
     */

    public List<Orders> findAllWithUser2();

<resultMap id="orderMap2" type="com.lagou.domain.Orders">
        <id property="id" column="id"/>
        <result property="ordertime" column="ordertime"/>
        <result property="total" column="total"/>
        <result property="uid" column="uid"/>

        <!--问题:1.怎么去执行第二条sql , 2.如何执行第二条sql的时候,把uid作为参数进行传递
        那么解决这些问题,主要是执行对应sql,那么这里有个属性select,可以进行对应sql语句
        在对应元素对象里,会再次进行对应元素对象的执行,并将colimn的值作为参数传递给对应的sql语句
        使得最终所有对应数据都在对应对象里面即MappedStatement
        那么就可以将执行的sql语句的结果,封装到对应的user变量里
        -->
    <!--先弄好User,然后赋值给user-->
        <association property="user" javaType="com.lagou.domain.User"
                     select="com.lagou.mapper.UserMapper.findById" column="uid" />
    <!--uid是数据库的字段,所以大小写可以忽略写-->
<!--由于下面语句查询结果里面,没有对应的字段,那么只能使用其他语句的数值了

上下两次不同的封装,但都是变量

//这里要说明一下,如果没有指定uid,那么不会操作自动了,因为使用了
//所以这里就写上了  <result property="uid" column="uid"/>

-->


    </resultMap>

    <!--一对一嵌套查询-->
    <select id="findAllWithUser2" resultMap="orderMap2">
        SELECT * FROM orders
    </select>
 <!--根据id查询用户
    -->
    <select id="findById" resultType="com.lagou.domain.User" parameterType="int" >
        SELECT * FROM user WHERE id = #{id}
    </select>

/*
        一对一嵌套查询:查询所有订单及关联的用户信息
    */
    @Test
    public void test4() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        SqlSession sqlSession = sqlSessionFactory.openSession();

        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);

        List<Orders> allWithUser2 = mapper.findAllWithUser2();

        for (Orders orders : allWithUser2) {
            System.out.println(orders);
        }

        sqlSession.close();


    }

在这里插入图片描述

实际上最后执行的user是javaType的user
一对多嵌套查询 :
需求:查询一个用户,与此同时查询出该用户具有的订单
sql语句:
SELECT * FROM USER
SELECT * FROM orders WHERE uid = #{uid}
    <!--一对多嵌套查询:查询所有的用户,同时还要查询出每个用户所关联的订单信息-->

    <resultMap id="userOrderMap" type="com.lagou.domain.User">
        <id property="id" column="id"/>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>


        <!--
        association和collection都可以使用select来再次进行语句执行,将结果赋值,一般的,都会将id作为参数
        当然了,使用select和column通常情况下,不会再写sql语句,但若写的话
        则会使用对应sql语句的结果,即自己写的不会进行赋值
        对于association来说,字段赋值时,只会操作一个类,进行赋值,所以使用对应语句的话,基本全是该语句的赋值
        对于collection来说,一般的会进行去重,但是使用对应sql语句,那么进行赋值时,没有去重(没有判断了)
        他们的操作,都是在对应MappedStatement对象里操作,当指定对应位置时,就会通过反射执行对应语句
        而下面的操作,实际上可以看成内部嵌套一个这个对象,再次反射执行,形成一个赋值,然后返回
        -->

        <collection property="ordersList" ofType="com.lagou.domain.Orders" column="id"
                    select="com.lagou.mapper.OrderMapper.findByUid" ></collection>
<!--一般情况下,对应的参数column都是传递的一个的值,这里与其他不关联的是不同的-->
    </resultMap>

    <select id="findAllWithOrder2" resultMap="userOrderMap">
        SELECT * FROM USER
    </select>
<select id="findByUid" parameterType="int" resultType="com.lagou.domain.Orders">
        SELECT * FROM orders WHERE uid = #{uid}
          <!--后面基本可以继续加参数(对应的参数column都是传递的一个的值),当然,可能会报错,主要看自己的测试-->
    </select>
/*
       一对多嵌套查询:查询所有用户及关联的订单信息
   */
    @Test
    public void test5() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        SqlSession sqlSession = sqlSessionFactory.openSession();

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

        List<User> allWithOrder2 = mapper.findAllWithOrder2();

        for (User user : allWithOrder2) {
            System.out.println(user);

            // 要用到该用户的订单信息
            // System.out.println(user.getOrdersList());
        }

        sqlSession.close();


    }
 /*
        一对多嵌套查询:查询所有的用户,同时还要查询出每个用户所关联的订单信息
     */
    public List<User> findAllWithOrder2();
 /*
        根据uid查询对应订单
     */
    public List<Orders> findByUid(Integer uid);
多对多嵌套查询:
需求:查询用户同时查询出该用户的所有角色
sql语句:
SELECT * FROM USER
SELECT * FROM sys_role r INNER JOIN sys_user_role ur ON ur.roleid = r.id
			WHERE ur.userid = #{uid}
			-- 对于数据来说,多对多其实也是一个一对多,但是对于表来说,就是多对多
<resultMap id="userRoleMap2" type="com.lagou.domain.User">
        <id property="id" column="id"/>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>

        <!--注意:使用对应sql,就是自动赋值了,使用对应set方法执行
参数就是赋值的参数,没有对应的当然就是默认了
        且会根据变量直接赋值,在Mybatis里是这样的,Mybatis会直接赋值,而一般的是不会的
        实际上都是一个新的对象,所以没赋值就是默认值
        -->
        <collection property="roleList" ofType="com.lagou.domain.Role" column="id" 
                    select="com.lagou.mapper.RoleMapper.findByUid"></collection>
    <!--这个感觉就像没有去重一样,直接将查询的所有结果封装好-->
    </resultMap>

    <!--多对多嵌套查询:查询所有的用户,同时还要查询出每个用户所关联的角色信息-->
    <select id="findAllWithRole2" resultMap="userRoleMap2">
        SELECT * FROM USER
    </select>
<?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.lagou.mapper.RoleMapper">

    <select id="findByUid" resultType="com.lagou.domain.Role" parameterType="int">
        SELECT * FROM sys_role r INNER JOIN sys_user_role ur ON ur.roleid = r.id
			WHERE ur.userid = #{uid}
    </select>
</mapper>
  /*
        根据用户id查询对应角色
     */
    public List<Role>  findByUid(Integer uid);

  /*
        多对多嵌套查询:查询所有的用户,同时还要查询出每个用户所关联的角色信息
     */
    public List<User> findAllWithRole2();
  /*
    多对多嵌套查询:查询所有用户及关联的角色信息
*/
    @Test
    public void test6() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        SqlSession sqlSession = sqlSessionFactory.openSession();

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

        List<User> allWithRole2 = mapper.findAllWithRole2();

        for (User user : allWithRole2) {
            System.out.println(user);
        }

        sqlSession.close();

    }
小结:
/*
一对一配置:使用<resultMap>+<association>做配置,通过column 条件,执行 select 查询 
 
一对多配置:使用<resultMap>+<collection>做配置,通过column 条件,执行 select 查询 
 
多对多配置:使用<resultMap>+<collection>做配置,通过column 条件,执行 select 查询 
 
优点:简化多表查询操作 
 
缺点:执行多次sql 语句,浪费数据库性能 

*/
最后:自动赋值一般是不会报错的
只是没有找到对应的set方法(没有对应set方法,Mybatis会直接赋值),不会进行操作而已
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值