SSM第五讲 动态SQL与高级查询

时间:2022-07-25
本文章向大家介绍SSM第五讲 动态SQL与高级查询,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

动态SQL与高级查询

  • 动态sql
  • 高级查询

一. 动态SQL(重点)

1. 动态SQL是什么

就是相对与固定SQL。就是通过传入的参数不一样,可以组成不同结构的SQL语句. 这种根据参数的条件修改SQL结构的SQL语句,我们称为动态SQL语句.

2. 动态SQL有什么用

a.根据条件组装不同结构的SQL语句,可以提高SQL代码的重用性.

b.满足某些特定需求,如,条件判断查询

3. 基于XML的实现

3.1. 标签包括

用于声明公有的SQL语句块.,在操作标签中调用 [不建议用]

不建议的原因,会导致代码难以维护。

类似java if(){},用于判断

:类似java的foreach循环,一般用户批量处理的SQL语句

:切割标签,主要用于切割关键字的头和尾的字符.新版的Mybatis使用的几率很少.

:使用 set标签就是set关键字,可以在update 的时候set 关键字后面的,逗号可以自动忽略

:使用where标签作为where关键字,好处如果where后面的条件都不成立,忽略where关键字.

: java的swithc case

3.2. 示例代码

public interface StudentMapper {
    /**
     * 插入记录
     * @param entity
     * @return
     */
    int insert(Student entity);
    /**
     * 批量插入记录
     * @param entity
     * @return
     */
    int batchInsert(List<Student> entitys);
    /**
     * 删除记录
     * @param sid
     * @return
     */
    int deleteById(int sid);
    /**
     * 批量删除记录
     * @param sid
     * @return
     */
    int deleteByIds(int... sid);
    /**
     * 更新记录
     * @param entity
     * @return
     */
    int update(Student entity);
    /**
     * 更新记录,如果传入的值不为空,才更新
     * @param entity
     * @return
     */
    int updateByNotnull(Student entity);
    /**
     * 更新记录,如果传入的值不为空,才更新
     * @param entity
     * @return
     */
    int updateByNotnull1(Student entity);
    /**
     * 查询所有的记录
     * @return
     */
    List<Student> findAll();
    /**
     * 将查询的数据放在map里面
     * @return
     */
    List<Map<String,Object>> findAllByMap();
    /**
     * 通过条件查询
     * @param entity
     * @return
     */
    List<Student> findByCondition(Student entity);
    /**
     * 通过条件查询
     * @param entity
     * @return
     */
    List<Student> findByCondition1(Student entity);
 
}

