Mybatis入门操作

在这里插入图片描述


一、Mybatis入门

1. jdbc编程问题总结

1.1 jdbc程序

@Override
public void insertUser(User user){
    Connection conn = DBUtil.getConnection();
    PreparedStatement stmt = null;
    String sql = "insert into sys_user(NAME,ACCT,PWD,CRTIME,UPTIME) values(?a,?c,?d,now(),now())";
    try {
        stmt = conn.prepareStatement(sql);
        stmt.setString(1, user.getName());
        stmt.setString(2, user.getAcct());
        stmt.setString(3, user.getPwd());
        stmt.executeUpdate();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        DBUtil.closeAll(conn, stmt, null);
    }
}

1.2 jdbc编程步骤

  1. 加载数据库驱动
  2. 创建并获取数据库链接
  3. 创建PreparedStatement对象
  4. 设置Sql语句中的占位符参数
  5. 通过PreparedStatement执行Sql并获取结果
  6. 对Sql执行结果进行解析处理
  7. 释放资源(Connection、Preparedstatement、ResultSet)

1.3 jdbc问题总结如下

  1. 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题
  2. Sql语句在代码中硬编码,造成代码不易维护,实际应用Sql变化的可能较大,Sql变动需要改变java代码
  3. 使用PreparedStatement向占位符号传参数存在硬编码,因为Sql语句的where条件不一定,可能多也可能少,修改Sql还要修改代码,系统不易维护
  4. 对结果集解析存在硬编码(查询列名),Sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便

2. MyBatis介绍

  MyBatis本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis,实质上MyBatis对iBatis进行一些改进
  MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建Connection、创建Statement、手动设置参数、结果集检索等jdbc繁杂的过程代码
  Mybatis通过xml或注解的方式将要执行的各种Statement(Statement、PreparedStatemnt、CallableStatement)配置起来,并通过java对象和Statement中的Sql进行映射生成最终执行的Sql语句,最后由MyBatis框架执行Sql并将结果映射成java对象并返回

3. MyBatis架构

images
  1. MyBatis配置文件mybatis-config.xml(名称不固定),此文件作为MyBatis的全局配置文件,配置了MyBatis的运行环境等信息。mapper.xml文件即Sql映射文件,文件中配置了操作数据库的Sql语句。此文件需要在mybatis-config.xml中加载
  2. 通过MyBatis环境等配置信息构造SqlSessionFactory即会话工厂
  3. 由会话工厂创建SqlSession即会话,操作数据库需要通过SqlSession进行
  4. MyBatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器
  5. Mapped Statement也是MyBatis一个底层封装对象,它包装了MyBatis配置信息及Sql映射信息等。mapper.xml文件中一个Sql对应一个Mapped Statement对象,Sql的id即是Mapped statement的id
  6. Mapped Statement对Sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行Sql前将输入的java对象映射至Sql中,输入参数映射就是jdbc编程中对PreparedStatement设置参数
  7. Mapped Statement对Sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程

4. 搭建MyBatis项目

4.1 建立Java项目

  MyBatis是一个持久层框架,操作数据库时使用的。无需创建JavaWEB项目,建立Java项目即可

4.2 导入MyBatis框架jar包

在这里插入图片描述

  • mybatis-3.4.6.jar为核心jar包,必须引入
  • lib目录下的jar包为工具包,可有可无
  • mysql或oracle数据库的驱动包,必须引入

maven依赖配置

<dependencies>
    <!-- mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.6</version>
    </dependency>
    <!--  mysql -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.38</version>
    </dependency>
    <!-- log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

4.3 编写MyBatis中全局配置文件

  作用:配置了数据源、事务等MyBatis运行环境等

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!-- 配置环境:与数据库的连接信息
	    此配置了解即可,后期与spring框架整合之后,此配置会消失
	 -->
    <environments default="mysql">
        <environment id="oracle">
            <!-- 配置事务:使用jdbc的事务管理 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源:连接数据库的信息
                type : 表示连接是否使用连接池,POOLED表示mybatis中自带的连接池
             -->
            <dataSource type="POOLED">
                <property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
                <property name="url" value="jdbc:oracle:thin:@localhost:1521:orcl"/>
                <property name="username" value="tom"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

4.4 编写实体类

public class Emp {
    private Integer empno;
    private String ename;
    private String job;
    private Integer mgr;
    private Date hiredate;
    private Double sal;
    private Double comm;
    //get,set方法省略...
}

4.5 编写映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
    namespace : 命名空间,作用是mapper文件进行分类管理
    注意:如果使用mapper代理的方式进行开发,namespace有特殊的作用
-->
<mapper namespace="emp">
    <!-- 在映射文件中编写sql语句 -->
    <!-- 1.通过主键查询员工信息 -->
    <!--
        通过<select>标签编写查询语句
        id : 映射文件中SQL语句的唯一标识
             mybatis会将SQL语句封装到Mapped Statement对象中,所以此处的id也可以标识Mapped Statement对象的id
             注意:同一个mapper文件中id不能重复,而且id在mapper代理模式下有着重要作用
        parameterType : 输入参数的类型
            sql语句的占位符:#{}
            #{empno}:其中empno表示接收输入的参数值,参数名称为empno
                但是如果参数的类型为简单类型(基本数据类型+包装类+字符串类型),参数名称可以任意指定
       resultType : 输出参数的类型
                    需要指定输出数据为Java中的数据类型(实体类的全限定名)
    -->
    <select id="findEmpById" parameterType="java.lang.Integer" resultType="com.mybatis.entity.Emp">
      select empno,ename,job,mgr,hiredate,sal,comm from emp where empno=#{abc}
    </select>
</mapper>

4.6 加载映射文件

  在MyBatis中全局配置文件中添加映射文件位置

<!-- 加载映射文件的位置 -->
<mappers>
    <mapper resource="com/mybatis/entity/Emp.xml"/>
</mappers>

4.7 编写测试程序

//1.创建读取全局配置文件的流
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

//2.通过配置文件流创建会话工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

//3.通过会话工厂创建会话对象(SqlSession)
SqlSession session = factory.openSession();

/*
4.通过会话对象操作数据库
selectOne(String statementId, Object param)查询单条数据
参数1:映射文件中的statementId,命名空间名.statementId
参数2:向sql语句中传入的数据,注意:传入的数据类型必须与映射文件中配置的parameterType保持一致
返回值:就是映射文件中配置的resultType的类型
*/
Emp emp = session.selectOne("emp.findEmpById",8001);
System.out.println(emp);
//5.关闭资源
session.close();

