自定义Mybatis

自定义mybatis:编写SqlSessionFactory和SqlSession

目标

编写SqlSessionFactory和SqlSession

复制入门案例模块,修改模块名为:mybatis01_02_framework,导入模块
[外链图片转存失败(img-IOO8iSrI-1564982999460)(/)]

删除配置文件中的DTD约束,不然DOM解析会联网,会失败

编写SqlSessionFactory类
  1. 新建com.itheima.mybatis
  2. com.itheima.mybatis创建SqlSessionFactory类

SqlSessionFactory的作用是创建SqlSession

public class SqlSessionFactory {
    public SqlSession openSession() {
        return new SqlSession();
    }
}
编写SqlSession类

com.itheima.mybatis创建SqlSession类

SqlSession的作用是获取Mapper的代理对象

UserMapper userMapper = session.getMapper(UserMapper.class);
import java.lang.reflect.Proxy;
// SqlSession的作用是获取Mapper的代理对象
public class SqlSession {
    /*
        Object newProxyInstance(ClassLoader loader, 类加载器
                                  Class<?>[] interfaces, 接口
                                  InvocationHandler h) 调用处理器
     */
    public <T> T getMapper(Class<T> type) {
        // 生成参数的代理对象
        return (T)Proxy.newProxyInstance(
                this.getClass().getClassLoader(),
                new Class[] {type},
                new MyInvocationHandler()
                );
    }
}

Mapper的代理对象是用来执行SQL语句的

List<User> users = userMapper.findAllUsers();

我们先固定写一些数据

public class MyInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 返回查询数据
        ArrayList<User> list = new ArrayList<>();
        list.add(new User(2, "张三", new Date(100), "男", "广州"));
        list.add(new User(3, "李四", new Date(200), "女", "深圳"));
        return list;
    }
}

小结

  1. 说出SqlSessionFactory的作用

    用于创建SqlSession

  2. 说出SqlSession的作用

    getMapper() 产生接口的代理对象

自定义mybatis:设计Mapper类

目标

编写Mapper类封装UserMapper.xml数据

分析UserMapper.xml

[外链图片转存失败(img-fE1zUFJX-1564982999461)()]

步骤
  1. 创建Mapper实体类:包含4个属性:namespace,id,resultType,sql
  2. 生成get和set方法
代码
package com.itheima.mybatis;

/**
 用来封装映射文件的实体类
 一个Mapper对象代表一条要操作的查询语句对象
 */
public class Mapper {
    private String namespace;  //接口类全名
    private String id;   //接口中方法名
    private String resultType;  //封装的数据类型
    private String sql;   //要执行的SQL语句

   	// 省略getter/setter
}

小结

Mapper实体类中有哪几个属性?

  1. namespace
  2. id
  3. resultType
  4. sql

自定义mybatis:设计Configuration类

目标

编写Configuration类

分析

sqlMapConfig.xml文件

[外链图片转存失败(img-x4GRbEBf-1564982999461)()]

编写Configuration类步骤
  1. 创建driver,url, username,password四个属性
  2. 实例化1个空的Map集合:封装接口映射文件的XML信息
  3. 声明数据源对象DataSource
  4. 生成get和set方法,生成toString()方法
代码
package com.itheima.mybatis;

import javax.sql.DataSource;
import java.util.HashMap;
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;  //数据源
	// 省略getter/setter
}

小结

Configuration有哪些属性?

private String driver; 驱动类名
private String url; 数据url
private String username; 数据库账号
private String password; 数据库密码
private HashMap<String, Mapper> mappers = new HashMap<>(); // 对应接口映射文件数据
private DruidDataSource dds; // 连接池

自定义mybatis:在Configuration中解析sqlMapConfig.xml核心配置文件

目标

解析核心配置文件sqlMapConfig.xml,给Configuration中的成员变量赋值,创建连接池

分析

[外链图片转存失败(img-SkdeXmVb-1564982999461)()]

实现步骤

编写loadSqlMapConfig方法:使用dom4j解析sqlMapConfig.xml文件,给数据库有关的属性赋值

  1. 从类路径加载sqlMapConfig.xml配置文件,创建输入流
  2. 使用dom4j得到文档对象
  3. 使用XPath读取每个property元素,读取它的name和value属性值
  4. 给对象的属性赋值
  5. 创建Druid连接池
