mybatis-第2天讲义

一.接口代理方式实现Dao ***

1.1 代理开发方式介绍
  • 采用 Mybatis 的代理开发方式实现 DAO 层的开发,这种方式是我们后面进入企业的主流。

  • Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法

  • 总结:接口代理方式,其实就我们自己不用实现MapperImpl(持久层实现类),mybatis通过接口代理的方式帮助我们实现

  • Mapper 接口开发需要遵循以下规范:

    1) Mapper.xml文件中的namespace与mapper接口的全限定名相同

    2) Mapper接口方法名和Mapper.xml中定义的每个statement的id相同

    3) Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同

    4) Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

  • 如下图:
    在这里插入图片描述

1.2 代码实现
  • 实现步骤:
  1. 删除 mapper 层接口的实现类(新建一个项目mybatis02,将第一天的代码全部复制过来)

  2. 修改映射配置文件

    <!-- 将命名空间修改为StudentMapper全路径名-->
    <mapper namespace="com.itheima.mapper.StudentMapper">
    
  3. 修改 service 层接口的实现类,采用接口代理方式实现功能(因为现在没有持久层实现类了,所以业务层无法直接调用,需要自己实现并且调用代理接口方式的MapperImpl)

    package com.itheima.service.impl;
    
    import com.itheima.bean.Student;
    import com.itheima.mapper.StudentMapper;
    import com.itheima.service.StudentService;
    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 java.io.IOException;
    import java.io.InputStream;
    import java.util.List;
    /*
        业务层实现类
     */
    public class StudentServiceImpl implements StudentService {
    
        @Override
        public List<Student> selectAll() {
            List<Student> list = null;
            SqlSession sqlSession = null;
            InputStream is = null;
            try{
                //1.加载核心配置文件
                is = Resources.getResourceAsStream("MyBatisConfig.xml");
    
                //2.获取SqlSession工厂对象
                SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    
                //3.通过工厂对象获取SqlSession对象
                sqlSession = sqlSessionFactory.openSession(true);
    
                //4.获取StudentMapper接口的实现类对象
                StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // StudentMapper mapper = new StudentMapperImpl();
    
                //5.通过实现类对象调用方法,接收结果
                list = mapper.selectAll();
    
            } catch (Exception e) {
    
            } finally {
                //6.释放资源
                if(sqlSession != null) {
                    sqlSession.close();
                }
                if(is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            //7.返回结果
            return list;
        }
    
        @Override
        public Student selectById(Integer id) {
            Student stu = null;
            SqlSession sqlSession = null;
            InputStream is = null;
            try{
                //1.加载核心配置文件
                is = Resources.getResourceAsStream("MyBatisConfig.xml");
    
                //2.获取SqlSession工厂对象
                SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    
                //3.通过工厂对象获取SqlSession对象
                sqlSession = sqlSessionFactory.openSession(true);
    
                //4.获取StudentMapper接口的实现类对象
                StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // StudentMapper mapper = new StudentMapperImpl();
    
                //5.通过实现类对象调用方法,接收结果
                stu = mapper.selectById(id);
    
            } catch (Exception e) {
    
            } finally {
                //6.释放资源
                if(sqlSession != null) {
                    sqlSession.close();
                }
                if(is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            //7.返回结果
            return stu;
        }
    
        @Override
        public Integer insert(Student stu) {
            Integer result = null;
            SqlSession sqlSession = null;
            InputStream is = null;
            try{
                //1.加载核心配置文件
                is = Resources.getResourceAsStream("MyBatisConfig.xml");
    
                //2.获取SqlSession工厂对象
                SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    
                //3.通过工厂对象获取SqlSession对象
                sqlSession = sqlSessionFactory.openSession(true);
    
                //4.获取StudentMapper接口的实现类对象
                StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // StudentMapper mapper = new StudentMapperImpl();
    
                //5.通过实现类对象调用方法,接收结果
                result = mapper.insert(stu);
    
            } catch (Exception e) {
    
            } finally {
                //6.释放资源
                if(sqlSession != null) {
                    sqlSession.close();
                }
                if(is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            //7.返回结果
            return result;
        }
    
        @Override
        public Integer update(Student stu) {
            Integer result = null;
            SqlSession sqlSession = null;
            InputStream is = null;
            try{
                //1.加载核心配置文件
                is = Resources.getResourceAsStream("MyBatisConfig.xml");
    
                //2.获取SqlSession工厂对象
                SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    
                //3.通过工厂对象获取SqlSession对象
                sqlSession = sqlSessionFactory.openSession(true);
    
                //4.获取StudentMapper接口的实现类对象
                StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // StudentMapper mapper = new StudentMapperImpl();
    
                //5.通过实现类对象调用方法,接收结果
                result = mapper.update(stu);
    
            } catch (Exception e) {
    
            } finally {
                //6.释放资源
                if(sqlSession != null) {
                    sqlSession.close();
                }
                if(is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            //7.返回结果
            return result;
        }
    
        @Override
        public Integer delete(Integer id) {
            Integer result = null;
            SqlSession sqlSession = null;
            InputStream is = null;
            try{
                //1.加载核心配置文件
                is = Resources.getResourceAsStream("MyBatisConfig.xml");
    
                //2.获取SqlSession工厂对象
                SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    
                //3.通过工厂对象获取SqlSession对象
                sqlSession = sqlSessionFactory.openSession(true);
    
                //4.获取StudentMapper接口的实现类对象
                StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // StudentMapper mapper = new StudentMapperImpl();
    
                //5.通过实现类对象调用方法,接收结果
                result = mapper.delete(id);
    
            } catch (Exception e) {
    
            } finally {
                //6.释放资源
                if(sqlSession != null) {
                    sqlSession.close();
                }
                if(is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            //7.返回结果
            return result;
        }
    }
    
    
  • 最后测试:通过StudentController的测试类来直接测试,能够测试通过即可
1.3 源码分析
  • 分析动态代理对象如何生成的?

    • 通过动态代理开发模式,我们只编写一个接口,不写实现类,我们通过 getMapper() 方法最终获取到 org.apache.ibatis.binding.MapperProxy 代理对象,然后执行功能,

    • 而这个代理对象正是 MyBatis 使用了 JDK 的动态代理技术,帮助我们生成了代理实现类对象。从而可以进行相关持久化操作。

    • 跟踪getMapper方法,最终可以跟踪到如下代码:这个代码就是我们之前自己实现动态代理的代码:
      在这里插入图片描述

  • 分析方法是如何执行的?

    • 分析过程:
      • 跟踪mapper.insert方法,代码打断点,然后调试跟踪

      • 代码执行到insert之后,进入方法的执行,然后就进入到了invoke方法(动态代理执行方法,都会执行invoke)

      • 在invoke中调用了mapperMethod.execute()方法

      • 这个方法中通过 switch 语句根据操作类型来判断是新增、修改、删除、查询操作,

      • 最后一步回到了 MyBatis 最原生的 SqlSession 方式来执行增删改查
        在这里插入图片描述

1.4 @Param
  1. 给映射文件的sql传递多个参数,除了封装对象,还可以通过注解Param来实现

  2. 代码:StudentMapper增加

    //根据姓名或年龄查询
        public abstract List<Student> selectByNameOrAge(@Param("p1") String name , @Param("p2") Integer age);
    
  3. 配置:

    <select id="selectByNameOrAge" resultType="student" >
        <include refid="select"/> WHERE name = #{p1} OR age = #{p2}
    </select>
    
    
  4. 通过@Param配置的参数名,在映射中就可以通过#{}来获取

1.5 知识小结
  • 接口代理方式可以让我们只编写接口即可,而实现类对象由 MyBatis 生成

  • 实现规则 :

  1. 映射配置文件中的名称空间必须和 Dao 层接口的全类名相同
  2. 映射配置文件中的增删改查标签的 id 属性必须和 Dao 层接口的方法名相同
  3. 映射配置文件中的增删改查标签的 parameterType 属性必须和 Dao 层接口方法的参数相同
  4. 映射配置文件中的增删改查标签的 resultType 属性必须和 Dao 层接口方法的返回值相同
  • 获取动态代理对象
    • 获取动态代理对象 SqlSession 功能类中的 getMapper() 方法

二. 动态sql语句

2.1 动态sql语句概述
  • Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,

  • 有些时候业务逻辑复杂时,我们的 SQL是动态变化的,此时在前面的学习中我们的 SQL 就不能满足要求了。

  • 例如,有时候查询条件是三个,有时候是两个:
    在这里插入图片描述

  • 代码:

    1. StudentMapper.xml增加多条件sql
      在这里插入图片描述

    2. Mapper接口:增加多条件查询方法

      //多条件查询
      public abstract List<Student> selectCondition(Student stu);
      
    3. 测试类:com.itheima.dynamic.Test01

       @Test
          public void selectCondition() throws Exception{
              //1.加载核心配置文件
              InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
      
              //2.获取SqlSession工厂对象
              SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
      
              //3.通过工厂对象获取SqlSession对象
              SqlSession sqlSession = sqlSessionFactory.openSession(true);
      
              //4.获取StudentMapper接口的实现类对象
              StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
      
              Student stu = new Student();
              stu.setId(2);
              stu.setName("李四");
              stu.setAge(24);
      
              //5.调用实现类的方法,接收结果
              List<Student> list = mapper.selectCondition(stu);
      
              //6.处理结果
              for (Student student : list) {
                  System.out.println(student);
              }
      
              //7.释放资源
              sqlSession.close();
              is.close();
          }
      
    4. 3个条件结果
      在这里插入图片描述

    5. 2个条件(将setAge屏蔽掉)结果:如果有一个参数没有传递,默认是null,这肯定查询不到数据
      在这里插入图片描述

      • 那如何解决这种问题呢?我们可能会根据不同需求要利用同一个功能,但是查询条件可能会变化
      • 在映射文件中如何动态配置sql呢?
      • 利用动态sql标签
  • 动态sql标签
    在这里插入图片描述

2.2 动态 SQL 之<**if> *****
  • 我们根据实体类的不同取值,使用不同的 SQL语句来进行查询。

  • 比如在 id如果不为空时可以根据id查询,如果username 不同空时还要加入用户名作为条件。

  • 这种情况在我们的多条件组合查询中经常会碰到。

  • 我们需要动态sql标签where和if。

  • <where>:条件标签,如果有动态条件,则使用该标签代替where关键字

  • <if>:条件判断标签

     <if test="条件判断">
    	 拼接的查询条件
     </if>
    
  • 如下图:

<select id="selectCondition" resultType="student" parameterType="student">
    select * from student
    <where>
        <if test="id != null">
            id = #{id}
        </if>
        <if test="name != null">
            AND name = #{name} <!--如果没有传递id,会不会直接是where and name=xxx,不会,mybatis会做特殊处理-->
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
    </where>
</select>
  • 总结语法:
<where>:条件标签。如果有动态条件,则使用该标签代替 where 关键字。
<if>:条件判断标签。语法如下:
<if test=“条件判断”>
	拼接的查询条件
</if>
2.3 动态 SQL 之<foreach> ***

在这里插入图片描述

  • 接下来我们来写代码:
  • 增加映射配置:
<select id="selectByIds" resultType="student" parameterType="list">
   <include refid="select"/>
   <where>
       <foreach collection="list" open="id IN (" close=")" item="id" separator=",">
           #{id}
       </foreach>
   </where>
</select>
  • 增加接口:
//根据多个id查询
public abstract List<Student> selectByIds(List<Integer> ids);
  • Test01中增加测试方法:
@Test
public void selectByIds() throws Exception{
    //1.加载核心配置文件
    InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");

    //2.获取SqlSession工厂对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

    //3.通过工厂对象获取SqlSession对象
    SqlSession sqlSession = sqlSessionFactory.openSession(true);

    //4.获取StudentMapper接口的实现类对象
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

    List<Integer> ids = new ArrayList<>();
    ids.add(1);
    ids.add(2);
    ids.add(3);

    //5.调用实现类的方法,接收结果
    List<Student> list = mapper.selectByIds(ids);

    //6.处理结果
    for (Student student : list) {
        System.out.println(student);
    }

    //7.释放资源
    sqlSession.close();
    is.close();
}
  • 测试:给ids集合中增加1和2测试,然后再集合中增加3来测试,发现都可以
2.4 SQL片段抽取
  • 我们发现在映射文件中的sql语句,有很多都有重复的部分,那怎么处理呢?

  • 通过sql片段抽取:
    在这里插入图片描述

  • Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的

  • 代码:

  • 映射文件:

<mapper namespace="com.itheima.mapper.StudentMapper">
	<!--抽取-->
    <sql id="select" >SELECT * FROM student</sql>
    <select id="selectAll" resultType="student">
        <!--引用-->
        <include refid="select"/>
    </select>

    <select id="selectById" resultType="student" parameterType="int">
        <!--引用-->
        <include refid="select"/> WHERE id = #{id}
    </select>

    <insert id="insert" parameterType="student">
        INSERT INTO student VALUES (#{id},#{name},#{age})
    </insert>

    <update id="update" parameterType="student">
        UPDATE student SET name = #{name},age = #{age} WHERE id = #{id}
    </update>

    <delete id="delete" parameterType="int">
        DELETE FROM student WHERE id = #{id}
    </delete>

    <select id="selectCondition" resultType="student" parameterType="student">
        <!--引用-->
        <include refid="select"/>
        <where>
            <if test="id != null">
                id = #{id}
            </if>
            <if test="name != null">
                AND name = #{name}
            </if>
            <if test="age != null">
                AND age = #{age}
            </if>
        </where>
    </select>

    <select id="selectByIds" resultType="student" parameterType="list">
        <!--引用-->
        <include refid="select"/>
        <where>
            <foreach collection="list" open="id IN (" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>
</mapper>
2.5 知识小结

在这里插入图片描述

三. 分页插件 ***

3.1 分页插件介绍
  • 常见的分页效果:例如百度:
    在这里插入图片描述
  • 分页可以将很多条结果进行分页显示
  • 如果当前在第一页,则没有上一页。如果当前在最后一页,则没有下一页
  • 需要明确当前是第几页,这一页中显示多少条结果
  • MyBatis分页插件总结
    • 在企业级开发中,分页也是一种常见的技术
    • 而目前使用的 MyBatis 是不带分页功能的,如果想实现分页的 功能,需要我们手动编写 LIMIT 语句
    • 但是不同的数据库实现分页的 SQL 语句也是不同的,所以手写分页 成本较高
    • 这个时候就可以借助分页插件来帮助我们实现分页功能
    • PageHelper:第三方分页助手。将复杂的分页操作进行封装,从而让分页功能变得非常简单
3.2 分页插件的使用
  • MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据

  • 开发步骤:

  • ①导入与PageHelper的jar包 “02-MyBatis进阶\资料\分页插件jar包”

  • ②在mybatis核心配置文件中配置PageHelper插件

<!-- 注意:分页助手的插件,配置在typeAliases之后。interceptor:拦截器,插件都是以拦截器形式实现的 -->
 <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
  • ③测试:新建com.itheima.paging.Test01
package com.itheima.paging;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.itheima.bean.Student;
import com.itheima.mapper.StudentMapper;
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.InputStream;
import java.util.List;

public class Test01 {
    @Test
    public void selectPaging() throws Exception{
        //1.加载核心配置文件
        InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");

        //2.获取SqlSession工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        //3.通过工厂对象获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        //4.获取StudentMapper接口的实现类对象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        //通过分页助手来实现分页功能 startPage参数1:当前页码,参数2:每页显示几条
        // 第一页:显示3条数据  pageNum,pageSize
        //PageHelper.startPage(1,3);
        // 第二页:显示3条数据
        //PageHelper.startPage(2,3);
        // 第三页:显示3条数据
        PageHelper.startPage(3,3);

        //5.调用实现类的方法,接收结果
        List<Student> list = mapper.selectAll();

        //6.处理结果
        for (Student student : list) {
            System.out.println(student);
        }
        //7.释放资源
        sqlSession.close();
        is.close();
    }
}

3.3 分页插件的参数获取
  • PageInfo:封装分页相关参数的功能类

  • 核心方法:
    在这里插入图片描述

  • 测试:修改Test01代码,在释放资源之前,增加如下代码:

//其他分页的数据
PageInfo<User> pageInfo = new PageInfo<User>(list);
System.out.println("总条数:"+pageInfo.getTotal());
System.out.println("总页数:"+pageInfo.getPages());
System.out.println("当前页:"+pageInfo.getPageNum());
System.out.println("每页显示长度:"+pageInfo.getPageSize());
System.out.println("是否第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否最后一页:"+pageInfo.isIsLastPage());

3.4 分页插件知识小结
  • 分页:可以将很多条结果进行分页显示
  • 分页插件 jar 包: pagehelper-5.1.10.jar 、 jsqlparser-3.1.jar

  • <plugins>:集成插件标签

  • 分页助手相关 API

    • PageHelper:分页助手功能类:
      1. startPage():设置分页参数
    • PageInfo:分页相关参数功能类:
      1. getTotal():获取总条数
      2. getPages():获取总页数
      3. getPageNum():获取当前页
      4. getPageSize():获取每页显示条数
      5. getPrePage():获取上一页
      6. getNextPage():获取下一页
      7. isIsFirstPage():获取是否是第一页
      8. isIsLastPage():获取是否是最后一页

四.MyBatis的多表操作 ***

4.1 多表模型介绍
  • 我们之前学习的都是基于单表操作的,而实际开发中,随着业务难度的加深,肯定需要多表操作的

  • 多表模型分类

    • 一对一:在任意一方建立外键,关联对方的主键
    • 一对多:在多的一方建立外键,关联一的一方的主键
    • 多对多:借助中间表,中间表至少两个字段,分别关联两张表的主键
4.2 多表模型一对一操作
  • 一对一模型: 人和身份证,一个人只有一个身份证
    在这里插入图片描述

  • 代码实现

    1. sql语句准备
    CREATE TABLE person(
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	NAME VARCHAR(20),
    	age INT
    );
    INSERT INTO person VALUES (NULL,'张三',23);
    INSERT INTO person VALUES (NULL,'李四',24);
    INSERT INTO person VALUES (NULL,'王五',25);
    
    CREATE TABLE card(
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	number VARCHAR(30),
    	pid INT,
    	CONSTRAINT cp_fk FOREIGN KEY (pid) REFERENCES person(id)
    );
    INSERT INTO card VALUES (NULL,'12345',1);
    INSERT INTO card VALUES (NULL,'23456',2);
    INSERT INTO card VALUES (NULL,'34567',3);
    
    • 老师在视频中拖出这两张表的关系图如下:
      在这里插入图片描述

    • 看到的怎么是一对多呢?这是因为外键pid并没有设置唯一约束,如果外键不是唯一,那就是允许重复,如果外键可以重复,那就是一对多

    • 所以如果想看到1:1的效果,那就需要给pid增加一个约束unique

    1. 创建项目mybatis03
      在这里插入图片描述
    • jdbc.properties

      driver=com.mysql.jdbc.Driver
      url=jdbc:mysql://192.168.59.143:3306/db2
      username=root
      password=itheima
      
    • MyBatisConfig.xml

      <?xml version="1.0" encoding="UTF-8" ?>
      <!--MyBatis的DTD约束-->
      <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
      
      <!--configuration 核心根标签-->
      <configuration>
      
          <!--引入数据库连接的配置文件-->
          <properties resource="jdbc.properties"/>
      
          <!--配置LOG4J-->
          <settings>
              <setting name="logImpl" value="log4j"/>
          </settings>
      
          <!--起别名-->
          <typeAliases>
              <package name="com.itheima.bean"/>
          </typeAliases>
      
          <!--environments配置数据库环境,环境可以有多个。default属性指定使用的是哪个-->
          <environments default="mysql">
              <!--environment配置数据库环境  id属性唯一标识-->
              <environment id="mysql">
                  <!-- transactionManager事务管理。  type属性,采用JDBC默认的事务-->
                  <transactionManager type="JDBC"></transactionManager>
                  <!-- dataSource数据源信息   type属性 连接池-->
                  <dataSource type="POOLED">
                      <!-- property获取数据库连接的配置信息 -->
                      <property name="driver" value="${driver}" />
                      <property name="url" value="${url}" />
                      <property name="username" value="${username}" />
                      <property name="password" value="${password}" />
                  </dataSource>
              </environment>
          </environments>
      
          <!-- mappers引入映射配置文件 -->
          <mappers>
          </mappers>
      </configuration>
      
    • log4j.properties

      # Global logging configuration
      # ERROR WARN INFO DEBUG
      log4j.rootLogger=DEBUG, stdout
      # Console output...
      log4j.appender.stdout=org.apache.log4j.ConsoleAppender
      log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
      log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
      
    1. javabean:card
    package com.itheima.bean;
    
    public class Card {
        private Integer id;     //主键id
        private String number;  //身份证号
    
        private Person p;       //所属人的对象 *** 在表中是外键pid:人id,但是在javabean中一般都是外键对应的实体类javabean
    
        public Card() {
        }
    
        public Card(Integer id, String number, Person p) {
            this.id = id;
            this.number = number;
            this.p = p;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getNumber() {
            return number;
        }
    
        public void setNumber(String number) {
            this.number = number;
        }
    
        public Person getP() {
            return p;
        }
    
        public void setP(Person p) {
            this.p = p;
        }
    
        @Override
        public String toString() {
            return "Card{" +
                    "id=" + id +
                    ", number='" + number + '\'' +
                    ", p=" + p +
                    '}';
        }
    }
    
    1. javabean:person
    package com.itheima.bean;
    
    public class Person {
        private Integer id;     //主键id
        private String name;    //人的姓名
        private Integer age;    //人的年龄
    
        public Person() {
        }
    
        public Person(Integer id, String name, Integer age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    1. 配置文件:新建com.itheima.one_to_one.OneToOneMapper.xml
    <?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.table01.OneToOneMapper">
        <!--配置字段和实体对象属性的映射关系
    	type指定要配置的实体对象是哪个:card
    	-->
        <resultMap id="oneToOne" type="card">
            <!--id配置主键列:将sql中查询的cid列与card实体类中的id进行关联 -->
            <id column="cid" property="id" />
            <!--result配置非主键列-->
            <result column="number" property="number" />
            <!--
                association:配置被包含对象的映射关系
                property:被包含对象的变量名(属性名,在Card实体类中有一个p属性)
                javaType:被包含对象的数据类型(直接给person别名,因为我们通过typeAliases统一设置了bean包下所有实体类的别名了)
            -->
            <association property="p" javaType="person">
                <id column="pid" property="id" />
                <result column="name" property="name" />
                <result column="age" property="age" />
            </association>
        </resultMap>
    	<!--resultMap属性指定的是resultMap标签的id
    	查询出来的结果涉及到两张表,所以不能使用resultType了
    	-->
        <select id="selectAll" resultMap="oneToOne">
            SELECT c.id cid,number,pid,NAME,age FROM card c,person p WHERE c.pid=p.id
        </select>
    </mapper>
    
    • 如何确定谁包含了谁?哪个是被包含对象?
    • 通过javabean的关联属性:
    • 在Card表中有一个关联属性P,那么Card就包含了Person
    1. MyBatisConfig.xml中配置映射文件:
    <mappers>
        <mapper resource="com/itheima/one_to_one/OneToOneMapper.xml"/>
    </mappers>
    
    1. 接口com.itheima.table01.OneToOneMapper
    package com.itheima.table01;
    
    import com.itheima.bean.Card;
    
    import java.util.List;
    
    public interface OneToOneMapper {
        //查询全部
        public abstract List<Card> selectAll();
    }
    
    
    1. 测试类:com.itheima.table01.Test01;
    package com.itheima.table01;
    
    import com.itheima.bean.Card;
    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.InputStream;
    import java.util.List;
    
    public class Test01 {
        @Test
        public void selectAll() throws Exception{
            //1.加载核心配置文件
            InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
    
            //2.获取SqlSession工厂对象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    
            //3.通过工厂对象获取SqlSession对象
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
    
            //4.获取OneToOneMapper接口的实现类对象
            OneToOneMapper mapper = sqlSession.getMapper(OneToOneMapper.class);
    
            //5.调用实现类的方法,接收结果
            List<Card> list = mapper.selectAll();
    
            //6.处理结果
            for (Card c : list) {
                System.out.println(c);
            }
    
            //7.释放资源
            sqlSession.close();
            is.close();
        }
    }
    
    1. 结果:
      在这里插入图片描述
  • 一对一配置总结:

<resultMap>:配置字段和对象属性的映射关系标签。
    id 属性:唯一标识
    type 属性:实体对象类型
<id>:配置主键映射关系标签。
<result>:配置非主键映射关系标签。
    column 属性:表中字段名称
    property 属性: 实体对象变量名称
<association>:配置被包含对象的映射关系标签。
    property 属性:被包含对象的变量名
    javaType 属性:被包含对象的数据类型
  • 分析图
    在这里插入图片描述
4.3 多表模型一对多操作
  • 一对多模型: 一对多模型:班级和学生,一个班级可以有多个学生
    在这里插入图片描述

  • 代码实现

    1. sql语句准备
    CREATE TABLE classes(
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	NAME VARCHAR(20)
    );
    INSERT INTO classes VALUES (NULL,'黑马一班');
    INSERT INTO classes VALUES (NULL,'黑马二班');
    
    
    CREATE TABLE student(
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	NAME VARCHAR(30),
    	age INT,
    	cid INT,
    	CONSTRAINT cs_fk FOREIGN KEY (cid) REFERENCES classes(id)
    );
    INSERT INTO student VALUES (NULL,'张三',23,1);
    INSERT INTO student VALUES (NULL,'李四',24,1);
    INSERT INTO student VALUES (NULL,'王五',25,2);
    INSERT INTO student VALUES (NULL,'赵六',26,2);
    
    1. 新建javabean:classes
    package com.itheima.bean;
    
    import java.util.List;
    
    public class Classes {
        private Integer id;     //主键id
        private String name;    //班级名称
    
        private List<Student> students; //班级中所有学生对象*** 一个班级里多个学生
    
        public Classes() {
        }
    
        public Classes(Integer id, String name, List<Student> students) {
            this.id = id;
            this.name = name;
            this.students = students;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public List<Student> getStudents() {
            return students;
        }
    
        public void setStudents(List<Student> students) {
            this.students = students;
        }
    
        @Override
        public String toString() {
            return "Classes{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", students=" + students +
                    '}';
        }
    }
    
    
    1. javabean:student
    package com.itheima.bean;
    
    import java.util.List;
    
    public class Student {
        private Integer id;     //主键id
        private String name;    //学生姓名
        private Integer age;    //学生年龄
    
        public Student() {
        }
    
        public Student(Integer id, String name, Integer age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    
    1. 配置文件:com.itheima.one_to_many.OneToManyMapper.xml
    <?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.table02.OneToManyMapper">
        <resultMap id="oneToMany" type="classes">
            <id column="cid" property="id"/>
            <result column="cname" property="name"/>
    
            <!--
                collection:配置被包含的集合对象映射关系(如果被包含的对象是多的一方,需要用collection)
                property:被包含对象的变量名
                ofType:被包含对象的实际数据类型(collection标签中指定被包含对象类型用的是ofType)
            -->
            <collection property="students" ofType="student">
                <id column="sid" property="id"/>
                <result column="sname" property="name"/>
                <result column="sage" property="age"/>
            </collection>
        </resultMap>
        <select id="selectAll" resultMap="oneToMany">
            SELECT c.id cid,c.name cname,s.id sid,s.name sname,s.age sage FROM classes c,student s WHERE c.id=s.cid
        </select>
    </mapper>
    
    1. 配置映射文件
     <mappers>
         <mapper resource="com/itheima/one_to_one/OneToOneMapper.xml"/>
         <mapper resource="com/itheima/one_to_many/OneToManyMapper.xml"/>
    </mappers>
    
    1. 接口
    package com.itheima.table02;
    
    import com.itheima.bean.Classes;
    
    import java.util.List;
    
    public interface OneToManyMapper {
        //查询全部
        public abstract List<Classes> selectAll();
    }
    
    
    1. 测试类com.itheima.table02.Test01
    package com.itheima.table02;
    
    import com.itheima.bean.Classes;
    import com.itheima.bean.Student;
    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.InputStream;
    import java.util.List;
    
    public class Test01 {
        @Test
        public void selectAll() throws Exception{
            //1.加载核心配置文件
            InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
    
            //2.获取SqlSession工厂对象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    
            //3.通过工厂对象获取SqlSession对象
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
    
            //4.获取OneToManyMapper接口的实现类对象
            OneToManyMapper mapper = sqlSession.getMapper(OneToManyMapper.class);
    
            //5.调用实现类的方法,接收结果
            List<Classes> classes = mapper.selectAll();
    
            //6.处理结果
            for (Classes cls : classes) {
                System.out.println(cls.getId() + "," + cls.getName());
                List<Student> students = cls.getStudents();
                for (Student student : students) {
                    System.out.println("\t" + student);
                }
            }
    
            //7.释放资源
            sqlSession.close();
            is.close();
        }
    }
    
    1. 结果:
      在这里插入图片描述
  • 一对多配置文件总结:

<resultMap>:配置字段和对象属性的映射关系标签。
    id 属性:唯一标识
    type 属性:实体对象类型
<id>:配置主键映射关系标签。
<result>:配置非主键映射关系标签。
    column 属性:表中字段名称
    property 属性: 实体对象变量名称
<collection>:配置被包含集合对象的映射关系标签。
    property 属性:被包含集合对象的变量名
    ofType 属性:集合中保存的对象数据类型
  • 分析图:
    在这里插入图片描述
4.4 多表模型多对多操作
  • 多对多模型:学生和课程,一个学生可以选择多门课程、一个课程也可以被多个学生所选择
    在这里插入图片描述

  • 代码实现

    1. sql语句准备
    CREATE TABLE course(
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	NAME VARCHAR(20)
    );
    INSERT INTO course VALUES (NULL,'语文');
    INSERT INTO course VALUES (NULL,'数学');
    
    
    CREATE TABLE stu_cr(
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	sid INT,
    	cid INT,
    	CONSTRAINT sc_fk1 FOREIGN KEY (sid) REFERENCES student(id),
    	CONSTRAINT sc_fk2 FOREIGN KEY (cid) REFERENCES course(id)
    );
    INSERT INTO stu_cr VALUES (NULL,1,1);
    INSERT INTO stu_cr VALUES (NULL,1,2);
    INSERT INTO stu_cr VALUES (NULL,2,1);
    INSERT INTO stu_cr VALUES (NULL,2,2);
    
    1. Javabean
    package com.itheima.bean;
    
    public class Course {
        private Integer id;     //主键id
        private String name;    //课程名称
    
        public Course() {
        }
    
        public Course(Integer id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Course{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
    1. 修改:Student

       private List<Course> courses;   // 学生所选择的课程集合
        public Student(Integer id, String name, Integer age, List<Course> courses) {
              this.id = id;
              this.name = name;
              this.age = age;
              this.courses = courses;
          }
           public List<Course> getCourses() {
              return courses;
          }
      
          public void setCourses(List<Course> courses) {
              this.courses = courses;
          }
      
    2. 我们这里只是多表查询,不做其他操作,所以中间表不需要对应的实体类

    3. 配置文件

    <?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.table03.ManyToManyMapper">
        <resultMap id="manyToMany" type="student">
            <id column="sid" property="id"/>
            <result column="sname" property="name"/>
            <result column="sage" property="age"/>
    
            <collection property="courses" ofType="course">
                <id column="cid" property="id"/>
                <result column="cname" property="name"/>
            </collection>
        </resultMap>
        <select id="selectAll" resultMap="manyToMany">
            SELECT sc.sid,s.name sname,s.age sage,sc.cid,c.name cname FROM student s,course c,stu_cr sc WHERE sc.sid=s.id AND sc.cid=c.id
        </select>
        <!--注意这里查询的是sc.cid,将这个cid给course的id了,这也是可以的,因为都是课程id
    		但其实sql语句中的sc.cid,可以换成c.id,这样的话给course的id更合适	
    	-->
    </mapper>
    
    1. 配置
     <mappers>
            <mapper resource="com/itheima/one_to_one/OneToOneMapper.xml"/>
            <mapper resource="com/itheima/one_to_many/OneToManyMapper.xml"/>
            <mapper resource="com/itheima/many_to_many/ManyToManyMapper.xml"/>
        </mappers>
    
    1. 接口
    package com.itheima.table03;
    
    import com.itheima.bean.Student;
    
    import java.util.List;
    
    public interface ManyToManyMapper {
        //查询全部
        public abstract List<Student> selectAll();
    }
    
    
    1. 测试类
    package com.itheima.table03;
    
    import com.itheima.bean.Course;
    import com.itheima.bean.Student;
    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.InputStream;
    import java.util.List;
    
    public class Test01 {
        @Test
        public void selectAll() throws Exception{
            //1.加载核心配置文件
            InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
    
            //2.获取SqlSession工厂对象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    
            //3.通过工厂对象获取SqlSession对象
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
    
            //4.获取ManyToManyMapper接口的实现类对象
            ManyToManyMapper mapper = sqlSession.getMapper(ManyToManyMapper.class);
    
            //5.调用实现类的方法,接收结果
            List<Student> students = mapper.selectAll();
    
            //6.处理结果
            for (Student student : students) {
                System.out.println(student.getId() + "," + student.getName() + "," + student.getAge());
                List<Course> courses = student.getCourses();
                for (Course cours : courses) {
                    System.out.println("\t" + cours);
                }
            }
    
            //7.释放资源
            sqlSession.close();
            is.close();
        }
    }
    
    1. 结果
      在这里插入图片描述
  • 多对多配置文件总结:

<resultMap>:配置字段和对象属性的映射关系标签。
	id 属性:唯一标识
	type 属性:实体对象类型
 <id>:配置主键映射关系标签。
 <result>:配置非主键映射关系标签。
	column 属性:表中字段名称
	property 属性: 实体对象变量名称
<collection>:配置被包含集合对象的映射关系标签。
	property 属性:被包含集合对象的变量名
	ofType 属性:集合中保存的对象数据类型
  • 分析图
    在这里插入图片描述
4.5 多表模型操作总结

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

InLoadwetrust

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值