Mybatis-接口代理方式Dao

接口代理方式实现Dao

案例项目骨架

什么是代理开发方式?

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

编写StudentMapper接口

案例演示

源码分析 

分析动态代理对象如何生成的?

分析方法是如何执行的?


接口代理方式实现Dao

案例项目骨架

继续使用之前Mybatis-传统实现方式Dao案例

什么是代理开发方式?

传统的方式实现Dao层,我们既要写接口,还要写实现类。而MyBatis框架可以帮我们省略编写Dao层接口实现类的步骤。程序员只要编写接口就行了,Mybatis框架自身可以根据接口的定义来创建接口的动态代理对象。

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

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

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

(1) Mapper.xml文件中的namespacemapper接口全限定名(全类名)相同。

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

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

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

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

StudentMapper.xml映射配置文件中的mapper标签的namespace属性值是StudentMapper接口的全类名。

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

StudentMapper.xml中的id属性值为selectAll,则StudentMapper接口中的抽象方法名要与其一致。

 

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

StudentMapper接口中抽象方法的参数类型是Integer,在其对应的映射配置文件中的parameterType的值也要是Integer

(注:此处本来是java.lang.Integer,但是在Mybatis中写int框架会自动将其转为Integer)

 

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

  

编写StudentMapper接口

按照上面的描述,总的来说就如下图所示:

案例演示

我们使用上一个案例的代码进行修改就行。

(1)删除mapper层的接口实现类

(2)修改映射配置文件

全部代码如下所示:

(1)bean层的Student类不变

(2)controller层的StudentController类不变

(3)mapper层将impl包(里面是其实现类)删除,StudentMapper接口不变