Configuration代码
package com.itheima.mybatis;

import com.alibaba.druid.pool.DruidDataSource;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import javax.sql.DataSource;
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 driver;
    private String url;
    private String username;
    private String password;

    // 数据源
    private DataSource dataSource;

    // 封装其它的映射文件中属性
    private Map<String, Mapper> mappers = new HashMap<>();

    public Configuration() {
        // 加载sqlMapConfig.xml中的数据库链接参数
        loadSqlMapConfig();
    }

    private void loadSqlMapConfig() {
        // 得到输入流
        InputStream in = Configuration.class.getResourceAsStream("/sqlMapConfig.xml");

        // DOM解析
        // 得到文档对象
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(in);
            // 加载数据库连接参数
            // 获得数据库连接字符串:driver
            Element driverElement = (Element) document.selectSingleNode("//property[@name='driver']");
            driver = driverElement.attributeValue("value");
            // 获得数据库连接字符串:url
            Element urlElement = (Element) document.selectSingleNode("//property[@name='url']");
            url = urlElement.attributeValue("value");
            // 获得数据库连接字符串:username
            Element usernameElement = (Element) document.selectSingleNode("//property[@name='username']");
            username = usernameElement.attributeValue("value");
            // 获得数据库连接字符串:password
            Element passwordElement = (Element) document.selectSingleNode("//property[@name='username']");
            password = passwordElement.attributeValue("value");
            
            // 创建数据源
            DruidDataSource ds = new DruidDataSource();
        	ds.setDriverClassName(driver);
        	ds.setUrl(url);
        	ds.setUsername(username);
        	ds.setPassword(password);
        	this.dataSource = ds;
            
            // TODO:加载接口映射文件
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
}

小结

loadSqlMapConfig()方法做了什么事情?

解析核心配置文件的数据,保存到Configuration的成员成员变量中,解析了

dirver,url, username,password,创建了连接池

自定义mybatis:编写Configuration解析Mapper实体类映射文件

目标

解析UserMapper.xml并且封装到Mapper类中

分析

[外链图片转存失败(img-NSOD3Li9-1564982999462)()]

实现步骤

作用:进一步解析接口映射XML文件,给mappers属性赋值

  1. 读取mapper中的resource属性值

  2. 解析resource对应的XML文件,得到namespace,id,resultType,sql的值

  3. 封装成Mapper对象,保存到Map集合中

代码
package com.itheima.mybatis;

