SpringBoot中间件—ORM(Mybatis)框架实现

news2024/11/26 2:54:50

目录

定义

需求背景

方案设计

代码展示

UML图

 实现细节

测试验证

 总结


定义:

        ORM:Object Relational Mapping  -->  对象关系映射,是一种程序设计技术,用于实现面向对象编程语言里面不同类型系统的数据之间的转换

需求背景:

        记不记得刚开始学JAVA时,编写一大串JDBC相关代码来进行与数据库的交互,日后我们接触到的MyBatis、MyBatisPlus等都是使用ORM组件来实现的框架。          

        本篇文章提炼出mybatis【最】经典、【最】精简、【最】核心的代码设计,来实现一个【mini-mybatis】,从而熟悉并掌握ORM框架的涉及实现。

方案设计:

        

  •  中间的四部分处理是ORM框架的核心内容
  • 这个框架会提供出SqlSession工厂以及调用方式

代码展示

UML图

很重要,建议code前跟我一样,先将类UML图整理出来,整个类的依赖关系及代码执行流程会一目而然。

篇幅有限,展开观看

  • 以上为ORM框架实现核心类:加载mysql配置文件、对mapper-xml解析、获取数据库session、操作数据库及封装响应结果。

 实现细节

1.定义sqlsession接口

对数据库的定义和处理,本篇我们只封装一个 T selectOne(Object param);

public interface SqlSession {

    <T> T selectOne(String statement, Object parameter);

    void close();
}

2.DefaultSqlSession(SqlSession的实现)

使用rt.jar包下(java.lang.sql包下)

Connection接口(负责与数据库进行连接)及PreparedStatement(执行具体sql)接口来实现

public class DefaultSqlSession implements SqlSession{

    private Connection connection;

    private Map<String,XNode> mapperElement;

    public DefaultSqlSession(Connection connection, Map<String, XNode> mapperElement) {
        this.connection = connection;
        this.mapperElement = mapperElement;
    }

