orm框架设计、分析与开发

orm框架设计、分析与开发

前面写过几篇文章介绍和分析mybatis,今天拆解下要设计一个ORM框架涉及到哪些方面,如何用现有的一些已知工具像spring jdbc、freemarker等重新造一个ORM框架出来,整体的拆解结构如图所示。

该ORM框架源码有兴趣的可以评论区留言备注下邮箱找我要下,因为是个人写的,所以不可能面面俱到,但是ORM框架基本的功能具备,使用也非常方便,会freemarker语法的话一看就会用,使用起来感觉应该比mybatis更容易。

在这里插入图片描述

一、准备阶段

准备阶段是指sql操作前,例如容器启动的时候需要做的一些事情,主要包括两个部分

1、解析映射

将sql xml中sqlid和sql操作语句之间的关联关系建立起来,后面sql操作的时候就可以直接根据sqlid 找到对应的sql语句

2、数据源映射

将数据库和数据源之间的关联关系建立起来,后面指定某个库时就可以直接找到对应的数据源来获取连接。

二、执行阶段

执行阶段就是真正的sql执行了,原生的jdbc sql操作主要包括几个部分,驱动加载、设置url用户名和密码、获取数据库连接,创建statement、执行sql操作、结果集处理,最后关闭。

1、sql绑定

准备阶段中保存的sql语句是带有一些特殊标记的像if、for之类的标签,需要根据入参的值来处理sql将这些标签去除,freemarker模板框架就能够满足这个要求。

2、数据库连接池

可以选c3p0、druid、dbcp

3、sql操作工具

选择spring jdbc template来支持

4、对象映射

结果集转换成对应的类型对象,或者对象装换成参数Map,可以通过commons-beanutils实现

三、使用示例

因为是在spring jdbc的基础上开发的orm框架,所以采用spring xml配置的方式进行配置

1、单数据源实例

spes-da-default.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--直接配置数据库连接-->
    <bean id="testDbDs" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/test"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>

    <!-- DAL客户端接口实现 -->
    <bean id="sqlSession"
          class="com.handserome.daat.session.DefaultSqlSession">
        <!--这个地方设置sql map文件的路径-->
        <property name="sqlMapConfigLocation" value="classpath*:conf/sqlMap/sqlMap_*.xml"/>
        <property name="dataSource" ref="testDbDs"/>
    </bean>

</beans>

sql map映射文件:包含增删改查的示例

<?xml version="1.0" encoding="UTF-8" ?>
<sqlMap namespace="student" jdbcTimeout="60">

    <!--查询 -->
    <sql id="queryStudentById">
        <![CDATA[
    		SELECT *
			FROM ${tableName}
    	   WHERE  id = :id
    	   <#if name?? && name != "">
    	   AND name = :name
    	   </#if>
    	]]>
    </sql>

    <!-- 根据id批量查询 -->
    <sql id="queryStudentByIds">
        <![CDATA[
    		SELECT *
			FROM ${tableName}
    	   WHERE id in (
                <#list idList as ids>
						:id${ids_index}
					<#if ids_has_next>,</#if>
				</#list>
    	   )
    	]]>
    </sql>

    <!-- 删除 -->
    <sql id="deleteData">
        <![CDATA[
    		DELETE FROM student
			 WHERE name = :name
    	]]>
    </sql>

    <!-- 保存 -->
    <sql id="saveData">
        <![CDATA[
	    	INSERT INTO student
   			(student_id, name)
    		VALUES
    		(:studentId,:name)
    	]]>
    </sql>

    <!-- 更新 -->
    <sql id="batchUpdate">
        <![CDATA[
	    	update student
   			set name = :name
   			<#if createTime?? && createTime != "">
   			,create_time = :createTime
   			</#if>
   			where student_id = :studentId
    	]]>
    </sql>

</sqlMap>

数据库操作实例

package com.handserome.test;
import java.sql.Timestamp;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.handserome.daat.session.SqlSession;
import com.handserome.test.entity.Student;

/**
 * Hello world!
 *
 */
public class SqlSessionTest {
    
    

    private static SqlSession sqlSession;

    private static SqlSession sqlSession1;

    public static void main(String[] args) {
    
    
        // 默认,非分库
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:daat-da-default.xml");
        sqlSession = (SqlSession) context.getBean("sqlSession");

        // 可以灵活指定查询的数据库分库
        ClassPathXmlApplicationContext context1 = new ClassPathXmlApplicationContext("classpath:daat-da-shard.xml");
        sqlSession1 = (SqlSession) context1.getBean("sqlSession");

        // 默认非分库测试
        queryForObj();
        queryForList();
        delete();
        insert();
        insert1();
        batchUpdateTest();

        // 分库测试
        queryForObjShard();
        queryForListShard();
        deleteShard();
        insertShard();
        insertShard1();
        batchUpdateShardTest();
    }