import com.alibaba.druid.pool.DruidDataSource;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import javax.sql.DataSource;
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 driver;
    private String url;
    private String username;
    private String password;

    // 数据源
    private DataSource dataSource;

    // 封装其它的映射文件中属性
    private Map<String, Mapper> mappers = new HashMap<>();

    public Configuration() {
        // 加载sqlMapConfig.xml中的数据库链接参数
        loadSqlMapConfig();
    }

    private void loadSqlMapConfig() {
        // 得到输入流
        InputStream in = Configuration.class.getResourceAsStream("/sqlMapConfig.xml");

        // DOM解析
        // 得到文档对象
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(in);
            // 加载数据库连接参数
            // 获得数据库连接字符串:driver
            Element driverElement = (Element) document.selectSingleNode("//property[@name='driver']");
            driver = driverElement.attributeValue("value");
            // 获得数据库连接字符串:url
            Element urlElement = (Element) document.selectSingleNode("//property[@name='url']");
            url = urlElement.attributeValue("value");
            // 获得数据库连接字符串:username
            Element usernameElement = (Element) document.selectSingleNode("//property[@name='username']");
            username = usernameElement.attributeValue("value");
            // 获得数据库连接字符串:password
            Element passwordElement = (Element) document.selectSingleNode("//property[@name='username']");
            password = passwordElement.attributeValue("value");

            // 创建数据源
            DruidDataSource ds = new DruidDataSource();
        	ds.setDriverClassName(driver);
        	ds.setUrl(url);
        	ds.setUsername(username);
        	ds.setPassword(password);
        	this.dataSource = ds;
            
            // 加载接口映射文件
            List<Node> nodes = document.selectNodes("//mapper");
            for (Node node : nodes) {
                Element mapperElement = (Element) node;
                // com/itheima/dao/UserMapper.xml
                String resource = mapperElement.attributeValue("resource");
                InputStream mapperIn = Configuration.class.getResourceAsStream("/" + resource);

                // DOM解析
                SAXReader reader = new SAXReader();
                Document mapperDocument = reader.read(mapperIn);

                // rootElement:<mapper namespace="com.itheima.dao.UserMapper">
                Element rootElement = mapperDocument.getRootElement();
                String namespace = rootElement.attributeValue("namespace");

               /* <select id="findAllUsers" resultType="com.itheima.entity.User">
                        select * from user;
                </select>*/
                Element select = rootElement.element("select");

                // 获得id属性值
                String id = select.attributeValue("id");
                // 获得返回值类型
                String resultType = select.attributeValue("resultType");
                // 获得标签体内容:sql语句字符串
                String sql = select.getTextTrim();

                // 创建Mapper对象
                Mapper mapper = new Mapper(namespace, id, resultType, sql);
                // 将Mapper对象添加到集合mappers中
                mappers.put(namespace + "." + id, mapper);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

	// 省略getter/setter
}

小结

  1. 解析UserMapper.xml文件,封装成Mapper对象,其中键和值分别是什么?

    键:接口类全名+"." + “方法名”

    值:Mapper对象,封装了4个属性:namespace,id,resultType,sql

自定义mybatis:封装查询的结果集

目标

  1. 使用JDBC从数据库中查询数据
  2. 使用反射来实例化查询结果对象,并且封装一条记录到对象中,添加对象到集合中

[外链图片转存失败(img-yoENfxxD-1564982999462)(/)]

步骤

[外链图片转存失败(img-wrwJir0R-1564982999463)()]

  1. 通过连接池得到连接对象

  2. 使用JDBC访问数据库执行SQL语句

  3. 处理结果集中的每条记录

  4. 使用反射将每条记录封装成一个对象

  5. 关闭资源

代码
package com.itheima.mybatis;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

// Mapper的代理对象是用来执行SQL语句的
public class MyInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 返回查询数据
        // 思考:在这个方法中,需要做什么?
        // 1.通过数据源得到连接对象
        // 2.得到要执行的 sql 语句
        // 3.执行数据库操作,封装结果集并且返回
        String methodName = method.getName();
        String className = method.getDeclaringClass().getName();
        String allName = className + "." + methodName;
        System.out.println("allName = " + allName);
        // allName = com.itheima.dao.UserMapper.findAllUsers

        // 1.通过数据源得到连接对象
        Configuration configuration = new Configuration();
        // 2.得到要执行的sql语句
        Mapper mapper = configuration.getMappers().get(allName);
        // 得到返回值类型
        String resultType = mapper.getResultType();
        Class cls = Class.forName(resultType);
        // 得到sql语句
        String sql = mapper.getSql();
        Connection conn = configuration.getDataSource().getConnection();
        // 3.执行数据库操作,封装结果集并且返回
        return query(conn, sql, cls);
    }

    // 执行sql语句,去数据库查询数据
    public <T> List<T> query(Connection conn, String sql, Class<T> returnType) {
        // 创建集合存储查询后的对象
        ArrayList<T> list = new ArrayList<>();
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            // 执行sql语句
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();

            // 得到查询结果的元信息,包括字段个数
            ResultSetMetaData metaData = rs.getMetaData();

            // 循环获取每条记录,转成对象
            while (rs.next()) {
                // 反射创建对象
                T obj = returnType.getConstructor().newInstance();

                // 得到字段的个数
                for (int i = 1; i <= metaData.getColumnCount(); i++) {
                    // 获取字段的名称
                    String columnName = metaData.getColumnName(i);
                    // 通过字段的名称获取字段的值
                    Object value = rs.getObject(columnName);
                    // 通过反射得到类中的成员变量
                    Field field = returnType.getDeclaredField(columnName);
                    field.setAccessible(true);
                    // 给对象的这个成员变量设置值
                    field.set(obj, value);
                }

                // 将一个转换好的对象添加到集合中
                list.add(obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(conn, pstmt, rs);
        }

        // 返回集合
        return list;
    }

    /**
     * 关闭连接对象,语句对象,结果集对象
     */
    public void close(Connection connection, Statement stmt, ResultSet rs) {
        // 结果集不为空则关闭
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        // 不为空则关闭
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        // 连接对象不为空,则关闭
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

小结

[外链图片转存失败(img-jNYxaA1x-1564982999463)()]

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值