欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

自定义mybatis

程序员文章站 2022-04-10 20:58:58
架构分析 Configuration类: 得到数据源对象 加载其它的实体类映射文件:UserMapper.xml,使用DOM4J Mapper类只是一个实体类:POJO,用来封装数据 SqlSession类: 生成了UserMapper接口的代理对象,JDK代理。 访问数据库:JDBC 封装查询的结 ......

架构分析

  1. configuration类:

    1. 得到数据源对象

    2. 加载其它的实体类映射文件:usermapper.xml,使用dom4j

  2. mapper类只是一个实体类:pojo,用来封装数据

  3. sqlsession类:

    1. 生成了usermapper接口的代理对象,jdk代理。

    2. 访问数据库:jdbc

    3. 封装查询的结果集,使用反射

自定义mybatis

使用到的技术

自定义mybatis

 

添加所需的所有依赖,做好准备工作。

pom.xml文件

 <properties>
        <!--dom4j版本-->
        <dom4j.vesrion>1.6.1</dom4j.vesrion>
        <!--dom4j依赖包版本-->
        <jaxen.version>1.1.6</jaxen.version>
        <!--mysql驱动版本-->
        <mysql.version>5.1.30</mysql.version>
        <!--c3p0版本-->
        <c3p0.version>0.9.2.1</c3p0.version>
        <!-- junit版本 -->
        <junit.version>4.12</junit.version>
    </properties>

    <dependencies>
        <!--dom4j依赖-->
        <dependency>
            <groupid>dom4j</groupid>
            <artifactid>dom4j</artifactid>
            <version>${dom4j.vesrion}</version>
        </dependency>
        <dependency>
            <groupid>jaxen</groupid>
            <artifactid>jaxen</artifactid>
            <version>${jaxen.version}</version>
        </dependency>
        <!-- mysql数据库依赖 -->
        <dependency>
            <groupid>mysql</groupid>
            <artifactid>mysql-connector-java</artifactid>
            <version>${mysql.version}</version>
        </dependency>
        <!--c3p0依赖-->
        <dependency>
            <groupid>com.mchange</groupid>
            <artifactid>c3p0</artifactid>
            <version>${c3p0.version}</version>
        </dependency>
        <!-- junit依赖 -->
        <dependency>
            <groupid>junit</groupid>
            <artifactid>junit</artifactid>
            <version>${junit.version}</version>
        </dependency>
    </dependencies>

自定义mybatis:mapper封装数据类

分析映射配置:usermapper.xml 

自定义mybatis

 

步骤

  1. 创建包com.it.mybatis

  2. 创建实体类:mapper包含4个属性:namespace,id,resulttype,sql

  3. 重写tostring()方法,方便后期测试看到封装的结果

  4. 生成get和set方法

  5. 一个mapper对象代表一条要操作的查询语句对象

代码

package com.it.mybatis;

/**
 用来封装映射文件的实体类
 */
public class mapper {

    private string namespace;  //接口类全名
    private string id;   //接口中方法名
    private string resulttype;  //封装的数据类型
    private string sql;   //要执行的sql语句

    @override
    public string tostring() {
        return "mapper{" +
                "namespace='" + namespace + '\'' +
                ", id='" + id + '\'' +
                ", resulttype='" + resulttype + '\'' +
                ", sql='" + sql + '\'' +
                '}';
    }

    public string getnamespace() {
        return namespace;
    }

    public void setnamespace(string namespace) {
        this.namespace = namespace;
    }

    public string getid() {
        return id;
    }

    public void setid(string id) {
        this.id = id;
    }

    public string getresulttype() {
        return resulttype;
    }

    public void setresulttype(string resulttype) {
        this.resulttype = resulttype;
    }

    public string getsql() {
        return sql;
    }

    public void setsql(string sql) {
        this.sql = sql;
    }
}

自定义mybatis:设计configuration的基本属性

  1. 设计configuration的基本属性

  2. 产生get和set方法

  3. 产生tostring()方法

分析

sqlmapconfig.xml文件

自定义mybatis

 

  1. 创建loadsqlmapconfig()方法,它的作用:

    1. 解析sqlmapconfig.xml配置文件,给configuration中的属性赋值

    2. 解析usermapper.xml配置文件,给mapper中的属性赋值

  2. 在构造方法中调用方法: loadsqlmapconfig()

  3. 作用:使用dom4j解析sqlmapconfig.xml文件,给数据库有关的属性赋值

    1. 从类路径加载/sqlmapconfig.xml配置文件,创建输入流

    2. 使用dom4j得到文档对象

    3. 使用xpath读取所有property元素

    4. 遍历每个property元素,读取它的name和value属性值

    5. 判断name的字符串,如果与类中的属性名相同,则赋值到相应属性中

  4.configuration解析实体类映射文件

         解析usermapper.xml并且封装到mapper类中

    1. 创建新的方法loadmapper(document document),将当前的文档对象传递给方法

    2. 读取<mapper>中的resource属性值

    3. 通过resource读取它对应的xml文件

    4. 得到namespace,id,resulttype,sql的值,封装成mapper对象

    5. 在loadsqlmapconfig()中调用此方法

