第三篇 - 手寫ORM框架

在這裏插入圖片描述
Github源碼下載地址:https://github.com/chenxingxing6/myorm
CSDN源碼下載地址:https://download.csdn.net/download/m0_37499059/11783237

在這裏插入圖片描述

一、前言

ORM對象關係映射(Object Relational Mapping),用於實現面向對象編程語言裏不通類型系統的數據之間進行轉換。簡單來說,ORM就是通過使用描述對象和數據庫之間映射的元數據,將程序中的對象與關係數據庫進行相互映射。
在這裏插入圖片描述
在這裏插入圖片描述


二、先簡單瞭解一下Mybatis

在這裏插入圖片描述

MyBatis 是一款優秀的持久層框架,它支持定製化 SQL、存儲過程以及高級映射。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可以使用簡單的 XML 或註解來配置和映射原生信息,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數據庫中的記錄。

1.SqlSessionFactory是線程安全的
2.qlSession是單線程對象,因爲它是非線程安全的

在這裏插入圖片描述
流程描述:

1.加載Mybatis全局配置文件並解析,生成Configuration對象和MapperdStatement
2.SqlSessionFactoryBuilder通過Configuration對象構建SqlSessionFactory
3.通過SqlSessionFactory獲取sqlSession
4.sqlSession和數據庫進行交互


三、MyORM實現

主要完成的功能【基本CURD是可以支持的】

1.通過自己實現的ORM,進行增刪改查demo
2.@Param註解解析,支持註解到對象和基本數據類型
3.用dtd文件定義Mapper.xml文檔的合法構建模塊
4.根據resultType,對結果進行處理
5.除了xml配置方式外,新加註解方式@Select @Insert @Delete @Update

我的實現思路

1.解析配置文件,初始化數據庫連接,創建sqlSession池,交給SqlSessionFactory管理
2.創建Execute,底層調用JDBC操作數據庫
3.創建MapperProxy代理對象,動態代理Mapper接口
4.大體架子搭建好後,可以繼續完善,比如@Param註解.
5.測試就直接使用單測測試就可以了


四、MyORM項目結構

在這裏插入圖片描述