5. 增删改查的基本操作

5.1 查询操作

mapper文件:

    <!--
        查询到数据返回多条记录,每一条封装在一个实体类对象中,所有的实体类对象封装在List集合中
        resultType :指定的并不是集合的类型,而是单条数据所对应实体类类型
        resultType="java.util.List" 错误的配置方式
    -->
    <select id="findEmp" resultType="com.mybatis.entity.Emp">
      select empno,ename,job,mgr,hiredate,sal,comm from emp order by empno desc
    </select>

Java代码:

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
List<Emp> list = session.selectList("emp.findEmp");
for (Emp e : list) {
    System.out.println(e);
}
session.close();

5.2 新增操作

mapper文件:

<insert id="insertEmp" parameterType="com.mybatis.entity.Emp" >
    insert into emp(empno,ename,job,mgr,hiredate,sal,comm)
    values(#{empno},#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm})
</insert>

Java代码:

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();

Emp emp = new Emp();
emp.setEname("tom");
emp.setJob("clrek");
emp.setMgr(1);
emp.setHiredate(new Date());
emp.setSal(6500.0);
emp.setComm(1200.0);

//在mybaits中事务默认需要手动提交
int result = session.insert("emp.insertEmp",emp);
System.out.println(result);
//执行提交的方法
session.commit();
session.close();

插入数据的主键返回:

  • select last_insert_id(),表示得到刚insert进去记录的主键值,适用与自增主键的数据库
  • select seq_demo.nextval from dual,表示获取下一个序列生成的值,适用于存在序列的数据库
  • keyProperty:将查询到主键值设置到parameterType指定的对象的哪个属性
  • order:selectKey标签中Sql语句,相对于insert语句来说的执行顺序
  • resultType:指定selectKey标签中Sql语句的结果类型
<!--mysql-->
<selectKey resultType="java.lang.Integer" keyProperty="empno" order="AFTER">
    select last_insert_id()
</selectKey>
<!--oracle-->
<selectKey resultType="java.lang.Integer" keyProperty="empno" order="BEFORE">
    select seq_demo.nextval from dual
</selectKey>

如果是主键自增型数据库还可以使用:

<insert id="insertEmp" parameterType="com.mybatis.entity.Emp" keyProperty="empno" useGeneratedKeys="true">

5.3 修改操作

mapper文件:

<update id="updateEmp" parameterType="com.mybatis.entity.Emp">
    update emp set ename=#{ename},job=#{job},mgr=#{mgr},hiredate=#{hiredate},sal=#{sal},comm=#{comm}
    where empno=#{empno}
</update>

Java代码:

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();

Emp emp = new Emp();
emp.setEmpno(52);
emp.setEname("jerry");
emp.setJob("manager");
emp.setMgr(10);
emp.setHiredate(new Date());
emp.setSal(8500.0);
emp.setComm(1200.0);

int result = session.update("emp.updateEmp",emp);
System.out.println(result);
//执行提交的方法
session.commit();
session.close();

5.4 删除操作

mapper文件:

<delete id="deleteEmp" parameterType="java.lang.Integer">
    delete from emp where empno=#{empno}
</delete>

Java代码:

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();

int result = session.delete("emp.deleteEmp",8000);
System.out.println(result);
//执行提交的方法
session.commit();
session.close();

5.5 条件查询

mapper文件:

<!--
    #{}占位符,需要在Java中将传入数据的前后拼接%符号
    ${}Sql语句拼接,在其中只能写value
-->
<select id="findEmpByEname" parameterType="java.lang.String" resultType="com.mybatis.entity.Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    where ename like #{abc}
</select>

<select id="findEmpByEname2" parameterType="java.lang.String" resultType="com.mybatis.entity.Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    where ename like '%${value}%'
</select>

Java代码:

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();

List<Emp> list = session.selectList("emp.findEmpByEname", "%tom%");
//List<Emp> list = session.selectList("emp.findEmpByEname", "tom");
for (Emp e : list) {
    System.out.println(e);
}

session.close();

5.6 总结

5.6.1 parameterType和resultType
  • parameterType:指定输入参数类型,MyBatis通过ognl从输入对象中获取参数值设置在Sql中
  • resultType:指定输出结果类型,MyBatis将Sql查询结果的一行记录数据映射为resultType指定类型的对象
5.6.2 #{} 和 ${}
  • #{}表示一个占位符号,#{}接收输入参数,类型可以是简单类型、pojo、HashMap
    #{}接收简单类型,#{}中可以写成value或其它名称
    #{}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性…的方式获取对象属性值

  • ${}表示一个拼接符号,会引用Sql注入,所以不建议使用${}
    ${}接收输入参数,类型可以是简单类型、pojo、HashMap
    ${}接收简单类型,${}中只能写成value
    ${}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性…的方式获取对象属性值

5.6.3 selectOne和selectList
  • selectOne表示查询出一条记录进行映射
    如果使用selectOne可以实现使用selectList也可以实现(list中只有一个对象)
  • selectList表示查询出一个列表(多条记录)进行映射
    如果使用selectList查询多条记录,不能使用selectOne
    如果使用selectOne报错:
    org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4

6. MyBatis和Hibernate本质区别和应用场景

  • Hibernate:是一个标准ORM框架(对象关系映射)
    入门门槛较高的,不需要程序写Sql,Sql语句自动生成,对sql语句进行优化、修改比较困难的
    应用场景:适用与需求变化不多的中小型项目,比如:后台管理系统,erp、crm、oa…
  • MyBatis:专注是Sql本身,需要程序员自己编写Sql语句,Sql修改、优化比较方便
    MyBatis是一个不完全的ORM框架,虽然程序员自己写Sql,MyBatis也可以实现映射(输入映射、输出映射)
    应用场景:适用与需求变化较多的项目,比如:互联网项目
  • 企业进行技术选型,以低成本高回报作为技术选型的原则,根据项目组的技术力量进行选择

二、MyBatis开发DAO

1. MyBatis API

1.1 SqlSessionFactoryBuilder

  SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFactoryBuilder了,因为SqlSession是通过SqlSessionFactory生产,所以可以将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量

1.2 SqlSessionFactory

  SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory

1.3 SqlSession

  SqlSession是一个面向用户(程序员)的接口,其中提供了很多操作数据库的方法。如:selectOne(返回单个对象)、selectList(返回单个或多个对象)、insert、update、delete。

  SqlSession的实例不能共享使用,它是线程不安全的,每个线程都应该有它自己的SqlSession实例,因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。

2. MyBatis工具类

  为了简化MyBatis的开发,可将MyBatis进一步封装

public class MybatisUtil {
    /**
     * 不让用户在外界创建工具类对象
     */
    private MybatisUtil(){}
    /**
     * 初始化工厂
     */
    private static SqlSessionFactory factory;
    static {
        try {
            InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
            factory = new SqlSessionFactoryBuilder().build(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取SqlSession对象的方法
     */
    public static SqlSession openSession(){
        return factory.openSession();
    }
}

3. 原始DAO开发方式

  原始Dao开发方法需要程序员编写Dao接口和Dao实现类

接口:

public interface DeptDao {
    public List<Dept> findDept();
    public Dept findDeptById(Integer deptno);
    public void insertDept(Dept dept);
    public void updateDept(Dept dept);
    public void deleteDept(Integer deptno);
}

实现类:

public class DeptDaoImpl implements DeptDao {
    @Override
    public List<Dept> findDept() {
        SqlSession session = MybatisUtil.openSession();
        List<Dept> list = session.selectList("dept.findDept");
        session.close();
        return  list;
    }
    @Override
    public Dept findDeptById(Integer deptno) {
        SqlSession session = MybatisUtil.openSession();
        Dept dept = session.selectOne("dept.findDeptById",deptno);
        session.close();
        return dept;
    }
    @Override
    public void insertDept(Dept dept) {
        SqlSession session = MybatisUtil.openSession();
        session.insert("dept.insertDept",dept);
        session.commit();
        session.close();
    }
    @Override
    public void updateDept(Dept dept) {
        SqlSession session = MybatisUtil.openSession();
        session.update("dept.updateDept",dept);
        session.commit();
        session.close();
    }
    @Override
    public void deleteDept(Integer deptno) {
        SqlSession session = MybatisUtil.openSession();
        session.delete("dept.deleteDept",deptno);
        session.commit();
        session.close();
    }
}

原始DAO开发问题

  • dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作量
  • 调用SqlSession方法时将Statement的id硬编码了
  • 调用SqlSession方法时传入的变量,由于SqlSession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发

4. Mapper代理方式

  Mapper代理开发方式只需要程序员编写Mapper接口(相当于Dao接口),由MyBatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法
  程序员编写Mapper接口需要遵循一些开发规范,MyBatis可以自动生成Mapper接口实现类代理对象

开发规范:

  1. 在mapper.xml中namespace等于Mapper接口的全限定名
  2. Mapper接口中的方法名和mapper.xml中statement的id一致
  3. Mapper接口中的方法输入参数类型和mapper.xml中parameterType指定的类型一致
  4. Mapper接口中的方法返回值类型和mapper.xml中resultType指定的类型一致

Mapper接口:

public interface EmpMapper {
    public List<Emp> findEmp();
    public Emp findEmpByEmpno(Integer empno);
    /**
     * 增删改方法的返回值
     * 1.void 无返回值
     * 2.int 返回操作数据库表的记录数
     * 3.boolean 根据返回数据库表影响的记录数,来判断是否成功
     *          注意:影响的记录数大于等于1,返回值为true,否则为false
     */
    public void insertEmp(Emp emp);
    public void updateEmp(Emp emp);
    public void deleteEmp(Integer empno);
}

映射文件:

<!--namespace : 与Mapper接口的全限定名保持一致-->
<mapper namespace="com.mybatis.mapper.EmpMapper">
    <!--
        statementId与Mapper接口的方法名称保持一致
        parameterType的类型必须与方法的参数类型保持一致
        resultType的类型必须与方法的返回值类型保持一致
    -->
    <select id="findEmpByEmpno" parameterType="java.lang.Integer" resultType="com.mybatis.entity.Emp">
      select empno,ename,job,mgr,hiredate,sal,comm from emp where empno=#{abc}
    </select>

    <!-- 2.查询所有员工 -->
    <select id="findEmp" resultType="com.mybatis.entity.Emp">
      select empno,ename,job,mgr,hiredate,sal,comm from emp order by empno desc
    </select>

    <!-- 4.新增员工 -->
    <insert id="insertEmp" parameterType="com.mybatis.entity.Emp" >
      insert into emp(ename,job,mgr,hiredate,sal,comm)
      values(#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm})
    </insert>
    <!-- 5. 修改员工-->
    <update id="updateEmp" parameterType="com.mybatis.entity.Emp">
        update emp set ename=#{ename},job=#{job},mgr=#{mgr},hiredate=#{hiredate},
        sal=#{sal},comm=#{comm} where empno=#{empno}
    </update>

    <!-- 6. 删除员工-->
    <delete id="deleteEmp" parameterType="java.lang.Integer">
       delete from emp where empno=#{empno}
    </delete>
</mapper>

测试:

SqlSession session = MybatisUtil.openSession();
//通过SqlSession对象获得到你指定Mapper接口的代理对象(就是接口的实现类对象)
EmpMapper mapper = session.getMapper(EmpMapper.class);

三、mybatis-config.xml

  MyBatis的全局配置文件mybatis-config.xml,配置内容如下:

  • properties(属性)
  • settings(全局配置参数)
  • typeAliases(类型别名)
  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins(插件)
  • environments(环境集合属性对象)
    • environment(环境子属性对象)
      • transactionManager(事务管理)
      • dataSource(数据源)
  • mappers(映射器)

1. properties(属性)

  将数据库连接参数单独配置在db.properties中,只需要在mybatis-config.xml中加载db.properties的属性值。在mybatis-config.xml中就不需要对数据库连接参数硬编码

mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/mybatis
mysql.username=root
mysql.password=123456

在mybatis-config.xml加载属性文件:

<!-- 加载外部资源文件 -->
<properties resource="db.properties">
    <!--properties中还可以配置一些属性-->
    <!--<property name="mysql.url" value="jdbc:mysql://localhost:3306/mybatis"/>-->
</properties>

注意: MyBatis将按照下面的顺序来加载属性:

  1. 在properties标签体内定义的属性首先被读取
  2. 然后会读取properties标签中resource或url加载的属性,它会覆盖已读取的同名属性
  3. 最后读取parameterType传递的属性,它会覆盖已读取的同名属性

建议:

  1. 不要在properties标签体内添加任何属性值,只将属性值定义在properties文件中
  2. 在properties文件中定义属性名要有一定的特殊性,如:XXXXX.XXXXX.XXXX

2. settings(全局配置参数)

  MyBatis框架在运行时可以调整一些运行参数
  比如:开启二级缓存、开启延迟加载
  可配置参数如下:

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

3. typeAliases(类型别名)

  在mapper.xml中,定义很多的statement,statement需要parameterType指定输入参数的类型、需要resultType指定输出结果的映射类型。如果在指定类型时输入类型全路径,不方便进行开发,可以针对parameterType或resultType指定的类型定义一些别名,在mapper.xml中通过别名定义,方便开发

3.1 MyBatis默认支持别名

别名映射的类型别名映射的类型
_bytebytebytejava.lang.Byte
_shortshortshortjava.lang.Short
_intintintjava.lang.Integer
_integerintintegerjava.lang.Integer
_longlonglongjava.lang.Long
_floatfloatfloatjava.lang.Float
_doubledoubledoublejava.lang.Double
_booleanbooleanbooleanjava.lang.Boolean
stringjava.lang.Stringdatejava.util.Date
mapjava.util.Maphashmapjava.util.HashMap
listjava.util.Listarraylistjava.util.ArrayList
objectjava.lang.Object

3.2 自定义别名

单个定义别名:

<typeAliases>
    <!--
        <typeAlias>单个别名配置
        type : Java中类型的全限定名
        alias : 自定义别名
    -->
    <typeAlias type="com.mybatis.entity.Dept" alias="abc"/>
</typeAliases>

<!--在resultType使用的abc为自定义类型别名-->
<select id="findDeptById" parameterType="int" resultType="abc">
    select deptno,dname,loc from dept where deptno=#{deptno}
</select>

批量定义别名:

<typeAliases>
    <!--
        <package>批量别名配置
        name : 需要配置别名的实体类所在包的包名
        默认别名为该类的类名,其首字母大小均可
    -->
    <package name="com.mybatis.entity"/>
</typeAliases>

4. typeHandlers(类型处理器)

  MyBatis中通过typeHandlers完成jdbc类型和Java类型的转换,MyBatis自带的类型处理器基本上满足日常需求,不需要单独定义

MyBatis支持类型处理器:

类型处理器Java类型JDBC类型
BooleanTypeHandlerBoolean,boolean任何兼容的布尔值
ByteTypeHandlerByte,byte任何兼容的数字或字节类型
ShortTypeHandlerShort,short任何兼容的数字或短整型
IntegerTypeHandlerInteger,int任何兼容的数字和整型
LongTypeHandlerLong,long任何兼容的数字或长整型
FloatTypeHandlerFloat,float任何兼容的数字或单精度浮点型
DoubleTypeHandlerDouble,double任何兼容的数字或双精度浮点型
BigDecimalTypeHandlerBigDecimal任何兼容的数字或十进制小数类型
StringTypeHandlerStringCHAR和VARCHAR类型
ClobTypeHandlerStringCLOB和LONGVARCHAR类型
NStringTypeHandlerStringNVARCHAR和NCHAR类型
NClobTypeHandlerStringNCLOB类型
ByteArrayTypeHandlerbyte[]任何兼容的字节流类型
BlobTypeHandlerbyte[]BLOB和LONGVARBINARY类型
DateTypeHandlerjava.util.DateTIMESTAMP类型
DateOnlyTypeHandlerjava.util.DateDATE类型
TimeOnlyTypeHandlerjava.util.DateTIME类型
SqlTimestampTypeHandlerjava.sql.TimestampTIMESTAMP类型
SqlDateTypeHandlerjava.sql.DateDATE类型
SqlTimeTypeHandlerjava.sql.TimeTIME类型
ObjectTypeHandler任意其他或未指定类型
EnumTypeHandlerEnumeration类型VARCHAR-任何兼容的字符串类型,作为代码存储(而不是索引)

5. mappers(映射器)

5.1 单个加载映射文件

通过resource加载单个映射文件

<mappers>
    <!--
        <mapper>单个加载映射文件
        resource : 通过相对路径加载文件(项目源目录下的文件)
        url : 通过绝对路径加载文件(文件系统中文件)
    -->
    <mapper resource="com/mybatis/mapper/DeptMapper.xml"/>
    <mapper resource="com/mybatis/mapper/EmpMapper.xml"/>
</mappers>

通过mapper接口加载单个映射文件

  • 前提:使用的是mapper代理方法
  • 遵循规范:需要将Mapper接口类名和mapper.xml映射文件名称保持一致且在一个目录中
<mappers>
    <!--
        <mapper>单个加载映射文件
        class : 配置mapper接口的全限定名,通过Java中的Mapper接口来加载映射文件
    -->
    <mapper class="com.mybatis.mapper.DeptMapper"/>
    <mapper class="com.mybatis.mapper.EmpMapper"/>
</mappers>

5.2 批量加载映射文件

  指定Mapper接口的包名,MyBatis自动扫描包下边所有Mapper接口进行加载
  遵循规范:与mapper接口加载单个映射文件一致

<mappers>
    <!--
        <package>批量加载映射文件
        name : 存放Mapper接口与mapper.xml文件的包名
    -->
    <package name="com.mybatis.mapper"/>
</mappers>

四、mapper.xml

  mapper.xml映射文件中定义了操作数据库的Sql,每个Sql是一个statement,映射文件是MyBatis的核心

1. parameterType输入映射

1.1 简单类型

<select id="findUsersByRealname" parameterType="string" resultType="Users">
    select userid,username,password,realname from users where realname like #{realname}
</select>

1.2 实体类或pojo类型

  开发中通过实体类或pojo类型传递查询条件,查询条件是综合的查询条件,不仅包括实体类中查询条件还包括其它的查询条件,这时可以使用包装对象传递输入参数

实体类:Users和Pagebean

//实体类
public class Users{
    private Integer userid;
    private String username;
    private String password;
    private String realname;
    //get,set方法省略...
}
//页时使用的pojo对象
public class Pagebean {
    private int page;
    private int pageSize;
    private int maxCount;
    private int maxPage;
    private int offset;
    //get,set方法省略...
}

复合实体类:UsersQuery

public class UsersQuery {
    private Users users;
    private Pagebean pagebean;
    //get,set方法省略...
}

映射文件:

<select id="findUsersByRealnameAndPage" parameterType="UsersQuery" resultType="Users">
    select 
        userid,username,password,realname from users
    where 
        realname like #{users.realname}
    order by userid 
    limit #{pagebean.offset},#{pagebean.pageSize}
</select>

1.3 Map类型

<select id="findUsersWithMap" parameterType="map" resultType="Users">
    select 
        userid,username,password,realname from users
    where 
        realname like #{map_realname}
    order by userid 
    limit #{map_offset},#{map_size}
</select>

1.4 多输入参数

  MyBatis中允许有多个输入参数,可使用@Param注解来表示

/**
* @Param注解可以实现多个输入参数
* 每个输入参数前必须使用@Param注解
* 注解中value属性的值为获取此参数值的key
*
* 多参数使用了注解之后,mybatis就将这些注解中value属性值与参数值存放一个map集合中
* 其中value属性值为map集合的key
* 参数值为map集合value
*
* 使用此种方法可以省略paramterType的配置
*/
public Users login(@Param(value = "uname") String username, @Param("pwd") String password);

  这种做法类似与Map类型的输入参数,其中@Param注解的value属性值为Map的key,在映射文件中通过ognl可获取对应的value,并且parameterType可以不指定类型

<select id="login" resultType="Users">
    select userid,username,password,realname from users
    where username=#{uname} and password=#{pwd}
</select>

2. resultType输出映射

2.1 简单类型

  查询出来的结果集只有一行且一列,可以使用简单类型进行输出映射

public int findUsersCount();
<select id="findUsersCount" resultType="int">
    select count(*) from users
</select>

2.2 实体类对象和列表

  不管是输出的实体类是单个对象还是一个列表(list中包括实体类对象),在mapper.xml中resultType指定的类型是一样的
  在原始Dao的方式中,通过selectOne和selectList方法来区分返回值为单个对象或集合列表,而在mapper代理中,则通过接口中定义的方法返回值来区分

public Users findUsersByUserid(Integer userid);
public List<Users> findUsers();
<select id="findUsersByUserid" parameterType="int" resultType="Users">
    select userid,username,password,realname from users where userid=#{userid}
</select>
<select id="findUsers" resultType="Users">
    select userid,username,password,realname from users order by userid
</select>

2.3 resultMap

  • resultType可以指定将查询结果映射为实体类,但需要实体类的属性名和Sql查询的列名一致方可映射成功
  • 如果Sql查询字段名和实体类的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系,resultMap实质上还会将查询结果映射到实体类对象中
  • resultMap可以实现将查询结果映射为复合型的实体类,比如在查询结果映射对象中包括实体类和list实现一对一查询和一对多查询

定义resultMap

<!--
    resultType : 自动映射
    resultMap : 手动映射
    属性解析:
    type : 对应Java中实体类的类型,可以使用全限定名也是使用类型别名
    id : <resultMap>标签的唯一标识,在<select>的resultMap属性中使用
    子标签:
    <id /> 配置对应表中的主键
    <result /> 配置对应表中的普通字段
    property : 实体类中的属性名
    column : 表或结果集中的字段名
    javaType : 属性类型
    jdbcType : 字段类型
    其中javaType与jdbcType可不配置由mybaits自动识别
-->
<resultMap id="usersResultMap" type="Users">
    <id property="userid" column="id"/>
    <result property="username" column="uname"/>
    <result property="password" column="pwd"/>
    <result property="realname" column="rname"/>
</resultMap>

使用resultMap作为statement的输出映射类型

<select id="findUsersWithResultMap" resultMap="usersResultMap">
    select userid id,username uname,password pwd,realname rname from users
</select>

3. 动态Sql

3.1 什么是动态Sql

  动态Sql是指MyBatis核心对Sql语句进行灵活操作,通过表达式进行判断,对Sql进行灵活拼接、组装

3.2 if标签

  将实体类不为空的属性作为where条件

<select id="findEmpUseIf" parameterType="map" resultType="Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    where 1=1
    <!--test:判断条件,条件成立if标签中的Sql语句拼接-->
    <if test="ename!=null and ename!=''">
            and ename like #{ename}
    </if>
    <if test="sal!=null and sal!=0.0">
        and sal=#{sal}
    </if>
    <if test="deptno!=null and deptno!=0">
        and deptno=#{deptno}
    </if>
</select>

3.2 where标签

  where标签中包含的任意if条件如果成立,它就插入一个where关键字。此外,如果标签返回的内容是以AND 或OR 开头的,则它会自动剔除掉

<select id="findEmpUseWhere" parameterType="map" resultType="Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    <where>
    <if test="ename!=null and ename!=''">
        and ename like #{ename}
    </if>
    <if test="sal!=null and sal!=0.0">
        and sal=#{sal}
    </if>
    <if test="deptno!=null and deptno!=0">
        and deptno=#{deptno}
    </if>
    </where>
</select>

3.3 set标签

  当在update语句中使用if标签时,如果前面的if没有执行,则或导致逗号多余错误。使用set标签可以将动态的配置SET关键字,和剔除追加到条件末尾的任何不相关的逗号
  注意:如果set包含的内容为空的话则会出错

<update id="updateEmpUseSet" parameterType="Emp">
    update emp
    <set>
        <if test="ename!=null">
            ename=#{ename},
        </if>
        <if test="job!=null">
            job=#{job},
        </if>
        <if test="mgr!=null">
            mgr=#{mgr},
        </if>
        <if test="sal!=null">
            sal=#{sal},
        </if>
        <if test="comm!=null">
            comm=#{comm},
        </if>
        <if test="hiredate!=null">
            hiredate=#{hiredate},
        </if>
    </set>
        where empno=#{empno}
</update>

3.4 trim标签

trim标签属性解析:

  • prefix:前缀,包含内容前加上某些字符
  • suffix:后缀,包含内容后加上某些字符
  • prefixOverrides:剔除包含内容前的某些字符
  • suffixOverrides:剔除包含内容后的某些字符

trim来代替where标签的功能

<select id="findEmpUseTrim" parameterType="Emp" resultType="Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    <trim prefix="where" prefixOverrides="and|or">
        <if test="ename!=null">
            and ename like #{ename}
        </if>
        <if test="sal!=null and sal!=0.0">
            and sal=#{sal}
        </if>
        <if test="job!=null">
            and job=#{job}
        </if>
    </trim>
</select>

trim来代替set标签的功能

<update id="updateEmpUseTrim" parameterType="Emp">
    update emp
    <trim prefix="set" suffixOverrides=",">
        <if test="ename!=null">
            ename=#{ename},
        </if>
        <if test="job!=null">
            job=#{job},
        </if>
        <if test="mgr!=null">
            mgr=#{mgr},
        </if>
        <if test="sal!=null">
            sal=#{sal},
        </if>
        <if test="comm!=null">
            comm=#{comm},
        </if>
        <if test="hiredate!=null">
            hiredate=#{hiredate},
        </if>
    </trim>
    where empno=#{empno}
</update>

trim实现insert语句

<insert id="insertEmpUseTrim" parameterType="Emp">
    insert into emp
    <trim suffix="(" prefix=")" prefixOverrides=",">
        <if test="ename!=null">
            ename,
        </if>
        <if test="job!=null">
            job,
        </if>
        <if test="mgr!=null">
            mgr,
        </if>
        <if test="hiredate!=null">
            hiredate,
        </if>
        <if test="sal!=null">
            sal,
        </if>
        <if test="comm!=null">
            comm,
        </if>
    </trim>
    <trim prefix=" values(" suffix=")" suffixOverrides=",">
        <if test="ename!=null">
            #{ename},
        </if>
        <if test="job!=null">
            #{job},
        </if>
        <if test="mgr!=null">
            #{mgr},
        </if>
        <if test="hiredate!=null">
            #{hiredate},
        </if>
        <if test="sal!=null">
            #{sal},
        </if>
        <if test="comm!=null">
            #{comm},
        </if>
    </trim>
</insert>

3.5 foreach标签

  • 向Sql传递数组或list,MyBatis使用foreach解析
  • Sql只接收一个数组参数,这时sql解析参数的名称MyBatis固定为array
  • Sql只接收一个List参数,这时sql解析参数的名称MyBatis固定为list
  • 如果是通过一个实体类或pojo的属性传递到Sql的数组或list,则参数的名称为实体类或pojo中的属性名

属性解析:

  • index:为数组的下标
  • item:每个遍历生成对象中
  • open:开始遍历时拼接的串
  • close:结束遍历时拼接的串
  • separator:遍历的两个对象中需要拼接的串
public void deleteEmpUseForeachArray(int[] empnos);
public List<Emp> findEmpUseForeachList(List<Integer> empnos);
public List<Emp> findEmpUseForeachPojo(EmpQuery query);
<delete id="deleteEmpUseForeachArray" parameterType="int">
    delete from emp where empno in
    <foreach collection="array" open="(" close=")" separator="," item="id">
        #{id}
    </foreach>
</delete>

<select id="findEmpUseForeachList" parameterType="int" resultType="Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    where empno in
    <foreach collection="list" open="(" close=")" separator="," item="empno">
        #{empno}
    </foreach>
</select>

<select id="findEmpUseForeachPojo" parameterType="EmpQuery" resultType="Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    where deptno in
    <foreach collection="deptnos" open="(" close=")" separator="," item="deptno">
        #{deptno}
    </foreach>
</select>

3.6 choose,when,otherwise标签

  if-else-if判断

<select id="">
    select...
    <choose>
        <when test="">

        </when>
        <when test="">

        </when>
        <otherwise>

        </otherwise>
    </choose>
</select>

3.7 Sql片段

  将实现的动态Sql判断代码块抽取出来,组成一个Sql片段,其它的statement中就可以引用Sql片段,方便程序员进行开发
  注意:在sql片段中不要包括where标签

<select id="findEmpUseSql" parameterType="map" resultType="Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    <where>
        <!--  引入sql片段 -->
        <include refid="empSql"/>
    </where>
</select>

<sql id="empSql">
    <if test="ename!=null">
        and ename like #{ename}
    </if>
    <if test="sal!=null and sal!=0.0">
        and sal=#{sal}
    </if>
    <if test="deptno!=null and deptno!=0">
        and deptno=#{deptno}
    </if>
</sql>

五、关联查询

1. 数据模型分析

在这里插入图片描述

表功能介绍

  • 用户表:记录用户的基本信息
  • 订单表:记录用户所创建的订单(购买商品的订单)
  • 订单详情表:记录订单的详细信息即购买商品的信息
  • 商品表:记录商品的基本信息

表与表之间的业务关系

  1. 用户表和订单表:
    用户表---->订单表:一个用户可以创建多个订单,一对多关系
    订单表---->用户表:一个订单只由一个用户创建,一对一关系
  2. 订单表和订单详情表:
    订单表---->订单详情表:一个订单可以包含多个订单详情,因为一个订单可以购买多个商品,每个商品的购买信息在订单详情表中记录,一对多关系
    订单详情表----->订单表:一个订单详情只能包括在一个订单中,一对一关系
  3. 订单详情表和商品表:
    订单详情表---->商品表:一个订单详情只对应一个商品信息,一对一关系
    商品表---->订单详情表:一个商品可以包括在多个订单详情,一对多关系
  4. 订单表和商品表:
    订单表<---->商品表:一个订单中包含多个商品,一个商品可以添加在多个订单中,两者是通过订单详情表建立关系,多对多关系

2. 一对一查询

2.1 resultType

2.1.1 Sql语句
SELECT 
    id, order_number,totalprice,status,u.userid,username,password,realname
FROM 
    orders o,users u 
WHERE 
    o.userid=u.userid
2.1.2 实体类
public class Users {
    private Integer userid;
    private String username;
    private String password;
    private String realname;
    //get,set方法省略...
}
public class Orders {
    private Integer id;
    private String order_number;
    private Double totalPrice;
    private String status;
    //get,set方法省略...
}

原始的Orders类不能映射全部字段,需要新创建的实体类,创建一个包括查询字段较多的实体类
OrdersUsers中包含了Orders以及Users需要查询的属性

public class OrdersUsers {
    /**
     * 对应orders表中的字段
     */
    private Integer id;
    private String order_number;
    private Double totalPrice;
    private String status;
    /**
     * 对象users表中的字段
     */
    private Integer userid;
    private String username;
    private String password;
    private String realname;
}
2.1.3 Mapper接口
public List<OrdersUsers> findOrdersUseResultType();
2.1.4 mapper.xml
<select id="findOrdersUseResultType" resultType="OrdersUsers">
    SELECT 
        id, order_number,totalprice,status,u.userid,username,password,realname
    FROM 
        orders o,users u 
    WHERE 
        o.userid=u.userid
</select>
2.1.5 测试
SqlSession session = MybatisUtil.openSession();

OrderMapper mapper = session.getMapper(OrderMapper.class);
List<OrdersUsers> list = mapper.findOrdersUseResultType();
for (OrdersUsers ou : list) {
    System.out.println(ou);
}
session.close();

2.2 resultMap

2.2.1 Sql语句
SELECT 
    id, order_number,totalprice,status,u.userid,username,password,realname
FROM 
    orders o,users u 
WHERE 
    o.userid=u.userid
2.2.2 实体类

  在Orders类中加入Users属性,Users属性用于存储关联查询的用户信息,因为订单关联查询用户是一对一关系,所以这里使用单个Users对象存储关联查询的用户信息

public class Users {
    private Integer userid;
    private String username;
    private String password;
    private String realname;
    //get,set方法省略...
}
public class Orders {
    private Integer id;
    private String order_number;
    private Double totalPrice;
    private String status;
    /**
     * 一对一/多对一依赖关系属性
     */
    private Users users;
    //get,set方法省略...
}
2.2.3 Mapper接口
public List<Orders> findOrdersUseResultMap();
2.2.4 mapper.xml
<resultMap id="ordersUsersResultMap" type="Orders">
    <id column="id" property="id"/>
    <result column="order_number" property="order_number"/>
    <result column="totalprice" property="totalPrice"/>
    <result column="status" property="status"/>
    <association property="users" javaType="Users">
        <id column="userid" property="userid"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="realname" property="realname"/>
    </association>
</resultMap>
<select id="findOrdersUseResultMap" resultMap="ordersUsersResultMap">
    SELECT 
        id, order_number,totalprice,status,u.userid,username,password,realname
    FROM 
        orders o,users u 
    WHERE 
        o.userid=u.userid
</select>
2.2.5 测试
SqlSession session = MybatisUtil.openSession();

OrderMapper mapper = session.getMapper(OrderMapper.class);
List<Orders> list = mapper.findOrdersUseResultMap();
for (Orders o : list) {
    System.out.println(o);
}
session.close();

2.3 resultType和resultMap实现一对一查询小结

  • resultType:使用resultType实现较为简单,如果实体类中没有包括查询出来的列名,需要增加列名对应的属性,即可完成映射。如果查询结果没有特殊要求,建议使用resultType
  • resultMap:需要单独定义resultMap,实现有点麻烦,如果对查询结果有特殊的要求,使用resultMap可以完成将关联查询映射到实体类的属性中
  • resultMap可以实现延迟加载,resultType无法实现延迟加载

3. 一对多查询

3.1 Sql语句

SELECT 
	o.id,order_number,totalprice,status,d.id detailid,amount,goods_id 
FROM 
	orders o,ordersdetail d
WHERE 
    o.id=d.orders_id

3.2 实体类

  在Order类中加入List details属性,details属性用于存储关联查询的订单详情,因为订单关联查询订单详情是一对多关系,所以这里使用集合对象存储关联查询的订单详情信息

public class OrdersDetail {
    private Integer id;
    private Integer amount;
    private Integer goods_id;
    //get,set方法省略...
}
public class Orders {
    private Integer id;
    private String order_number;
    private Double totalPrice;
    private String status;
    /**
     * 一对多,多对多依赖关系属性
     */
    private List<OrdersDetail> details;
    //get,set方法省略...
}

3.2 Mapper接口

public List<Orders> findOrdersAndOrdersDetail();

3.3 mapper.xml

<resultMap id="ordersAndDetailResultMap" type="Orders">
    <id column="id" property="id"/>
    <result column="order_number" property="order_number"/>
    <result column="totalprice" property="totalPrice"/>
    <result column="status" property="status"/>
    <!--
        映射依赖关系属性
        <collection>用于描述一对多或多对多关系属性
        property : 关系属性的名称
        OrdersDetail : 关系属性集合中存放的实体类类型
    -->
    <collection property="details" ofType="OrdersDetail">
        <id column="detailid" property="id"/>
        <result column="amount" property="amount"/>
        <result column="goods_id" property="goods_id"/>
    </collection>
</resultMap>
<select id="findOrdersAndOrdersDetail" resultMap="ordersAndDetailResultMap">
    SELECT 
        o.id,order_number,totalprice,status,d.id detailid,amount,goods_id 
    FROM 
        orders o,ordersdetail d
    WHERE 
        o.id=d.orders_id
</select>

3.4 测试

SqlSession session = MybatisUtil.openSession();

OrderMapper mapper = session.getMapper(OrderMapper.class);
List<Orders> list = mapper.findOrdersAndOrdersDetail();
for (Orders orders : list) {
    System.out.println(orders);
}
session.close();

4. 多对多查询

4.1 订单与商品之间的多对多关系

4.1.1 Sql语句
SELECT
    o.id,order_number,totalprice,status,d.id detailid,amount,goods_id,gname,descr,price
FROM
    orders o,ordersdetail d,goods g
WHERE
    o.id=d.orders_id 
AND 
    d.goods_id=g.id
4.1.2 实体类

  将Orderdetail类中Integer goods_id属性修改为Goods goods属性,goods属性用于存储关联查询的商品信息。订单与订单详情是一对多关系,订单详情与商品是一对一关系,反之商品与订单详情是一对多关系,订单详情与订单是一对一关系,所以订单与商品之前为多对多关系

public class Goods {
    private Integer id;
    private String gname;
    private String descr;
    private Double price;
    //get,set方法省略...
}
public class OrdersDetail {
    private Integer id;
    private Integer amount;
    /**
     * 一对一依赖关系属性
     */
    private Goods goods;
    //get,set方法省略...
}
public class Orders {
    private Integer id;
    private String order_number;
    private Double totalPrice;
    private String status;
    /**
     * 一对多,多对多依赖关系属性
     */
    private List<OrdersDetail> details;
    //get,set方法省略...
}
4.1.3 Mapper接口
public List<Orders> findOrdersAndGoods();
4.1.4 mapper.xml
<resultMap id="ordersAndGoodsResultMap" type="Orders">
    <id column="id" property="id"/>
    <result column="order_number" property="order_number"/>
    <result column="totalprice" property="totalPrice"/>
    <result column="status" property="status"/>
    <collection property="details" ofType="OrdersDetail">
        <id column="detailid" property="id"/>
        <result column="amount" property="amount"/>
        <association property="goods" javaType="Goods">
            <id column="goods_id" property="id"/>
            <result column="gname" property="gname"/>
            <result column="descr" property="descr"/>
            <result column="price" property="price"/>
        </association>
    </collection>
</resultMap>
<select id="findOrdersAndGoods" resultMap="ordersAndGoodsResultMap">
    SELECT
        o.id,order_number,totalprice,status,d.id detailid,amount,goods_id,gname,descr,price
    FROM
        orders o,ordersdetail d,goods g
    WHERE
        o.id=d.orders_id 
    AND 
        d.goods_id=g.id
</select>
4.1.5 测试
SqlSession session = MybatisUtil.openSession();

OrderMapper mapper = session.getMapper(OrderMapper.class);
List<Orders> list = mapper.findOrdersAndGoods();
for (Orders orders : list) {
    System.out.println(orders);
}
session.close();

4.2 学生与课程之间的多对多关系

4.2.1 Sql语句
SELECT 
    s.id,name,gender,major,course_id,cname 
FROM 
    student s,student_course sc,course c
WHERE 
    s.id=sc.student_id
AND 
    sc.course_id=c.id
4.2.2 实体类
public class Course {
    private Integer id;
    private String cname;
    //get,set方法省略...
}
public class Student {
    private Integer id;
    private String name;
    private String gender;
    private String major;
    /**
     * 多对多的依赖关系属性
     */
    private List<Course> courses;
    //get,set方法省略...
}
4.2.3 Mapper接口
public List<Student> findStudent();
4.2.4 mapper.xml
<resultMap id="studentAndCourseResultMap" type="Student">
    <id column="id" property="id"/>
    <result column="NAME" property="name"/>
    <result column="gender" property="gender"/>
    <result column="major" property="major"/>
    <collection property="courses" ofType="Course">
        <id column="course_id" property="id"/>
        <result column="cname" property="cname"/>
    </collection>
</resultMap>
<select id="findStudent" resultMap="studentAndCourseResultMap">
    SELECT 
        s.id,name,gender,major,course_id,cname 
    FROM 
        student s,student_course sc,course c
    WHERE 
        s.id=sc.student_id
    AND 
        sc.course_id=c.id
</select>
4.2.5 测试
SqlSession session = MybatisUtil.openSession();

StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> list = mapper.findStudent();
for (Student student : list) {
    System.out.println(student);
}
session.close();

5. 关联查询总结

5.1 resultType

  作用:将查询结果按照Sql列名与实体类属性名一致性映射到实体类对象中
  场合:常见一些明细记录的展示,比如用户购买商品明细,将关联查询信息全部展示在页面时,此时可直接使用resultType将每一条记录映射到实体类中,在前端页面遍历list(list中是实体类)即可

5.2 resultMap

  使用association和collection完成一对一和一对多高级映射(对结果有特殊的映射要求)

5.2.1 association

  作用:将关联查询信息映射到一个实体类对象中
  场合:为了方便查询关联信息可以使用association将关联信息映射为当前对象的一个属性,比如:查询订单以及关联用户信息

5.2.2 collection

  作用:将关联查询信息映射到一个list集合中
  场合:为了方便查询遍历关联信息可以使用collection将关联信息映射到list集合中,比如:查询用户权限范围模块及模块下的菜单,可使用collection将模块映射到模块list中,将菜单列表映射到模块对象的菜单list属性中,这样的作的目的也是方便对查询结果集进行遍历查询。如果使用resultType无法将查询结果映射到list集合中

5.2.3 resultMap的继承

  resultMap标签可以通过extends属性来继承一个已有的或公共的resultMap,避免重复配置的出现,减少配置量。例子如下:

<!-- 父resultMap标签-->
<resultMap id="ordersRusultMap" type="Orders">
    <id column="id" property="id"/>
    <result column="order_number" property="order_number"/>
    <result column="totalprice" property="totalPrice"/>
    <result column="status" property="status"/>
</resultMap>
<!-- 继承父resultMap标签中的配置,避免重复配置 -->
<resultMap id="ordersUsersResultMap" type="Orders" extends="ordersRusultMap">
    <association property="users" javaType="Users">
        <id column="userid" property="userid"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="realname" property="realname"/>
    </association>
</resultMap>

六、延迟加载

1. 什么是延迟加载

  • 需要查询关联信息时,使用MyBatis延迟加载特性可有效的减少数据库压力,首次查询只查询主要信息,关联信息等用户获取时再加载
  • resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能

2. 打开延迟加载开关

  在MyBatis核心配置文件中配置:lazyLoadingEnabled、aggressiveLazyLoading

设置项描述允许值默认值
lazyLoadingEnabled全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载true \ falsefalse
aggressiveLazyLoading当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载true \ falsetrue
<!-- 全局参数设置 -->
<settings>
    <!-- 开启延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

3. 使用association实现延迟加载

  查询员工以及相关联的部门信息

3.1 Mapper接口

public List<Emp> findEmp();

3.2 mapper.xml

  查询员工信息

<select id="findEmp" resultMap="empResultMap">
    select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp
</select>

  查询关联的部门信息
  通过上边查询到的员工信息中deptno外键去关联查询部门信息,所以需要定义一个根据ID查询部门的语句

<select id="findDeptByDeptno" parameterType="int" resultType="Dept">
    select deptno,dname,loc from dept where deptno=#{deptno}
</select>

  resultMap配置

<resultMap id="empResultMap" type="Emp">
    <id column="empno" property="empno"/>
    <result column="ename" property="ename"/>
    <result column="job" property="job"/>
    <result column="mgr" property="mgr"/>
    <result column="hiredate" property="hiredate"/>
    <result column="sal" property="sal"/>
    <result column="comm" property="comm"/>
    <!-- 关系属性描述-->
    <!--
    <association>实现延迟加载
        select : 执行延迟加载时关联数据查询的sql对应的statementId
                1.执行的关联查询语句在同一mapper文件中,可以直接使用statementId
                2.执行的关联查询语句在不同的mapper文件中,namespace.statementId
        column : 在执行关联查询时,使用到主表的一个字段名称(外键字段的名称)
    -->
    <association property="dept" javaType="Dept" select="com.mybatis.mapper.DeptMapper.findDeptByDeptno" column="deptno">
    </association>
</resultMap>

4. 使用collection实现延迟加载

4.1 Mapper接口

public List<Dept> findDept();

4.2 mapper.xml

  查询部门信息

<select id="findDept" resultMap="deptResultMap">
    select deptno,dname,loc from dept
</select>

  查询关联的员工详情信息
  通过查询到的部门信息中deptno去关联查询员工详情,所以需要定义一个根据部门ID查询员工详情的语句

<select id="findEmpByDeptno" parameterType="int" resultType="Emp">
    select empno,ename,job,mgr,hiredate,sal,comm from emp
    where deptno=#{deptno}
</select>

  resultMap配置

<resultMap id="deptResultMap" type="Dept">
    <id column="deptno" property="deptno"/>
    <result column="dname" property="dname"/>
    <result column="loc" property="loc"/>
    <collection property="empList" ofType="Emp" select="com.mybatis.mapper.EmpMapper.findEmpByDeptno" column="deptno"/>
</resultMap>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

JTZ001

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

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

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

打赏作者

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

抵扣说明:

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

余额充值