自定义mybatis

 

 

loadmapper(document document)方法开发步骤

作用:进一步解析其它的xml文件,给mappers属性赋值

  1. 读取mapper中的resource属性值

    1. 使用xpath读取所有mapper元素

    2. 遍历每个mapper元素

    3. 读取mapper的resource属性值

  2. 通过resource读取它对应的xml文件,得到namespace,id,resulttype,sql的值

    1. 使用类对象,读取输入流下面resource,注:要加上/

    2. 创建文档对象

    3. 读取根元素mapper

    4. 读取namespace属性

    5. 读取根元素下的一个select标签

    6. 得到id,resulttype,sql内容

  3. 封装成mapper对象

    1. 创建一个自定义的mapper对象,封装上面三个属性

    2. 再封装namespace属性

    3. 将封装好的mapper对象添加到this的mappers属性中,其中键是namespace+"."+id,值是自定义的mapper对象。

代码

package com.it.mybatis;

import com.mchange.v2.c3p0.combopooleddatasource;
import org.dom4j.document;
import org.dom4j.documentexception;
import org.dom4j.element;
import org.dom4j.io.saxreader;

import javax.sql.datasource;
import java.beans.propertyvetoexception;
import java.io.inputstream;
import java.util.hashmap;
import java.util.list;
import java.util.map;

/**
 1. 封装sqlmapconfig.xml配置信息
 2. 得到数据源
 3. 加载usermapper.xml配置信息
 */
public class configuration {

    //数据源的四个属性
    private string username;
    private string password;
    private string url;
    private string driver;

    //封装其它的映射文件中属性
    private map<string, mapper> mappers = new hashmap<>();

    private datasource datasource;  //数据源

    //在构造方法中调用
    public configuration() {
        try {
            loadsqlmapconfig();  //加载配置文件
            createdatasource();  //创建数据源
        } catch (exception e) {
            e.printstacktrace();
        }
    }

    /*
        解析sqlmapconfig.xml文件,封装上面的数据源的四个属性
         */
    private void loadsqlmapconfig() throws exception {
        //得到输入流
        inputstream inputstream = configuration.class.getresourceasstream("/sqlmapconfig.xml");
        //得到文档对象
        saxreader saxreader = new saxreader();
        document document = saxreader.read(inputstream);
        //读取property
        list<element> list = document.selectnodes("//property");
        for (element property : list) {
            //读取name属性
            string name = property.attributevalue("name");
            //读取value属性
            string value = property.attributevalue("value");
            //判断是哪个属性
            switch (name) {
                case "username":
                    this.username = value;
                    break;
                case "password":
                    this.password = value;
                    break;
                case "driver":
                    this.driver = value;
                    break;
                case "url":
                    this.url = value;
                    break;
            }
        }
        //读取usermapper.xml文件
        loadmapper(document);
    }

    /**
    解析其它的实体类映射文件
    @param document 上面已经得到的文档对象
     */
    private void loadmapper(document document) throws exception {
        //读取mapper中resource属性
        list<element> list = document.selectnodes("//mapper");
        for (element mapperelement : list) {
            //读取mapper中resource属性
            string resource = mapperelement.attributevalue("resource");
            //再次读取新的xml文件
            inputstream in = configuration.class.getresourceasstream("/" + resource);
            //创建文档对象
            document doc = new saxreader().read(in);
            //得到根元素
            element rootelement = doc.getrootelement();
            string namespace = rootelement.attributevalue("namespace");
            //得到mapper下select元素
            element select = rootelement.element("select");
            //得到id属性
            string id = select.attributevalue("id");
            string resulttype = select.attributevalue("resulttype");
            string sql = select.gettexttrim();
            //创建mapper对象
            mapper mapper = new mapper();
            mapper.setid(id);
            mapper.setnamespace(namespace);
            mapper.setsql(sql);
            mapper.setresulttype(resulttype);
            //键=namespace + "." + "id";
            string key = namespace + "." + id;
            //将创建好的mapper对象加到map集合中
            mappers.put(key,mapper);
        }
    }

    /**
     创建数据源
     */
    private void createdatasource() throws propertyvetoexception {
        //使用c3p0的数据源
        combopooleddatasource ds = new combopooleddatasource();
        //设置数据库访问属性
        ds.setuser(username);
        ds.setpassword(password);
        ds.setjdbcurl(url);
        ds.setdriverclass(driver);
        this.datasource = ds;
    }

    @override
    public string tostring() {
        return "configuration{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", url='" + url + '\'' +
                ", driver='" + driver + '\'' +
                ", mappers=" + mappers +
                ", datasource=" + datasource +
                '}';
    }

    public string getusername() {
        return username;
    }