    /**
     *
     * 功能描述: 查询对象
     *
     * @param
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private static void queryForObj() {
    
    
        String sqlId1 = "student.queryStudentById";
        Map<String, Object> paramMap1 = new HashMap<String, Object>();
        paramMap1.put("id", 1);
        // paramMap.put("name", "111");
        paramMap1.put("tableName", "student");
        Student student1 = sqlSession.queryForObject(sqlId1, paramMap1, Student.class);
        System.out.println(student1.toString());
    }

    /**
     *
     * 功能描述: 批量查询
     *
     * @param
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private static void queryForList() {
    
    
        Map<String, Object> paramMap = new HashMap<String, Object>();
        List<Integer> idList = new ArrayList<Integer>();
        for (int i = 0; i < 4; i++) {
    
    
            paramMap.put("id" + i, i);
            idList.add(i);
        }
        paramMap.put("idList", idList);
        paramMap.put("tableName", "student");
        String sqlId = "student.queryStudentByIds";
        List<Student> studentList = sqlSession.queryForList(sqlId, paramMap, Student.class);
        for (Student student : studentList) {
    
    
            System.out.println("根据id批量查询" + student);
        }
    }

    /**
     *
     * 功能描述: 删除
     *
     * @param
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private static void delete() {
    
    
        Map<String, Object> paramMap12 = new HashMap<String, Object>();
        paramMap12.put("name", "aaa");
        String deleteData = "student.deleteData";
        int execute = sqlSession.execute(deleteData, paramMap12);
        System.out.println("删除条数" + execute);
    }

    /**
     * 插入
     */
    private static void insert() {
    
    
        Map<String, Object> paramMap11 = new HashMap<String, Object>();
        paramMap11.put("studentId", "222");
        paramMap11.put("name", "bbb");
        String insert = "student.saveData";
        int execute = sqlSession.execute(insert, paramMap11);
        System.out.println("插入条数" + execute);
    }

    /**
     *
     * 功能描述: 批量插入
     *
     * @param
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private static void insert1() {
    
    
        List<Object> objectList = new ArrayList<Object>();
        Student student = new Student();
        student.setStudentId("7878");
        student.setName("7878");
        student.setCreateTime(new Timestamp(new java.util.Date().getTime()));

        objectList.add(student);

        Student student1 = new Student();
        student1.setStudentId("78781");
        student1.setName("78781");
        student1.setCreateTime(new Timestamp(new java.util.Date().getTime()));

        objectList.add(student1);
        String insert = "student.saveData";
        int i = sqlSession.batchUpdate(insert, objectList);
        System.out.println("插入条数" + i);
    }

    /**
     *
     * 功能描述: 批量更新
     *
     * @param
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private static void batchUpdateTest() {
    
    
        List<Object> studentList = new ArrayList<Object>();
        for (int i = 0; i < 5; i++) {
    
    
            Student student = new Student();
            student.setStudentId("" + i);
            student.setName("name" + i);
            if ((i % 2) ==0) {
    
    
                student.setCreateTime(new Timestamp(new java.util.Date().getTime()));
            }
            studentList.add(student);
        }
        String sqlId = "student.batchUpdate";
        int i = sqlSession.batchUpdate(sqlId, studentList);
        System.out.println("批量更新条数" + i);
    }



    /**
     *
     * 功能描述: 查询对象
     *
     * @param
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private static void queryForObjShard() {
    
    
        String sqlId1 = "student.queryStudentById";
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("id", 1);
        // paramMap.put("name", "111");
        paramMap.put("tableName", "student");
        // 指定数据库
        paramMap.put("dbKey", "db1");
        Student student1 = sqlSession1.queryForObject(sqlId1, paramMap, Student.class);
        System.out.println(student1.toString());
    }

    /**
     *
     * 功能描述: 批量查询
     *
     * @param
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private static void queryForListShard() {
    
    
        Map<String, Object> paramMap = new HashMap<String, Object>();
        List<Integer> idList = new ArrayList<Integer>();
        for (int i = 0; i < 4; i++) {
    
    
            paramMap.put("id" + i, i);
            idList.add(i);
        }
        paramMap.put("idList", idList);
        paramMap.put("tableName", "student");
        // 指定数据库
        paramMap.put("dbKey", "db1");
        String sqlId = "student.queryStudentByIds";
        List<Student> studentList = sqlSession1.queryForList(sqlId, paramMap, Student.class);
        for (Student student : studentList) {
    
    
            System.out.println("根据id批量查询" + student);
        }
    }

    /**
     *
     * 功能描述: 删除
     *
     * @param
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private static void deleteShard() {
    
    
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("name", "aaa");
        // 指定数据库
        paramMap.put("dbKey", "db1");
        String deleteData = "student.deleteData";
        int execute = sqlSession1.execute(deleteData, paramMap);
        System.out.println("删除条数" + execute);
    }

    /**
     * 插入
     */
    private static void insertShard() {
    
    
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("studentId", "222");
        paramMap.put("name", "bbb");
        paramMap.put("dbKey", "db1");
        String insert = "student.saveData";
        int execute = sqlSession1.execute(insert, paramMap);
        System.out.println("插入条数" + execute);
    }

