最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

day02-自己实现Mybatis底层机制-01

来源:博客园

自己实现Mybatis底层机制-01

主要实现:封装SqlSession到执行器+Mapper接口和Mapper.xml+MapperBean+动态代理Mapper的方法


【资料图】

1.Mybatis整体架构分析

对上图的解读:

1)mybatis 的核心配置文件

​mybatis-config.xml:进行全局配置,全局只能有一个这样的配置文件

​XxxMapper.xml 配置多个SQL,可以有多个 XxxMapper.xml 配置文件

2)通过 mybatis-config.xml 配置文件得到 SqlSessionFactory

3)通过 SqlSessionFactory 得到 SqlSession,用 SqlSession 就可以操作数据了

4)SqlSession 底层是 Executor(执行器),有两个重要的实现类

5)MappedStatement 是通过 XxxMapper.xml 来定义的,用来生成 statement 对象

6)参数输入执行并输出结果集,无需动手判断参数类型和参数下标位置,且自动将结果集映射为Java对象

2.搭建开发环境

(1)创建maven项目

(2)在pom.xml 中引入必要的依赖

    UTF-8    1.8    1.8    1.8                dom4j        dom4j        1.6.1                    mysql        mysql-connector-java        5.1.49                    org.projectlombok        lombok        1.18.4                    junit        junit        4.12    

(3)创建数据库和表

-- 创建数据库CREATE DATABASE `li_mybatis`;USE `li_mybatis`;-- 创建monster表CREATE TABLE `monster`(`id` INT NOT NULL AUTO_INCREMENT,`age` INT NOT NULL,`birthday` DATE DEFAULT NULL,`email` VARCHAR(255) NOT NULL,`gender` TINYINT NOT NULL,-- 1 male,0 female`name` VARCHAR(255) NOT NULL,`salary` DOUBLE NOT NULL,PRIMARY KEY(`id`))CHARSET=utf8-- insertINSERT INTO `monster` VALUES(NULL,200,"2000-11-11","nmw@qq.com",1,"牛魔王",8888);

3.设计思路

解读:

  1. 传统的方式操作数据库1)得到 MySession 对象2)调用 MyExecutor 的方法完成操作3)MyExecutor 的连接是从 MyConfiguration 获取

  2. Mybatis 操作数据库的方式1)得到 MySession 对象2)不直接调用 MyExecutor 的方法3)而是通过 MyMapperProxy 获取 Mapper 对象4)调用 Mapper 的方法,完成对数据库的操作5)Mapper 最终还是动态代理方式,使用 MyExecutor 的方法完成操作6)这里比较麻烦的就是 MyMapperProxy 的动态代理机制如何实现

4.任务阶段1

阶段1任务:通过配置文件,获取数据库连接

4.1分析

4.2代码实现

(1)在src 的 resources目录下创建 my-config.xml,模拟原生的 mybatis 配置文件

                    

(2)创建 MyConfiguration 类,用来读取xml文件,建立连接

因为这里重点是实现 Mybatis 的底层机制,为了简化操作,就不使用数据库连接池了,直接使用原生的connection 连接