3.3. 映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd" >
<!-- 命名空间,用于指定动态创建的对象返回的映射接口 -->
<mapper namespace="cn.mapper.StudentMapper">
  <!--
    <sql> <include> 用于声明SQL语句块. [不建议用,原因导致SQL的代码维护性差]
    
    需求:更新学生表,如果参数没有值的字段,就不用设置null值.保持不变.
    
   需求:实现学生信息的批量插入.  
   需求:实现学生的批量删除  
   需求:通过传入的条件判断,实现模糊查询  
  
   -->
   <sql id="sqlfeild">
    <!-- 这里可以写任何的SQL语句块 -->
    AID, SNAME, SEX, BIRTHDAY, AGE
   </sql>
   
   <insert id="insert">
 
     INSERT INTO student(<include refid="sqlfeild"></include>) VALUES (#{sid}, #{sname}, #{sex}, #{birthday}, #{age})
   </insert>
   
   <insert id="batchInsert">
 
    INSERT INTO student(AID, SNAME, SEX, BIRTHDAY, AGE) VALUES
    <!--
    collection集合类型:
    list :如果参数是一个集合使用list
    array:参数是一个数组使用array
    
    注意:open,close一次循环只增加一次.不是每次循环都增加指定字符.
    open:循环前面增加的字符
    close:循环后面增加的字符
    
    separator:每次循环的分割的字符,使用separator这个属性的好处,会忽略最后一个字符
    index:索引
    item:每次循环的参数对象变量名
     -->
     <foreach collection="list"  separator=","  item="item">
        (#{item.aid}, #{item.sname}, #{item.sex}, #{item.birthday}, #{item.age})
 </foreach>
   
   </insert>
   
 
   <!-- 删除记录 -->
   <delete id="deleteById">
        DELETE FROM student WHERE SID=#{sid}
   </delete>
   
   <delete id="deleteByIds">
         DELETE FROM student WHERE SID in
         <foreach collection="array" item="item" open="(" close=")" separator=",">
           <!-- 注意,因为int deleteByIds(int... sid),的ids就是一个值,不是实体,所以item的值就是ids的值 -->
           #{item}
         </foreach>
         
   </delete>
 
 
   <update id="update">
      UPDATE student SET AID=#{aid},SNAME=#{sname},SEX=#{sex},BIRTHDAY=#{birthday},AGE=#{age} WHERE SID=#{sid}
   </update>
   
   <update id="updateByNotnull">
    <!-- 注意事项:
      参数的属性名在标签里面,不要加#{}
     -->
      UPDATE student
      
      <set>
      
<!--       SET  -->
      <if test="aid!=null">
      AID=#{aid},
      </if>
       <if  test="sname!=null">
      SNAME=#{sname},
      </if>
      <if  test="sex!=null">
      SEX=#{sex},
      </if>
      <if test="birthday!=null">
      BIRTHDAY=#{birthday},
      </if>
      <if test="age!=null">
      AGE=#{age}
       </if>
     </set>
      WHERE SID=#{sid}
   </update>
   
      <update id="updateByNotnull1">
    <!-- 注意事项:
      参数的属性名在标签里面,不要加#{}
     -->
      UPDATE student
      
      <!-- 意思就是set关键字的最后的逗号,去掉 -->
      <!--
      prefix="" :就是在<trim>标签包括的SQL关键字,前面增加字符
      prefixOverrides="" 就是在<trim>标签包括的SQL关键字,前面删除指定的字符
      suffix="":就是在<trim>标签包括的SQL关键字的最后面,增加字符
      suffixOverrides:就是在<trim>标签包括的SQL关键字最后面删除指定字符
      
      注意,trim一定要注意,起作用的是关键字,如UPDATE student SET SEX=? WHERE SID=? ,<trim>包括在set关键字,
      那么prefix就是set前面,suffix就是set关键的最后面,where的前面.
      
       -->
      <trim  suffixOverrides="," >
      SET
      <if test="aid!=null">
      AID=#{aid},
      </if>
       <if  test="sname!=null">
      SNAME=#{sname},
      </if>
      <if  test="sex!=null">
      SEX=#{sex},
      </if>
      <if test="birthday!=null">
      BIRTHDAY=#{birthday},
      </if>
      <if test="age!=null">
      AGE=#{age}
       </if>
    </trim>
      WHERE SID=#{sid}
   </update>
 
  <select resultType="Student" id="findAll">
     SELECT <include refid="sqlfeild"></include> FROM student
 
  </select> 
  <!-- mybatis支持将数据放在map里面 -->
  <select resultType="map" id="findAllByMap">
     SELECT * FROM student
  </select> 
  
  <!-- 模糊查询 -->
  <select resultType="Student" id="findByCondition">
   SELECT * FROM student
   <!-- 使用where标签作用SQL的关键字,判断条件 -->
   <where>
     <!-- 使用choose,后面的条件必须要在前面的条件不成立的情况才会判断 -->
     <!--
     switch()
       case:
         break;
       default:
     
     
      -->
     <choose>
        <when test="aid!=null">
            AID = #{aid}
        </when>
        <when test="sname!=null">
            SNAME=#{sname}
        </when>
        <when test="sex!=null">
            SEX = #{sex}
        </when>
        <otherwise>
        1=1
        </otherwise>
     </choose>
   </where>
   
  </select>
  
    <!-- 模糊查询 -->
  <select resultType="Student" id="findByCondition1">
   SELECT * FROM student
   <!-- 使用where标签作用SQL的关键字,判断条件 ,好处,会自动忽略多余字符-->
   <where>
 
        <if test="aid!=null">
           and AID = #{aid}
        </if>
        <if test="sname!=null">
           and SNAME=#{sname}
        </if>
        <if test="sex!=null">
           and  SEX = #{sex}
        </if>
    
   </where>
   
  </select>
 
</mapper>

动态SQL的其实就根据条件拼接SQL语句. 不管你使用哪种标签组合,只要最后的结果生成的SQL语句是你想要的就可以.

4. 基于注解的实现

4.1. 映射接口

public interface StudentMapper {
/**
 * InsertProvider注解:作用是设置插入的动态SQL语句
 *   type:指定类的类型
 *   method:指定对应的方法名
 * 批量插入记录
 * @param entity
 * @return
 */
@InsertProvider(type=StudentProvider.class,method="batchInsert")
int batchInsert(List<Student> entitys);
/**
 * 批量删除
 * @param sid
 * @return
 */
@DeleteProvider(type=StudentProvider.class,method="deleteByIds")
int deleteByIds(int... sid);
/**
 * 更新非空字段
 * @param entity
 * @return
 */
@UpdateProvider(type=StudentProvider.class,method="updateByNotnull")
int updateByNotnull(Student entity);
/**
 * 通过条件查询
 * @param entity
 * @return
 */
@SelectProvider(type=StudentProvider.class,method="findByCondition")
List<Student> findByCondition(Student entity);
 
}

4.2. 动态SQL实现类

public class StudentProvider {
 
    /**
     * 注意: 1.动态SQL语句的提供类,提供的就是拼接好的SQL语句 2.如果参数不是标量类型或者单个实体对象,那么必须使用Map包装起来
     * 标量类型=基础数据类型、基础数据类型包装类、String
     *
          *如下面的方法参数中,不对传入的list进行包装,会出现如下错误 Caused by: org.apache.ibatis.binding.BindingException: Parameter '0' not found. Available parameters are [collection, list]
     * @param entitys
     * @return 返回的是根据参数拼接好的SQL语句
     */
    public String batchInsert(Map<String,List<Student>> map) {

    List<Student> entitys= map.get("list");

    String sql = "INSERT INTO tb_student (stu_name, stu_age, stu_password) VALUES ";
    StringBuilder builder = new StringBuilder(sql);
    for (int i = 0; i < entitys.size(); i++) {
    builder.append("(#{list[" + i + "].stuName}, #{list[" + i + "].stuAge}, #{list[" + i + "].stuPassword})");
    // 判断最后一次不可以有逗号
    if (i < entitys.size() - 1) {
    builder.append(",");
    }
    }
    System.out.println(builder.toString());
    return builder.toString();
    }


    /**
     * 注意:为什么不使用... 答:原因是泛型语法不支持类型是...的类型,而可变参数类型,本质是一个数组,所以设置为一个数组
     *
     * @param sid
     * @return
     */
    public String deleteByIds(Map<String, int[]> idsMap) {
    int[] ids = idsMap.get("array");
    String sql = "DELETE FROM tb_student WHERE stu_id in ";
    StringBuilder builder = new StringBuilder(sql);
    builder.append("(");
    for (int i = 0; i < ids.length; i++) {
    // 注意事项:如果使用注解配置,只能通过数组的别名与下标来获得OGNL的值
    builder.append("#{array[" + i + "]}");
    // 判断最后一次不可以有逗号
    if (i < ids.length - 1) {
    builder.append(",");
    }
    }
    builder.append(")");
    System.out.println(builder.toString());
    return builder.toString();
    }

    /**
     * 单个对象不需要使用Map包起来 在字符串拼接SQL语句OGNL 如果是一个单对象,规则与配置文件一致 如果是集合或者数组,规则为
     * list[下标].属性名 ,array[下标].属性名
     *
     * @param entity
     * @return
     */
    public String updateByNotnull(Student entity) {
    String sql = "UPDATE tb_student SET ";
    StringBuilder builder = new StringBuilder(sql);
    if (entity.getStuName() != null) {
    builder.append("stu_name = #{stuName},");
    }
    if (entity.getStuAge() != null) {
    builder.append("stu_age = #{stuAge},");
    }

    if (entity.getStuPassword() != null) {
    builder.append("stu_password = #{stuPassword},");
    }
    // 必须要去掉最后的逗号
    builder.deleteCharAt(builder.length() - 1);

    builder.append("WHERE stu_id = #{stuId}");
    System.out.println(builder.toString());
    return builder.toString();
    }

    /**
     * 条件查询
     * @param entity
     * @return
     */
    public String findByCondition(Student entity) {
    String sql = "SELECT * FROM tb_student WHERE 1=1 ";
    StringBuilder builder = new StringBuilder(sql);
    if (entity.getStuName() != null) {
    builder.append("AND stu_name like CONCAT('%',#{stuName},'%')");
    }
    if (entity.getStuAge() != null) {
    builder.append("AND stu_age = #{stuAge}");
    }

    if (entity.getStuPassword() != null) {
    builder.append("AND stu_password = #{stuPassword}");
    }
    System.out.println(builder.toString());
    return builder.toString();
    }

}

4.3. 测试类

public class StudentMapperTest {
    /**
     * 增加
     */
    @Test
    public void batchInsert(){
    try {
    //1.获得操作对象,session
    SqlSession session = MybatisUtils.getSession();
    //2.获得操作接口的动态对象
    StudentMapper studentMapper = session.getMapper(StudentMapper.class);
    Student student1=new Student();
    student1.setStuName("张三");
    Student student2=new Student();
    student2.setStuName("李四");
    List<Student> students=new ArrayList<>();
    students.add(student1);
    students.add(student2);
    //3.插入对象
    int count = studentMapper.batchInsert(students);
    //4.提交事务
    session.commit();
    //5.关闭
    session.close();
    System.out.println("返回值:"+count);
    } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }


    }
    /**
     * 删除
     */
    @Test
    public void deleteByIds(){
    //1.获得操作对象,session
    SqlSession session = MybatisUtils.getSession();
    //2.获得操作接口的动态对象
    StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        //3.删除指定Id的记录
    int count = studentMapper.deleteByIds(new int[]{1,2});
    //int count = studentMapper.deleteByIds(2);
    //4.提交事务
    session.commit();
    //5.关闭
    session.close();
    System.out.println("返回值:"+count);
    }
    /**
     * 更新
     */
    @Test
    public void updateByNotnull(){
    //1.获得操作对象,session
    SqlSession session = MybatisUtils.getSession();
    //2.获得操作接口的动态对象
    StudentMapper studentMapper = session.getMapper(StudentMapper.class);
    Student student=new Student();
    student.setStuName("张三2");
    //student.setStuAge(20);
    student.setStuId(2L);
        //3.更新对象
    int count = studentMapper.update(student);
    //4.提交事务
    session.commit();
    //5.关闭
    session.close();
    System.out.println("返回值:"+count);
    }
    /**
     * 条件查询
     */
    @Test
    public void findByCondition(){
    //1.获得操作对象,session
    SqlSession session = MybatisUtils.getSession();
    //2.获得操作接口的动态对象
    StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        Student entity=new Student();
        entity.setStuName("张三");
        entity.setStuId(1L);

    //3.返回单条数据
    List<Student> students = studentMapper.findByCondition(entity);
    //5.关闭
    session.close();
    for (Student student : students) {
    System.out.println(student.getStuName());
    }
    }
}

4.4. 为什么如何使用集合或者数组需要包一层Map

为什么Provider的方法,如果遇到数组或者集合,需要包一层Map。因为作者的设计就是这样。没有可以解释的理由。具体判断代码如下。在Mybatis的源码的

org.apache.ibatis.session.defaults.DefaultSqlSession.wrapCollection(Object)对应的代码!!!!

二.高级查询(难点)

前提:在使用POJO来绑定数据库表的数据的时候使用!!

所谓的高级查询,就是多表连接查询.并将查询的结果绑定到指定对象里面.

如果要实现将多个表的数据绑定到一个对象,那必须要将表与表之间关系建立在实体类里面。那么首先要理解表与表直接的关系有哪些?

表与表的关系:

一对一

一对多

多对一

多对多

Mybatis是使用标签

<association> :一的一方配置<association>标签

<collection>  :多的一方配置<collection>:标签

1. 根据数据库的关系创建实体类

多表查询时,需要一个实体类多个表的数据.那么需要将表与表的关系,也要反应在实体类与实体类之间的关系.

配置一条数据的一方,使用实体类的引用

配置多条数据的一方,使用List集合

地址表

public class Address {
    private Integer aid;
    private String aname;
    private Student student;
    public Student getStudent() {
    return student;
    }
    public void setStudent(Student student) {
    this.student = student;
    }
    public String getAname() {
    return aname;
    }
    public void setAname(String aname) {
    this.aname = aname;
    }
    public Integer getAid() {
    return aid;
    }
    public void setAid(Integer aid) {
    this.aid = aid;
	}
 
 
}

学生表

public class Student {
    private Integer sid;//int(11) not null auto_increment comment '编号',
    private Integer aid;//int(11) null default '0',
    private String sname;//varchar(50) null default null comment '姓名',


    private String sex;//char(3) null default null comment '性别',
    private Date birthday;//date null default null comment '生日',
    private Integer age;//int(11) null default null comment '年龄',
    //假定一个地址对应一个学生,那么这里就配置一个实体类
    private Address address;
    //多的一方,使用集合
    private List<Course> courses;
    public List<Course> getCourses() {
    return courses;
    }
    public void setCourses(List<Course> courses) {
    this.courses = courses;
    }
    public Address getAddress() {
    return address;
    }
    public void setAddress(Address address) {
    this.address = address;
    }
    public Integer getSid() {
    return sid;
    }
    public void setSid(Integer sid) {
    this.sid = sid;
    }
    public Integer getAid() {
    return aid;
    }
    public void setAid(Integer aid) {
    this.aid = aid;
    }

    public String getSex() {
    return sex;
    }
    public void setSex(String sex) {
    this.sex = sex;
    }
    public Date getBirthday() {
    return birthday;
    }
    public void setBirthday(Date birthday) {
    this.birthday = birthday;
    }
    public Integer getAge() {
    return age;
    }
    public void setAge(Integer age) {
    this.age = age;
    }
    public String getSname() {
    return sname;
    }
    public void setSname(String sname) {
    this.sname = sname;
    }
}

课程表

public class Course {
    private Integer cid;//int(11) not null auto_increment comment '科目编号',
    private Integer sid;//int(11) null default null comment '学生编号',
    private Integer tid;//int(11) null default null comment '教师编号',
    private String courseName;//varchar(50) null default null comment '科目名',
    public Integer getCid() {
    return cid;
    }
    public void setCid(Integer cid) {
    this.cid = cid;
    }
    public Integer getSid() {
    return sid;
    }
    public void setSid(Integer sid) {
    this.sid = sid;
    }
    public Integer getTid() {
    return tid;
    }
    public void setTid(Integer tid) {
    this.tid = tid;
    }
    public String getCourseName() {
    return courseName;
    }
    public void setCourseName(String courseName) {
    this.courseName = courseName;
    }
}

根据实体类的定义,如果是属性集合就配置,如果返回的数据是一个实体的就配置

2. association标签的使用

接口代码

public interface StudentMapper {
 
    /**
     * 查询所有的学生信息,并包括地址信息
     * @return
     */
    List<Student> findStudentAndAddress();
    /**
     * 查询所有的学生信息,并包括地址信息
     * @return
     */
    List<Student> findStudentAndAddress1();
    /**
     * 查询学生的信息,包括学生选择的课程
     * @return
     */
    List<Student> findStudentAndCourse();
    /**
     * 查询多个个表,将数据放在map里面
     * @return
     */
    List<Map<String,Object>> findStudentAndCourseToMap();
 
}

映射文件代码

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd" >
<!-- 命名空间,用于指定动态创建的对象返回的映射接口 -->
<mapper namespace="cn.mapper.StudentMapper">
 
   <!-- #查询学生的信息,并且包括学生的学习的课程 -->
   <resultMap type="Student" id="studentAndCourseResultMap" autoMapping="true">
     <!-- 多的一方使用collection -->
     <!--
     property:集合的属性名
     ofType:集合元素的类型
     select:就是指定查询的操作,操作的结果一定要与collection的类型匹配.
     column:就是传入到select的ID的字段名
      -->
     <collection property="courses" column="sid" autoMapping="true" ofType="Course" select="findCourseBySid">
        
     </collection>
   </resultMap>
   
 
   <!-- 创建一个resultMap组合 findStudentAndAddress操作的结果-->
   <resultMap type="Student" id="studentAndAddressResultMap" autoMapping="true">
     <!-- 如果是多个表组合autoMapping默认是false,如果不想所有字段都写,要显示声明autoMapping="true" -->
     <!--
     property:指定实体类的属性
     column:指定这个实体类属性对应的外键字段名
     javaType:指定实体类的类型
     autoMapping:表示,如果数据库的字段和属性一样,自动匹配
      -->
      <association property="address" column="aid" javaType="Address" autoMapping="true">
         <id property="aid" column="AID"/>
         <result property="aname" column="aname"/>
      </association>
   
   </resultMap>
   
   
   <resultMap type="Student" id="studentAndAddressResultMap1" autoMapping="true">
     <!-- 一的一方 -->
     <!-- 指定select查询的操作,查询的的结果一定要与property的实体类的字段匹配 -->
     <!-- 注意事项,使用select去获得地址表的数据,因为学生表与地址是通过aid来关联的,那么需要指定aid -->
     <!-- 一个映射调用另一个映射文件的操作是,使用"命名空间.操作ID" -->
     <association property="address" column="aid" autoMapping="true" select="cn.mapper.AddressMapper.findAddressById">
     </association>
   
   </resultMap>
 
  
  <!-- 需求:查询学生表的所有数据,并且包括地址表的记录 -->
  <!-- 方案1:将两个表的数据使用一条SQL语句查询出来,然后使用ResultMap组合查询的结果 -->
  <!-- 查询多个表数据不要使用resultType,因为resultType只能接收一个表的数据,如果要接收两个以上的表的数据使用resultMap -->
  <select resultMap="studentAndAddressResultMap" id="findStudentAndAddress">
    select * from student s,address  a where s.AID=a.aid
   </select>
   
   <!-- 方案2: 先查询Student表的记录,然后在resultMap的子标签association的select属性指定查询的操作,将结果返回到实体类-->
   <select resultMap="studentAndAddressResultMap1" id="findStudentAndAddress1">
     SELECT * FROM student
   </select>
   
   <!-- 查询学生的信息 -->
   <select resultMap="studentAndCourseResultMap" id="findStudentAndCourse">
     SELECT * FROM student
   </select>
   <!-- 查询指定学生编号的课程,通过学生的编号,我们知道该学生选择了哪些课程 -->
   <select resultType="Course" id="findCourseBySid">
     select * from course where sid=#{sid}
   </select>
 
   <select resultType="map" id="findStudentAndCourseToMap">
     select * from student s,course c where s.SID=c.SID
   </select>
   
</mapper>

测试代码

public class StudentMapperTest {

    @Test
    public void findStudentAndAddress(){
        SqlSession session = DbUtils.getSession();
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        List<Student> students = studentMapper.findStudentAndAddress();
        for(Student s:students){
            System.out.println("学生姓名:"+s.getSname()+",地址:"+s.getAddress().getAname());
        }
    }

    @Test
    public void findStudentAndAddress1(){
        SqlSession session = DbUtils.getSession();
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        List<Student> students = studentMapper.findStudentAndAddress1();
        for(Student s:students){
            System.out.println("学生姓名:"+s.getSname()+",地址:"+s.getAddress().getAname());
        }
    }


    @Test
    public void findStudentAndCourse(){
        SqlSession session = DbUtils.getSession();
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        List<Student> students = studentMapper.findStudentAndCourse();
        for(Student s:students){
            System.out.println("学生姓名:"+s.getSname());
            for(Course c:s.getCourses()){
                System.out.println("--课程:"+c.getCourseName());
            }
        }
    }

    @Test
    public void findStudentAndCourseToMap(){
        SqlSession session = DbUtils.getSession();
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        List<Map<String, Object>> students = studentMapper.findStudentAndCourseToMap();
        for(Map<String, Object> map:students){
            //必须跟数据库的字段一致
            System.out.print("学生姓名:"+map.get("SNAME")+",");

            System.out.println("--课程:"+map.get("COURSE_NAME"));

        }
    }
}

3. collection标签的使用

映射文件代码

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd" >
<!-- 命名空间,用于指定动态创建的对象返回的映射接口 -->
<mapper namespace="cn.mapper.StudentMapper">
 
   <!-- #查询学生的信息,并且包括学生的学习的课程 -->
   <resultMap type="Student" id="studentAndCourseResultMap" autoMapping="true">
     <!-- 多的一方使用collection -->
     <!--
     property:集合的属性名
     ofType:集合元素的类型
     select:就是指定查询的操作,操作的结果一定要与collection的类型匹配.
     column:就是传入到select的ID的字段名
      -->
     <collection property="courses" column="sid" autoMapping="true" ofType="Course" select="findCourseBySid">
        
     </collection>
   </resultMap>
   
 
   <!-- 创建一个resultMap组合 findStudentAndAddress操作的结果-->
   <resultMap type="Student" id="studentAndAddressResultMap" autoMapping="true">
     <!-- 如果是多个表组合autoMapping默认是false,如果不想所有字段都写,要显示声明autoMapping="true" -->
     <!--
     property:指定实体类的属性
     column:指定这个实体类属性对应的外键字段名
     javaType:指定实体类的类型
     autoMapping:表示,如果数据库的字段和属性一样,自动匹配
      -->
      <association property="address" column="aid" javaType="Address" autoMapping="true">
         <id property="aid" column="AID"/>
         <result property="aname" column="aname"/>
      </association>
   
   </resultMap>
   
   
   <resultMap type="Student" id="studentAndAddressResultMap1" autoMapping="true">
     <!-- 一的一方 -->
     <!-- 指定select查询的操作,查询的的结果一定要与property的实体类的字段匹配 -->
     <!-- 注意事项,使用select去获得地址表的数据,因为学生表与地址是通过aid来关联的,那么需要指定aid -->
     <!-- 一个映射调用另一个映射文件的操作是,使用"命名空间.操作ID" -->
     <association property="address" column="aid" autoMapping="true" select="cn.mapper.AddressMapper.findAddressById">
     </association>
   
   </resultMap>
 
  
  <!-- 需求:查询学生表的所有数据,并且包括地址表的记录 -->
  <!-- 方案1:将两个表的数据使用一条SQL语句查询出来,然后使用ResultMap组合查询的结果 -->
  <!-- 查询多个表数据不要使用resultType,因为resultType只能接收一个表的数据,如果要接收两个以上的表的数据使用resultMap -->
  <select resultMap="studentAndAddressResultMap" id="findStudentAndAddress">
    select * from student s,address  a where s.AID=a.aid
   </select>
   
   <!-- 方案2: 先查询Student表的记录,然后在resultMap的子标签association的select属性指定查询的操作,将结果返回到实体类-->
   <select resultMap="studentAndAddressResultMap1" id="findStudentAndAddress1">
     SELECT * FROM student
   </select>
   
   <!-- 查询学生的信息 -->
   <select resultMap="studentAndCourseResultMap" id="findStudentAndCourse">
     SELECT * FROM student
   </select>
   <!-- 查询指定学生编号的课程,通过学生的编号,我们知道该学生选择了哪些课程 -->
   <select resultType="Course" id="findCourseBySid">
     select * from course where sid=#{sid}
   </select>
 
   <select resultType="map" id="findStudentAndCourseToMap">
     select * from student s,course c where s.SID=c.SID
   </select>
   
</mapper>

如果记得这样关联配置复杂,使用map可以代替上面 的配置.

4. 基于注解配置

映射接口

–学生表

/**
 * 注意事项:操作接口的方法名是不可以相同的 答:因为方法名对应的xml的是ID,Id意味必须是唯一的。
 *
 * @author ranger
 *
 */
public interface StudentMapper {
 
/**
 * 通过ID查询指定的记录
 *
 * @param id
 * @return
 */
@Select(value="SELECT * FROM tb_student WHERE stu_id=#{id}")
/**
 * Results注解,对应<resultmap>
 * Result注解,对应的是<result 标签>同时可以配置One注解和Many注解
 * One注解:对应<association>标签
 * Many注解:对应<collection>标签
 * @param id
 * @return
 */
@Results(value=@Result(many=@Many(select="cn.mapper.ResultMapper.findByStuId",fetchType=FetchType.LAZY),property="results",column="stu_id"))
Student findById(Object id);
 
}

–成绩表

/**
 * 注意事项:操作接口的方法名是不可以相同的 答:因为方法名对应的xml的是ID,Id意味必须是唯一的。
 *
 * @author ranger
 *
 */
public interface ResultMapper {
 
 
   @Select(value="SELECT * FROM tb_result WHERE stu_id=#{stuId}")
   List<Result> findByStuId(Object sutId);
   
   /**
    * 通过ID获得对应的成绩信息
    * @param id
    * @return
    */
   @Select(value="SELECT * FROM tb_result WHERE res_id=#{resId}")
   @Results(value=@org.apache.ibatis.annotations.Result(property="student",column="stu_id",one=@One(fetchType=FetchType.EAGER,select="cn.mapper.StudentMapper.findById")))
   Result findById(Object id);
}

5 延迟加载

在一些多表连接的查询中,只要用到主表数据,不需要使用关联表数据的时候不查询关联表,就是延迟加载 。

mybatis延迟加载配置:

1.在主配置文件中设置两个setting

lazyLoadingEnabled需要为true(默认true),表示开启延迟加载

aggressiveLazyLoading需要为false(默认为true) 表示取消积极加载策略

(积极加载策略的意思就是当前的对象关联的属性也会立即加载)

2.在关联查询的xml或者注解中配置Fetch=lazy

<settings>
	<!-- 使用驼峰命名 -->
		<setting name="mapUnderscoreToCamelCase" value="true"/>
		<!-- 
		lazyLoadingEnabled 默认值为true 延迟加载开启
		aggressiveLazyLoading默认值是true
		积极加载,如果它为true,使用了延迟加载,所有的级联属性都会加载(发送sql)
		如果为false,级联属性不会自动加载,需要用到才加载
		 -->
		<!-- <setting name="lazyLoadingEnabled" value="true"/> -->
		<setting name="aggressiveLazyLoading" value="false"/>
	</settings>
@Select("select * from tb_result")
    @Results(id="resultAndStudentMap",value={
    		/*@Result(column="res_id",property="resId"),
    		@Result(column="res_subject",property="resSubject"),
    		@Result(column="res_score",property="resScore"),*/
    		@Result(column="stu_id",property="stuId"),
    		//one=@One(select="",fetchType=):   one表示关联一方属性的映射如何封装    @One设置一方映射操作  等于association select:需要发送的sql语句id mapper接口.方法名 
    		//fetchType:设置是否懒加载     设置了懒加载FetchType.LAZY,如果不需要用到关联属性,则不发送关联sql语句
    		@Result(column="stu_id",property="student",one=@One(select="cn.nyse.mapper.StudentMapper.selectByPrimaryKey",fetchType=FetchType.LAZY)),
    		
    })
    List<cn.nyse.entity.Result> findAllResultAndStudent();

测试

public void testFindAllResultAndStudent(){
		List<Result> list = resultMapper.findAllResultAndStudent();
		for (Result result : list) {
			//在默认情况下,不使用关联的属性信息(学生),也会关联学生表查询
//			System.out.println("当前的学生为:"+result.getStudent());
			//不使用不加载的延迟策略,不能直接打印对象信息   只要不使用关联的student属性,就不会查tb_student表
			System.out.println("成绩为:"+result.getResSubject()+","+result.getResScore()+result.getStuId()+result.getResId());
			System.out.println("-------------------------------开始查询student数据--------------------------------------");
			System.out.println(result.getStudent());
			System.out.println(result.getClass().getName());
		}
	}
ject",property=“resSubject”),
 @Result(column=“res_score”,property=“resScore”),*/
 @Result(column=“stu_id”,property=“stuId”),
 //one=@One(select="",fetchType=): one表示关联一方属性的映射如何封装 @One设置一方映射操作 等于association select:需要发送的sql语句id mapper接口.方法名
 //fetchType:设置是否懒加载 设置了懒加载FetchType.LAZY,如果不需要用到关联属性,则不发送关联sql语句
 @Result(column=“stu_id”,property=“student”,one=@One(select=“cn.nyse.mapper.StudentMapper.selectByPrimaryKey”,fetchType=FetchType.LAZY)),
})
List<cn.nyse.entity.Result> findAllResultAndStudent();
测试

~~~java
public void testFindAllResultAndStudent(){
		List<Result> list = resultMapper.findAllResultAndStudent();
		for (Result result : list) {
			//在默认情况下,不使用关联的属性信息(学生),也会关联学生表查询
//			System.out.println("当前的学生为:"+result.getStudent());
			//不使用不加载的延迟策略,不能直接打印对象信息   只要不使用关联的student属性,就不会查tb_student表
			System.out.println("成绩为:"+result.getResSubject()+","+result.getResScore()+result.getStuId()+result.getResId());
			System.out.println("-------------------------------开始查询student数据--------------------------------------");
			System.out.println(result.getStudent());
			System.out.println(result.getClass().getName());
		}
	}