    /**
     *
     * 功能描述: 批量插入
     *
     * @param
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private static void insertShard1() {
    
    
        List<Object> objectList = new ArrayList<Object>();
        Student student = new Student();
        student.setStudentId("7878");
        student.setName("7878");
        student.setCreateTime(new Timestamp(new java.util.Date().getTime()));
        student.setDbKey("db1");
        objectList.add(student);

        Student student1 = new Student();
        student1.setStudentId("78781");
        student1.setName("78781");
        student1.setDbKey("db1");
        student1.setCreateTime(new Timestamp(new java.util.Date().getTime()));

        objectList.add(student1);
        String insert = "student.saveData";
        int i = sqlSession1.batchUpdate(insert, objectList);
        System.out.println("插入条数" + i);
    }

    /**
     *
     * 功能描述: 批量更新
     *
     * @param
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private static void batchUpdateShardTest() {
    
    
        List<Object> studentList = new ArrayList<Object>();
        for (int i = 0; i < 5; i++) {
    
    
            Student student = new Student();
            student.setStudentId("" + i);
            student.setName("name" + i);
            student.setDbKey("db1");

            if ((i % 2) ==0) {
    
    
                student.setCreateTime(new Timestamp(new java.util.Date().getTime()));
            }
            studentList.add(student);
        }
        String sqlId = "student.batchUpdate";
        int i = sqlSession1.batchUpdate(sqlId, studentList);
        System.out.println("批量更新条数" + i);
    }

}

2、多数据源实例配置

多数据源配置类daat-da-shard.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--直接配置数据库连接-->
    <bean id="testDbDs1" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/test"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>

    <!--直接配置数据库连接-->
    <bean id="testDbDs2" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/test1"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>

    <bean id="shardRegister"
          class="com.handserome.daat.datasource.DefaultDbShardRegister">
        <property name="dbShardInfos">
            <set>
                <!-- 测试库1 -->
                <bean class="com.handserome.daat.datasource.DbShardInfo">
                    <property name="id" value="db1"/>
                    <property name="dataSource" ref="testDbDs1"/>
                    <property name="description" value="测试1库数据源"/>
                </bean>
                <!-- 测试库2 -->
                <bean class="com.handserome.daat.datasource.DbShardInfo">
                    <property name="id" value="db2"/>
                    <property name="dataSource" ref="testDbDs2"/>
                    <property name="description" value="测试1库数据源"/>
                </bean>
            </set>
        </property>
    </bean>

    <!-- DAL客户端接口实现 -->
    <bean id="sqlSession"
          class="com.handserome.daat.session.DbShardSqlSession">
        <property name="dbShardRegister" ref="shardRegister"/>
        <property name="sqlMapConfigLocation" value="classpath*:conf/sqlMap/sqlMap_*.xml"/>
    </bean>

</beans>

sql map映射文件和java测试类参考单数源实例

3、结果展示

数据库就涉及一个student表,默认支持字段和对像驼峰映射,应此也不用想mybatis一样写一堆resultType 映射。

CREATE TABLE `student` (
  `student_id` int(11) DEFAULT NULL,
  `name` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

数据库操作正确

[id=1, studentId=1, name=name1, createTime=null]
根据id批量查询[id=1, studentId=1, name=name1, createTime=null]
根据id批量查询[id=2, studentId=222, name=bbb, createTime=null]
根据id批量查询[id=3, studentId=7878, name=7878, createTime=null]
删除条数0
插入条数1
插入条数2
批量更新条数1
[id=1, studentId=1, name=name1, createTime=null]
根据id批量查询[id=1, studentId=1, name=name1, createTime=null]
根据id批量查询[id=2, studentId=222, name=bbb, createTime=null]
根据id批量查询[id=3, studentId=7878, name=7878, createTime=null]
删除条数0
插入条数1
插入条数2
批量更新条数1

猜你喜欢

转载自blog.csdn.net/khuangliang/article/details/108437794