package com.li.limybatis.sqlsession;import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.SAXReader;import java.io.InputStream;import java.sql.Connection;import java.sql.DriverManager;/** * @author 李 * @version 1.0 * 用来读取xml文件,建立连接 */public class MyConfiguration {    //属性-类的加载器    private static ClassLoader loader = ClassLoader.getSystemClassLoader();    //读取xml文件并处理    public Connection build(String resource) {        Connection connection = null;        try {            //先加载配置文件 my-config.xml,获取对应的InputStream            InputStream stream = loader.getResourceAsStream(resource);            //解析 my-config.xml文件            SAXReader reader = new SAXReader();            Document document = reader.read(stream);            //获取 xml文件的根元素             Element root = document.getRootElement();            System.out.println("root=" + root);            //根据root解析,获取Connection            connection = evalDataSource(root);        } catch (Exception e) {            e.printStackTrace();        }        return connection;    }    //解析 my-config.xml 的信息,并返回 Connection    private Connection evalDataSource(Element node) {        if (!"database".equals(node.getName())) {            throw new RuntimeException("root节点应该是");        }        //连接DB的必要参数        String driverClassName = null;        String url = null;        String username = null;        String password = null;        //遍历node下的子节点,获取其属性值        for (Object item : node.elements("property")) {            //i就是对应的 property节点            Element i = (Element) item;            //property节点的 name属性的值            String name = i.attributeValue("name");            //property节点的 value属性的值            String value = i.attributeValue("value");            //判断值是否为空            if (name == null || value == null) {                throw new RuntimeException("property节点没有设置name或value属性!");            }            switch (name) {                case "url":                    url = value;                    break;                case "username":                    username = value;                    break;                case "driverClassName":                    driverClassName = value;                    break;                case "password":                    password = value;                    break;                default:                    throw new RuntimeException("属性名没有匹配到..");            }        }        //获取连接        Connection connection = null;        try {            Class.forName(driverClassName);            connection = DriverManager.getConnection(url, username, password);        } catch (Exception e) {            e.printStackTrace();        }        return connection;    }}

5.任务阶段2

阶段2任务:通过实现执行器机制,对数据表进行操作

5.1分析

我们把对数据库的操作封装到一套Executor机制中,程序具有更好的拓展性,结构更加清晰。这里我们先实现传统的方式连接数据库,即通过MyExecutor直接操作数据库。

5.2代码实现

(1)生成 entity 类 Monster.java

package com.li.entity;import lombok.*;import java.util.Date;/** * @author 李 * @version 1.0 * Monster类和 monster有映射关系 * * 注解说明: * @Getter 给所有属性生成 getter方法 * @Setter 给所有属性生成 setter方法 * @ToString 生成toString方法 * @NoArgsConstructor 生成一个无参构造器 * @AllArgsConstructor 生成一个全参构造器 * @Data 会生成上述除了无参/全参构造器的所有方法,此外还会生成equals,hashCode等方法 */@Getter@Setter@ToString@NoArgsConstructor@AllArgsConstructorpublic class Monster {        private Integer id;    private Integer age;    private String name;    private String email;    private Date birthday;    private double salary;    private Integer gender;}

(2)Executor 接口

package com.li.limybatis.sqlsession;/** * @author 李 * @version 1.0 */public interface Executor {    //泛型方法    public  T query(String statement, Object parameter);}

(3)执行器实现类 MyExecutor.java

package com.li.limybatis.sqlsession;import com.li.entity.Monster;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;/** * @author 李 * @version 1.0 */public class MyExecutor implements Executor {    private MyConfiguration myConfiguration = new MyConfiguration();    /**     * 根据sql,返回查询结果     *     * @param sql     * @param parameter     * @param      * @return     */    @Override    public  T query(String sql, Object parameter) {        //获取连接对象        Connection connection = getConnection();        //查询返回的结果集        ResultSet set = null;        PreparedStatement pre = null;        try {            //构建PreparedStatement对象            pre = connection.prepareStatement(sql);            //设置参数,如果参数多,可以使用数组处理            pre.setString(1, parameter.toString());            //查询返回的结果集            set = pre.executeQuery();            //把结果集的数据封装到对象中-monster            //说明:这里做了简化处理,认为返回的结果就是一个monster记录,完善的写法应该使用反射机制            Monster monster = new Monster();            //遍历结果集,将数据封装到monster对象中            while (set.next()) {                monster.setId(set.getInt("id"));                monster.setName(set.getString("name"));                monster.setEmail(set.getString("email"));                monster.setAge(set.getInt("age"));                monster.setGender(set.getInt("gender"));                monster.setBirthday(set.getDate("birthday"));                monster.setSalary(set.getDouble("salary"));            }            return (T) monster;        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                if (set != null) {                    set.close();                }                if (pre != null) {                    pre.close();                }                if (connection != null) {                    connection.close();                }            } catch (Exception e) {                e.printStackTrace();            }        }        return null;    }    //编写方法,通过myConfiguration对象返回连接    private Connection getConnection() {        Connection connection = myConfiguration.build("my-config.xml");        return connection;    }}

(4)进行测试

@Testpublic void query() {    Executor executor = new MyExecutor();    Monster monster =            (Monster) executor.query("select * from monster where id = ?", 1);    System.out.println("monster--" + monster);}

测试结果:

6.任务阶段3

阶段3任务:将执行器封装到SqlSession

6.1代码实现

(1)创建 MySqlSession 类,将执行器封装到SqlSession中。

package com.li.limybatis.sqlsession;/** * @author 李 * @version 1.0 * MySqlSession:搭建Configuration(连接)和Executor之间的桥梁 */public class MySqlSession {    //执行器    private Executor executor = new MyExecutor();    //配置    private MyConfiguration myConfiguration = new MyConfiguration();    //编写方法selectOne,返回一条记录    public  T selectOne(String statement,Object parameter){        return executor.query(statement, parameter);    }}

(2)测试

@Testpublic void selectOne() {    MySqlSession mySqlSession = new MySqlSession();    Monster monster =            (Monster) mySqlSession.selectOne("select * from monster where id=?", 1);    System.out.println("monster=" + monster);}

测试结果:

关键词: 配置文件 任务阶段 设计思路