(4)service层的StudentServiceImpl是主要修改的对象!!!

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() {
        InputStream is = null;
        SqlSession sqlSession = null;
        List<Student> list = null;
        try {
            // 1. 加载核心配置文件
            is = Resources.getResourceAsStream("MyBatisConfig.xml");
            // 2.获取SqlSessionFactory工厂对象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            // 3.通过工厂对象获取SqlSession对象
            sqlSession = sqlSessionFactory.openSession(true);

            // ***4.获取StudentMapper接口的实现类对象(通过Mybatis动态生成)
            // getMapper() 里面要一个接口类型的class对象
            // 等价于:StudentMapper mapper = new StudentMapperImpl();
            // 只不过是MyBatis通过动态的形式为我们获取到的实现类对象
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

            // 5.通过实现类对象调用方法,接受结果
            list = mapper.selectAll();
        } catch (Exception e) {
            e.printStackTrace();
        } 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) {
        InputStream is = null;
        SqlSession sqlSession = null;
        Student stu = null;
        try {
            // 1. 加载核心配置文件
            is = Resources.getResourceAsStream("MyBatisConfig.xml");
            // 2.获取SqlSessionFactory工厂对象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            // 3.通过工厂对象获取SqlSession对象
            sqlSession = sqlSessionFactory.openSession(true);

            // ***4.获取StudentMapper接口的实现类对象(通过Mybatis动态生成)
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

            // 5.通过实现类对象调用方法,接受结果
            stu = mapper.selectById(id);

        } catch (Exception e) {
            e.printStackTrace();
        } 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) {
        InputStream is = null;
        SqlSession sqlSession = null;
        Integer result = null;
        try {
            // 1. 加载核心配置文件
            is = Resources.getResourceAsStream("MyBatisConfig.xml");
            // 2.获取SqlSessionFactory工厂对象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            // 3.通过工厂对象获取SqlSession对象
            sqlSession = sqlSessionFactory.openSession(true);

            // ***4.获取StudentMapper接口的实现类对象(通过Mybatis动态生成)
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

            // 5.通过实现类对象调用方法,接受结果
            result = mapper.insert(stu);

        } catch (Exception e) {
            e.printStackTrace();
        } 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) {
        InputStream is = null;
        SqlSession sqlSession = null;
        Integer result = null;
        try {
            // 1. 加载核心配置文件
            is = Resources.getResourceAsStream("MyBatisConfig.xml");
            // 2.获取SqlSessionFactory工厂对象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            // 3.通过工厂对象获取SqlSession对象
            sqlSession = sqlSessionFactory.openSession(true);

            // ***4.获取StudentMapper接口的实现类对象(通过Mybatis动态生成)
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

            // 5.通过实现类对象调用方法,接受结果
            result = mapper.update(stu);

        } catch (Exception e) {
            e.printStackTrace();
        } 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) {
        InputStream is = null;
        SqlSession sqlSession = null;
        Integer result = null;
        try {
            // 1. 加载核心配置文件
            is = Resources.getResourceAsStream("MyBatisConfig.xml");
            // 2.获取SqlSessionFactory工厂对象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            // 3.通过工厂对象获取SqlSession对象
            sqlSession = sqlSessionFactory.openSession(true);

            // ***4.获取StudentMapper接口的实现类对象(通过Mybatis动态生成)
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

            // 5.通过实现类对象调用方法,接受结果
            result = mapper.delete(id);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6.释放资源
            if(sqlSession != null) {
                sqlSession.close();
            }
            if(is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        // 7.返回结果
        return result;
    }
}

(5)核心配置文件MyBatisConfig.xml不变

(6)映射配置文件StudentMapper.xml要改一下

里面的mapper标签下的namespace属性改为全类名

<?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属性:名称空间
-->

<mapper namespace="com.itheima.mapper.StudentMapper">
    <!--
    select:查询功能的标签
    id属性:唯一标识
    resultType属性:指定结果映射对象类型
    parameterType属性:指定参数映射对象类型
    -->
    <select id="selectAll" resultType="student">
        SELECT * FROM student
    </select>
    
    <select id="selectById" resultType="student" parameterType="int">
        SELECT * FROM student WHERE id = #{id}
    </select>

    <!--
        返回的是一个int类型的行数!所以可以省略resultType
        但是,SQL语句的参数id、name、age是从学生对象中来所以,要有parameterType
    -->
    <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>

    <!-- java.lang.Integer -> int-->
    <delete id="delete" parameterType="int">
        DELETE FROM student WHERE id = #{id}
    </delete>
</mapper>




源码分析 

分析动态代理对象如何生成的?

通过动态代理开发模式,我们只编写一个接口,不写实现类,我们通过 getMapper() 方法最终获取到 org.apache.ibatis.binding.MapperProxy 代理对象,然后执行功能,而这个代理对象正是 MyBatis 使用了 JDK 的动态代理技术,帮助我们生成了代理实现类对象。从而可以进行相关持久化操作。

获取StudentMapper接口的实现类对象(通过Mybatis动态生成),getMapper() 的底层如下:

getMapper()是来自SqlSeesion

<T> T getMapper(Class<T> var1);

SqlSeesion的实现类为DefaultSqlSession ,在里面重写了getMapper()方法

注意:里面的type就是StudentMapper接口的class对象this就是SqlSession对象

然后又调用了configuration的getMapper方法

public <T> T getMapper(Class<T> type) {
    return this.configuration.getMapper(type, this);
}

configuration对象中的getMapper()方法,又调用mapperRegistry的getMapper方法

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return this.mapperRegistry.getMapper(type, sqlSession);
}

 获取了映射代理工厂对象mapperProxyFactory,工厂又调用了newInstance

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
    if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    } else {
        try {
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
        }
    }
}

 创建了映射对象MapperProxy,调用了 newInstance方法将映射代理对象mapperProxy,进行传递

public T newInstance(SqlSession sqlSession) {
    MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
    return this.newInstance(mapperProxy);
}

最终是调用了Proxy.newProxyInstance(JDK底层提供的动态代理)

里面的三个参数分别是:类加载器、class类型的对象数组,代理规则

protected T newInstance(MapperProxy<T> mapperProxy) {
    return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}

分析方法是如何执行的?

动态代理实现类对象在执行方法的时候最终调用了 mapperMethod.execute() 方法,这个方法中通过 switch 语句根据操作类型来判断是新增、修改、删除、查询操作,最后一步回到了 MyBatis 最原生的 SqlSession 方式来执行增删改查

动态代理要执行方法首先是要借助invoke()方法,得到了mapperMethod对象,调用execute方法

 通过switch对方法的类型进行判断!最后还是使用SqlSession对象的insert()方法!

posted @ 2022-02-27 02:23  金鳞踏雨  阅读(56)  评论(0编辑  收藏  举报  来源