CREATE TABLE `sys_role` (
  `role_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(100) DEFAULT NULL COMMENT '角色名稱',
  `remark` varchar(100) DEFAULT NULL COMMENT '備註',
  `dept_id` bigint(20) DEFAULT NULL COMMENT '部門ID',
  `create_time` datetime DEFAULT NULL COMMENT '創建時間',
  PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='角色'

INSERT INTO cloud_disk.sys_role (role_id, role_name, remark, dept_id, create_time) VALUES (1, '超級管理員', '最高權限', 34, '2018-07-31 19:27:42');
INSERT INTO cloud_disk.sys_role (role_id, role_name, remark, dept_id, create_time) VALUES (2, '管理員', '權限比較少', 9, '2018-07-31 19:28:58');
INSERT INTO cloud_disk.sys_role (role_id, role_name, remark, dept_id, create_time) VALUES (3, 'IT經理', 'IT用

4.1 增刪改查Demo

在這裏插入圖片描述
在這裏插入圖片描述

package com.demo;

import com.alibaba.fastjson.JSON;
import com.test.entry.Role;
import com.test.mapper.RoleMapper;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;

import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author: cxx
 * @Date: 2019/9/18 12:42
 */
public class MapperTest {
    private RoleMapper roleMapper;

    @Before
    public void init(){
        // 使用弱引用創建SqlSessionFactoryBuilder,保證下次GC時回收該對象。
        WeakReference<SqlSessionFactoryBuilder> builder = new WeakReference<>(new SqlSessionFactoryBuilder());
        String mapxmlPath = "mapper";
        SqlSessionFactory factory = builder.get().build(mapxmlPath);
        roleMapper = factory.getMapper(RoleMapper.class);
    }

    /**
     * 查詢(普通 & 有@param註解)
     */
    @Test
    public void test01(){
        Role role1 = roleMapper.getRoleById(1L);
        System.out.println("普通方式:"+JSON.toJSONString(role1));

        Role role2 = roleMapper.getRoleByIdAndDeptId(1L, 34L);
        System.out.println("@Param註解:"+JSON.toJSONString(role2));
    }

    /**
     * 刪除
     */
    @Test
    public void test02(){
        int result = roleMapper.deleteById(36L);
        System.out.println(result >= 1 ? "刪除成功" : "刪除失敗");
    }

    /**
     * 插入
     */
    @Test
    public void test03(){
        Role role = new Role();
        role.setRoleId(Long.valueOf(new Random().nextInt(100)));
        role.setDeptId(1L);
        role.setRemark("remark");
        role.setRoleName("roleName");
        role.setCreateTime(new Date());
        int result = roleMapper.insert(role);
        System.out.println(result >= 1 ? "插入成功" : "插入失敗");
    }

    /**
     * 修改
     */
    @Test
    public void test04(){
        int result = roleMapper.updateRoleName(10L, "update remark");
        System.out.println(result >= 1 ? "修改成功" : "修改失敗");
    }
}

4.2 RoleMapper.java
package com.test.mapper;

import com.test.entry.Role;
import org.apache.ibatis.annotations.Param;
/**
 * @Author: cxx
 * @Date: 2019/9/18 0:48
 */
public interface RoleMapper {
    public Role getRoleById(Long id);

    public Role getRoleByIdAndDeptId(Long id, @Param("deptId") Long deptId);

    public int deleteById(Long id);

    public int insert(@Param("role") Role role);

    public int updateRoleName(@Param("roleId") Long roleId, @Param("roleName") String name);
}

4.3 RoleMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper SYSTEM "myorm.dtd">
<mapper namespace="com.test.mapper.RoleMapper">
  <select id="getRoleById" parameterType="java.lang.Long" resultType ="com.test.entry.Role">
    select * from sys_role where role_id = #{id}
  </select>

  <select id="getRoleByIdAndDeptId" resultType ="com.test.entry.Role">
    select * from sys_role where role_id = #{id} and dept_id = #{deptId}
  </select>

  <delete id="deleteById" parameterType="java.lang.Long">
    delete from sys_role where role_id = #{id}
  </delete>

  <insert id="insert">
    insert into sys_role (role_id, role_name, remark, dept_id, create_time)
    values (#{role.roleId}, #{role.roleName}, #{role.remark}, #{role.deptId}, #{role.createTime})
  </insert>

  <update id="updateRoleName">
    update sys_role set role_name = #{roleName} where role_id = #{roleId}
  </update>
</mapper>
注意:@Param()中的value一定要和實體對象Role字段相同。

4.4 myorm.dtd 對mapperxml文檔的合法構建
<!ELEMENT mapper (select* | insert* | update* | delete* | sql*)+>

<!ELEMENT select (#PCDATA | select)*>

<!ELEMENT insert (#PCDATA)>
<!ELEMENT update (#PCDATA)>
<!ELEMENT delete (#PCDATA)>
<!ELEMENT sql (#PCDATA)>

<!ATTLIST mapper namespace CDATA #IMPLIED>

<!ATTLIST select
id CDATA #REQUIRED
parameterType CDATA #IMPLIED
resultType CDATA #IMPLIED
>

<!ATTLIST delete
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
>

<!ATTLIST insert
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
>

<!ATTLIST update
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
>

4.5 通過註解方式【優先使用註解,沒註解使用xml配置】
@Select("select * from sys_role where role_id = #{id}")
public Role selectRoleById(Long id);

核心代碼:

if (method.getAnnotations().length !=0 && method.getAnnotations().length > 1){
    throw new RuntimeException("該方法上只能有一個註解");
}
if (method.getAnnotations().length == 1){
    Function function = new Function();
    String sql = "";
    String sqlType = "";
    if (method.isAnnotationPresent(Insert.class)){
        Insert insert = method.getAnnotation(Insert.class);
        sql = insert.value();
        sqlType = "insert";

    }else if (method.isAnnotationPresent(Delete.class)){
        Delete insert = method.getAnnotation(Delete.class);
        sql = insert.value();
        sqlType = "delete";
    }else if (method.isAnnotationPresent(Update.class)){
        Update insert = method.getAnnotation(Update.class);
        sql = insert.value();
        sqlType = "update";
    }else if (method.isAnnotationPresent(Select.class)){
        Select insert = method.getAnnotation(Select.class);
        sql = insert.value();
        sqlType = "select";
    }
    function.setSql(sql);
    function.setFunctionName(method.getName());
    function.setParameterType("");
    function.setSqlType(sqlType);
    function.setResultType(method.getReturnType().getTypeName());
    functionMap.put(method.getName(), function);
}    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章