    public void setusername(string username) {
        this.username = username;
    }

    public string getpassword() {
        return password;
    }

    public void setpassword(string password) {
        this.password = password;
    }

    public string geturl() {
        return url;
    }

    public void seturl(string url) {
        this.url = url;
    }

    public string getdriver() {
        return driver;
    }

    public void setdriver(string driver) {
        this.driver = driver;
    }

    public map<string, mapper> getmappers() {
        return mappers;
    }

    public void setmappers(map<string, mapper> mappers) {
        this.mappers = mappers;
    }

    public datasource getdatasource() {
        return datasource;
    }

    public void setdatasource(datasource datasource) {
        this.datasource = datasource;
    }
}

 

核心组件sqlsession:编写getmapper方法

自定义mybatis

 

步骤

自定义mybatis

得到sql语句和返回类型

  1. 得到configuration中map集合

    1. 实例化configuration对象

    2. 通过configuration得到mapper对象的集合

  2. 得到map中的键:类全名.方法名

    1. 通过方法对象->得到声明的接口->得到名称:即类全名 com.it.dao.usermapper

    2. 获取当前执行的方法名称:findallusers

    3. 通过类全名+方法名得到键

  3. 得到mapper中相应的属性

    1. 通过类全名+"."+方法名,从mappers中得到映射的mapper对象

    2. 从mapper中获取查询的sql语句

    3. 从mapper中获取返回值类型resulttype

    4. 通过反射将上面的resulttype字符串转成类对象,供后面的方法使用

得到connection对象访问数据库

  1. 通过configuration得到数据源,通过数据源得到连接对象

  2. 调用list queryforlist(connection connection, string sql, class clazz)方法

    1. 参数:连接对象,sql语句,结果集的类型。 直接创建一个list集合,添加3个user对象到集合中,暂时不访问数据库。

    2. 返回封装好的集合

 

package com.it.mybatis;

import com.itheima.entity.user;

import javax.sql.datasource;
import java.lang.reflect.field;
import java.lang.reflect.invocationhandler;
import java.lang.reflect.method;
import java.lang.reflect.proxy;
import java.sql.*;
import java.util.arraylist;
import java.util.list;
import java.util.map;

/**
 核心类
 1. 生成了usermapper接口的代理对象,jdk代理。
 2. 访问数据库:jdbc
 3. 封装查询的结果集,使用反射。
 */
public class sqlsession {

    public <t> t getmapper(class<t> clazz) {
        /*
        参数1:类加载器
        参数2:接口数组
        参数3:每个方法调用一次
         */
        return (t) proxy.newproxyinstance(
                sqlsession.class.getclassloader(),
                new class[]{clazz},
                new invocationhandler() {
                    @override
                    public object invoke(object proxy, method method, object[] args) throws throwable {
                       //1. 通过键得到mapper对象
                        //创建configuration对象
                        configuration configuration = new configuration();
                        //得到集合
                        map<string, mapper> mappers = configuration.getmappers();
                        string id = method.getname();  //方法名
                        //getdeclaringclass得到method所在类全名
                        string namespace = method.getdeclaringclass().getname();
                        //如何得到键
                        string key = namespace + "." + id;
                        //通过键得到值
                        mapper mapper = mappers.get(key);

                        //2. 从mapper对象中得到sql语句执行,并且封装成对象返回
                        string sql = mapper.getsql();
                        //resulttype = com.itheima.entity.user
                        string resulttype = mapper.getresulttype();
                        //将字符串转成class
                        class type = class.forname(resulttype);

                        //3.查询数据库,必须要连接对象
                        //连接对象从数据源中得到
                        datasource datasource = configuration.getdatasource();
                        connection connection = datasource.getconnection();

                        //使用jdbc访问数据库得到封装好的结果
                        list list = queryforlist(connection, sql, type);
                        return list;
                    }
                });
    }

    /**
     通过jdbc来访问数据库
     @param connection 连接对象
     @param sql 语句
     @param type 返回类型
     @return
     */
    private list queryforlist(connection connection, string sql, class type) throws exception {
        list users = new arraylist<>();

        //1. 通过connection创建语句对象:preparedstatement
        preparedstatement ps = connection.preparestatement(sql);

        //2. 通过语句对象执行sql语句
        resultset rs = ps.executequery();

        //3. 执行完毕以后得到结果集resultset
        while(rs.next()) {
            //4. 将resultset进行遍历,封装成实体类对象,添加到集合中
            //创建对象
            object user = type.getconstructor().newinstance();
            //得到类中所有的属性
            field[] fields = type.getdeclaredfields();
            for (field field : fields) {
                //得到属性名
                string name = field.getname();
                //得到相应的值
                object value = rs.getobject(name);
                //暴力反射
                field.setaccessible(true);
                //给每个属性赋值
                field.set(user, value);
            }
            users.add(user);
        }

        //5.释放资源
        rs.close();
        ps.close();
        connection.close();

        return users;
    }

}