    @Override
    public <T> T selectOne(String statement, Object parameter) {
        XNode xNode = mapperElement.get(statement);
        Map<Integer, String> parameterMap = xNode.getParameter();
        try {
            PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());
            buildParameter(preparedStatement, parameter, parameterMap);
            // SQL执行结果集的行数据
            ResultSet resultSet = preparedStatement.executeQuery();
            List<T> objects = resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));
            return objects.get(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz) {
        List<T> list = new ArrayList<>();
        try {
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            // 每次遍历行值
            while (resultSet.next()) {
                T obj = (T) clazz.newInstance();
                for (int i = 1; i <= columnCount; i++) {
                    Object value = resultSet.getObject(i);
                    String columnName = metaData.getColumnName(i);
                    String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);
                    Method method;
                    if (value instanceof Timestamp) {
                        method = clazz.getMethod(setMethod, Date.class);
                    } else {
                        method = clazz.getMethod(setMethod, value.getClass());
                    }
                    method.invoke(obj, value);
                }
                list.add(obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }
    

    @Override
    public void close() {
        if (null == connection) return;
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    private void buildParameter(PreparedStatement preparedStatement, Object parameter, Map<Integer, String> parameterMap) throws SQLException, IllegalAccessException {

        int size = parameterMap.size();
        // 单个参数
        if (parameter instanceof Long) {
            for (int i = 1; i <= size; i++) {
                preparedStatement.setLong(i, Long.parseLong(parameter.toString()));
            }
            return;
        }else{
            // TODO 后面紧跟的章节继续补充其他类型的入参
        }
        
    }
}

3.定义SqlSessionFactory接口

        每次执行一个SQL语句,应用程序都需要获取一个SqlSession对象。SqlSession对象是执行持久化操作的入口点,可以用于执行SQL语句、刷新缓存、提交事务等操作。建议在使用完SqlSession后,及时关闭它来释放资源。

public interface SqlSessionFactory {

    SqlSession openSession();
}

4.DefaultSqlSessionFactory(上述接口实现类)

        构造方法中向下传递了Configuration配置文件

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSession() {
        return new DefaultSqlSession(configuration.getConnection(), configuration.getMapperElement());
    }
}

5.SqlSessionFactoryBuilder

        数据库操作的核心类,负责解析Mapper文件(拿datasource,数据库连接信息,mapper文件中sql的各个信息如id,入返参类型,sql)

public class SqlSessionFactoryBuilder {

    public DefaultSqlSessionFactory build(Reader reader) {
        SAXReader saxReader = new SAXReader();
        Document document = null;
        try {
            document = saxReader.read(new InputSource(reader));
            // 拿到根标签元素
            Element rootElement = document.getRootElement();
            Configuration configuration = parseConfiguration(rootElement);
            return new DefaultSqlSessionFactory(configuration);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return null;
    }

    public Configuration parseConfiguration(Element rootElement) {
        Configuration configuration = new Configuration();
        configuration.setDataSource(dataSource(rootElement.selectNodes("//dataSource")));
        configuration.setConnection(connection(configuration.getDataSource()));
        configuration.setMapperElement(mapperElement(rootElement.selectNodes("//mappers")));
        return configuration;
    }

    private Map<String, String> dataSource(List<Element> list) {
        Map<String, String> dataSource = new HashMap<>(4);
        Element element = list.get(0);
        List content = element.content();
        for (Object o : content) {
            Element e = (Element) o;
            String name = e.attributeValue("name");
            String value = e.attributeValue("value");
            dataSource.put(name, value);
        }
        return dataSource;
    }

    private Connection connection(Map<String, String> dataSource) {
        try {
            return DriverManager.getConnection(dataSource.get("url"), dataSource.get("username"), dataSource.get("password"));
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    private Map<String, XNode> mapperElement(List<Element> list) {
        Map<String, XNode> map = new HashMap<>();
        Element element = list.get(0);
        List content = element.content();
        try {
            for (Object o : content) {
                Element e = (Element) o;
                // 拿到mapper文件对应地址
                String resource = e.attributeValue("resource");
                Reader reader = Resources.getResourceAsReader(resource);
                SAXReader saxReader = new SAXReader();
                Document document = saxReader.read(new InputSource(reader));
                Element rootElement = document.getRootElement();
                String namespace = rootElement.attributeValue("namespace");
                List<Element> selectNodes = rootElement.selectNodes("select");
                for (Element ele : selectNodes) {
                    String id = ele.attributeValue("id");
                    String parameterType = ele.attributeValue("parameterType");
                    String resultType = ele.attributeValue("resultType");
                    String sql = ele.getText();
                    // ? 匹配
                    Map<Integer, String> parameter = new HashMap<>();
                    Pattern pattern = Pattern.compile("(#\\{(.*?)})");
                    Matcher matcher = pattern.matcher(sql);
                    for (int i = 1; matcher.find(); i++) {
                        String g1 = matcher.group(1);
                        String g2 = matcher.group(2);
                        parameter.put(i, g2);
                        sql = sql.replace(g1, "?");
                    }
                    XNode xNode = new XNode();
                    xNode.setId(id);
                    xNode.setNameSpace(namespace);
                    xNode.setParameterType(parameterType);
                    xNode.setResultType(resultType);
                    xNode.setSql(sql);
                    xNode.setParameter(parameter);
                    map.put(namespace + "." + id, xNode);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return map;
    }
}

测试验证

建表

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint(20) NOT NULL COMMENT '自增id',
  `userId` varchar(9) DEFAULT NULL COMMENT '用户ID',
  `userNickName` varchar(32) DEFAULT NULL COMMENT '用户昵称',
  `userHead` varchar(255) DEFAULT NULL COMMENT '用户头像',
  `userPassword` varchar(255) DEFAULT NULL COMMENT '用户密码',
  `createTime` datetime DEFAULT NULL COMMENT '创建时间',
  `updateTime` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
BEGIN;
INSERT INTO `user` VALUES (1, '001', 'xxx', '001', '123', '2023-07-14 17:33:55', '2023-07-14 17:33:58');
INSERT INTO `user` VALUES (2, '002', 'xxx2', '002', '123', '2023-07-14 17:33:55', '2023-07-14 17:33:58');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

定义POJO及DAO

@Data
public class User {

    private Long id;
    private String userId;          // 用户ID
    private String userNickName;    // 昵称
    private String userHead;        // 头像
    private String userPassword;    // 密码
    private Date createTime;        // 创建时间
    private Date updateTime;        // 更新时间
}
public interface IUserDao {
     User queryUserInfoById(Long id);
}

ORM配置文件--mybatis-config-datasource.xml

<?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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://172.17.1.245:3306/airticketbasedb?useUnicode=true"/>
                <property name="username" value="write"/>
                <property name="password" value="write123"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="mapper/User_Mapper.xml"/>
    </mappers>

</configuration>

Mapper配置

UserMapper.xml

<?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="com.example.minimybatis.dao.IUserDao">

    <select id="queryUserInfoById" parameterType="java.lang.Long" resultType="com.example.minimybatis.po.User">
        SELECT id, userId, userNickName, userHead, userPassword, createTime
        FROM user
        where id = #{id}
    </select>

</mapper>

 测试类

public class ApiTest {
    @Test
    public void test(){
        String resouce = "mybatis-config-datasource.xml";
        Reader reader;
        try{
            reader = Resources.getResourceAsReader(resouce);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            User user = sqlSession.selectOne(
                    "com.example.minimybatis.dao.IUserDao.queryUserInfoById",
                    1L);
            System.out.println(JSONObject.toJSONString(user));
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

 总结

比mybatis小很多,取其(mybaits)精华来达到掌握ORM框架的目的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/760339.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端|CSS(一)| pink老师

参考视频&#xff1a;黑马程序员前端CSS3基础教程&#xff0c;前端必备基础 目录 &#x1f4da;CSS引入 &#x1f407;引入CSS样式表 &#x1f4da;CSS基础选择器 &#x1f407;标签选择器 &#x1f407;类选择器&#xff08;最常用&#xff09; &#x1f407;id选择器 …

VIM文本如何复制到系统剪切板?

今天从vim上用鼠标复制代码&#xff0c;发现把VIM当中的行号也复制进去了&#xff0c;就很麻烦&#xff0c;于是简单研究了下&#xff0c;如果vim支持clipboard的话就比较好办&#xff0c;具体支持与否&#xff0c;使用命令查看&#xff1a; vim --version | grep "clipb…

C语言指针面试题

C语言指针面试题 一.指针和数组1.一维数组2.字符数组3.二维数组 二.指针 一.指针和数组 1.一维数组 int a[] {1,2,3,4};(1)printf("%d\n",sizeof(a));// 16(2)printf("%d\n",sizeof(a0));// 4/8(3)printf("%d\n",sizeof(*a));// 4(4)printf(&…

xred病毒分析

概述 xred病毒是一种感染型病毒&#xff0c;会感染系统中特定目录下的exe和xlsx文件&#xff0c;该病毒会将自身伪装成Synaptics触摸板驱动程序&#xff0c;使用Dephi编写。 样本的基本信息 Verified: Unsigned Link date: 6:22 1992/6/20 Company: Synaptics Description: S…

vue3 前端编码规范

prettier 配置 1. vscode 安装prettier 的 插件 2. 新建 .prettierrc 文件 {"semi": false, // 不尾随分号"singleQuote": true, // 使用单引号"trailingComma": "none" // 多行逗号分隔的语法&#xff0c;最后一行不加逗号 }eslin…

WPF嵌入外部exe应用程序-实现基本的嵌入

WPF嵌入外部exe应用程序 使用场景功能实现嵌入基本功能实现1.导入windows API2.运行外部程序3. 获取窗体句柄4. 嵌入窗体5.设置子窗体位置整个代码 嵌入存在的问题&#xff1a; 使用场景 在WPF桌面应用程序开发过程中&#xff0c;有时候需要将其他程序结合到一起&#xff0c;让…

Linux尖刀——shell

目录 知识点 lsblk grep awk tail du df 对新增存储设备的检测与分区 用lsblk查询块设备 用dmesg看内核打印信息 用ls查看新增设备 对rootfs空间使用情况的监控 知识点 首先想要用shell脚本解决一些问题肯定要熟悉linux的命令 lsblk -t或–tree&#xff1a;以…

Redis进阶底层原理- 缓冲区

Redis中使用了很多缓冲区&#xff0c;在redis各个环节起到了非常核心的作用。下面来一一介绍一下&#xff1a; 输入输出缓冲区&#xff08;客户端缓冲区&#xff09; Redis中的输入输出缓冲区是为了平衡客户端发送命令和服务端处理命令的速度差异&#xff0c;如果客户端发送指…

java用 postman输入 数字 会加上单引号 和逗号,方便查询

java用 postman输入 数字 会加上单引号 和逗号&#xff0c;方便查询 /*** 输入12575726* 891006* 12575726* 891006* 返回* 12575726,* 891006,* 12575726,* 891006* 方便查询数据* param data* return*/RequestMapping(value "l…

MySQL查看系统性能参数、统计SQL的查询成本last_query_cost的使用

1、查看MySQL系统性能参数 在MySQL中&#xff0c;可以使用SHOW STATUS语句查询一些MySQL数据库服务器的性能参数、执行频率。 语法如下&#xff1a; SHOW [ GLOBAL | SESSION] STATUS LIKE 参数; 一些常用的性能参数如下&#xff1a; Connections&#xff1a;连接MySQL服…

《银行法律法规》三、银行管理——2、公司治理、 内部控制与合规管理

第二章 公司治理、 内部控制与合规管理 第一节 公司治理 考点1 银行公司治理概述★★ 商业银行公司治理是指股东大会、 董事会、 监事会、 高级管理层、 股东及其他利益相关者之间的相互关系&#xff0c; 包括组织架构、 职责边界、 履职要求等治理制衡机制&#xff0c; 以…

从零到精通!50个必懂IT术语,让你成为行业翘楚!

您刚进入IT行业&#xff1f;不必惊慌&#xff01;我们为您整理了50个最常见的IT术语&#xff0c;从ICT到ITIL再到SLM&#xff0c;全方位解读&#xff0c;让您快速掌握这些关键概念。更令人兴奋的是&#xff0c;我们将向您介绍轻易云数据集成平台&#xff0c;它是您在数字化转型…

Redis进阶底层原理 - 分区算法方案

Redis分区是指将数据分散到不同的Redis实例&#xff0c;降低单个Redis实例内存和高并发请求的压力。为什么要做分区&#xff1a;目前来说前面所学知识都是基于Redis单实例上进行的&#xff0c;及时是哨兵模式也只是保证了单个Redis实例的可用性。当内存数据越来越多时单个Redis…

BOM编程

十四、BOM和DOM编程 windows对象 常见方法 _self&#xff1a;在当前页面打开窗口 _blank&#xff1a;打开一个新的选项卡 第一个参数是x轴&#xff0c;一般没有横向滚动条&#xff0c;都是操作第二个参数 history对象 属性&#xff1a;一般用不到 方法&#xff1a; 01.histor…

PCB规则设置

PCB设计规则 网络class设置间距规则设置线宽规则过孔规则设置铺铜规则设置其他规则设置 网络class设置 间距规则设置 进入规则设置 线宽规则 新建线宽选项&#xff0c;电源类 过孔规则设置 铺铜规则设置 其他规则设置

Django ORM Field源码解读

已models.py的CharField字段代码为起点 secret_id = models.CharField("secret_id", max_length=256, default="") 构造方法中,会去调父类Field 的构造方法,而后向 CharField 的验证器列表 中添加一个 MaxLengthValidator 对象,用于…

leecode 数据库:1084. 销售分析III

导入数据&#xff1a; Create table If Not Exists Product (product_id int, product_name varchar(10), unit_price int); Create table If Not Exists Sales (seller_id int, product_id int, buyer_id int, sale_date date, quantity int, price int); Truncate table Prod…

Elasticsearch中查询性能优化

Elasticsearch是一种流行的搜索引擎和分布式文档存储解决方案&#xff0c;它的高效性能和可伸缩性使其成为许多应用程序的首选存储引擎。在工作中&#xff0c;优化Elasticsearch的检索性能是一个非常重要的任务&#xff0c;可以大大提高应用程序的响应速度和用户体验。下面我们…

0131 物理层2

目录 2.物理层 2.2传输介质 2.3物理层设备 2.2&#xff0c;2.3部分习题 2.物理层 2.2传输介质 2.3物理层设备 2.2&#xff0c;2.3部分习题 1.利用一根同轴电缆互连主机构成以太网&#xff0c;则主机间得通信方式为&#xff08;&#xff09; A.全双工 B.半双工 …

PTA天梯赛的赛场安排

天梯赛使用 OMS 监考系统&#xff0c;需要将参赛队员安排到系统中的虚拟赛场里&#xff0c;并为每个赛场分配一位监考老师。每位监考老师需要联系自己赛场内队员对应的教练们&#xff0c;以便发放比赛账号。为了尽可能减少教练和监考的沟通负担&#xff0c;我们要求赛场的安排满…