超详细!带你手写一个简单的ORM框架领悟mybatis,hibernate,jpa对象关系映射的秘密

  • Post author:
  • Post category:其他


1、

ORM介绍


对象关系映射

解决了一个问题: 对象模型和关系模型之间阻抗。

对象模型                     关系模型

对象名称 Student             表           t_student

对象属性类型 Integer         列的值的类型 int

对象属性名称 stuId           列名         student_id

1、ORM思想就诞生=>Java是一门面向对象的语言。

2、操作对象中属性的值间接操作数据库中表。

实现方式:hibernate框架

Student.hbm.xml

<class name="类的全路径" table="映射的表的名称">
	<id name="主键映射的属性名称">
		<cloumn name="表中的主键名称" />
	</id>
	<property name="studentName" cloumn="student_name" />
   </class>
   
   mybatis框架:
   <resutMap name="stuMap" type="com.demo.domain.Student">
		<id property="studentId" cloumn="student_id" />
		<result property="studentName"  column="student_name"/>
   </resultMap>
   
   // 通过注解方式完成ORM映射配置
   JPA,hibernate,spring-data-jpa
  
   @Table(name="t_student")
   public class Student {
		
		@Id // 映射主键
		@Column(name="student_id")
		private Integer studentId;
		
		@Column(name="student_name")
		private String studentName;
   }

springBoot(mybatis,spring-data-jpa,hibernate)=>微框架

2、

ORM框架的核心反射


反射:API

自定义注解:核心语法和使用方式

3、

模拟ORM的框架的部分实现

首先需要连接到JDBC,导入我们的驱动包以及手写一个工具类

手动实现增删改执行返回影响行数的方法

package com.orm.demo.db;

/**
 * @author longhai
 * @date 2018/9/19 - 14:06
 */
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

/**
 * 操作数据的通过方法
 */
public class DBUtils {
    private static final String DRIVER_CLASS = "com.mysql.jdbc.Driver";
    private static final String URL = "jdbc:mysql://localhost:3306/practice?useSSL=true";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "123456";

    static {
        try {
            Class.forName(DRIVER_CLASS);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接对象的方法
     *
     * @return
     */
    private static Connection getConnection() {
        try {
            return DriverManager.getConnection(URL, USERNAME, PASSWORD);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 编写一个执行通过的增,删,改的方法, 返回受影响的行数
     */
    public static int executeUpdate(String sql, Object...parameters) {

        try (
                Connection connection = getConnection();
                PreparedStatement pst = connection.prepareStatement(sql);
        ) {
            //在sql语句里面添加所对应的参数
            // 是否设置参数呢
            if (parameters.length > 0 ) {
                for (int i = 0; i < parameters.length; i++) {
                    // 循环设置参数
                    pst.setObject(i + 1, parameters[i]);//注意:此方法在API里面写到,
                    // 第一个参数不是零,是1,所以要i+1
                }
            }

            return pst.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return 0;
    }

}

然后创建一个学生对象

package com.orm.demo.model;

import com.orm.demo.annoation.Colum;
import com.orm.demo.annoation.Table;

/**
 * @author longhai
 * @date 2018/9/19 - 14:29
 */
@Table("t_student")
public class StudentInfo {
    @Colum("student_id")
    private Integer studentId;
    @Colum("student_name")
    private String studentName;
    @Colum("student_sex")
    private String studentSex;
    @Colum("student_age")
    private int studentAge;

    public StudentInfo(Integer studentId, String studentName, String studentSex, int studentAge) {
        this.studentId = studentId;
        this.studentName = studentName;
        this.studentSex = studentSex;
        this.studentAge = studentAge;
    }

    public Integer getStudentId() {
        return studentId;
    }

    public void setStudentId(Integer studentId) {
        this.studentId = studentId;
    }

    public String getStudentName() {
        return studentName;
    }

    public void setStudentName(String studentName) {
        this.studentName = studentName;
    }

    public String getStudentSex() {
        return studentSex;
    }

    public void setStudentSex(String studentSex) {
        this.studentSex = studentSex;
    }

    public int getStudentAge() {
        return studentAge;
    }

    public void setStudentAge(int studentAge) {
        this.studentAge = studentAge;
    }

}

自定义注解Colum和Table

package com.orm.demo.annoation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author longhai
 * @date 2018/9/19 - 13:53
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Colum {
    String value() default "";//属性映射的字段名称
}
package com.orm.demo.annoation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author longhai
 * @date 2018/9/19 - 13:52
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {
    String value() default "";//注解映射的表名称
}

具体实现类

注:由于注释比较详细,思路我就不多说了,非常易懂

package com.orm.demo;

import com.orm.demo.annoation.Colum;
import com.orm.demo.annoation.Table;
import com.orm.demo.db.DBUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * @author longhai
 * @date 2018/9/19 - 14:36
 *
*
 * javabean操作的通用工具类
 * <pre>
 *     目标: 把entity对象传入到方法中完成该对象的持久化。
 *     核心: 反射
 * </pre>
 *
 */
public class BeanUtil {
    public static <T> int save(T entity){
        // 定义存储SQL语句占位符对应值的集合(也就是属性字段所对应的值)
        List<Object> parameters = new ArrayList<>();
        StringBuilder sqlBuilder = new StringBuilder(256);//springbuffer方便组合sql语句
        // 获取到当前操作类的Class对象
        Class<?> aClass = entity.getClass();
        // 问题1: 表名称和类名称不一致怎么办?
        String tableName = aClass.getSimpleName();
        // 判断aClass是否有Table注解
        if (aClass.isAnnotationPresent(Table.class)) {
            Table table = aClass.getAnnotation(Table.class);
            if (!"".equals(table.value())) {
                tableName = table.value().toUpperCase();//如果写了别名
                // 就将表的名字设为别名的大写
            }
        }
        // 构建前面半部分SQL
        sqlBuilder.append("INSERT INTO ").append(tableName).append(" (");
         确定哪些字段需要参与SQL语句中 /
        Field[] fields = aClass.getDeclaredFields();
        try {//通过此对象获取到其所有字段以及属性
            if (fields != null && fields.length > 0) {
                for (Field field : fields) {
                    // 获取列名
                    String columName = field.getName();
                   //判断是否有表字段和此类属性名称对不上的情况
                    if (field.isAnnotationPresent(Colum.class)) {
                        Colum column = field.getAnnotation(Colum.class);
                        if (!"".equals(column.value())) {
                            columName = column.value().toUpperCase();
                        }
                    }

                    // 获取当前这个字段的值
                    field.setAccessible(true);
                    Object value = field.get(entity);

                    if (value != null) {
                        sqlBuilder.append(columName).append(",");
                        parameters.add(value);
                        //每个字段值追加逗号
                    }
                }
                sqlBuilder.deleteCharAt(sqlBuilder.length() - 1).append(") VALUES (");
                //截取最后一个逗号
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        for (int i = 0; i < parameters.size(); i++) {
            sqlBuilder.append("?,");
        }
         //构建sql语句后半段(?,?,?,?,
        sqlBuilder.deleteCharAt(sqlBuilder.length() - 1).append(")");
        //(?,?,?,?)
        System.out.println(sqlBuilder);//打印sql语句
        System.out.println(parameters);//打印参数

        return DBUtils.executeUpdate(sqlBuilder.toString(),parameters.toArray());
        //由于parameters是集合所以将他toArrary一下
    }

}
import com.orm.demo.model.StudentInfo;

/**
 * @author longhai
 * @date 2018/9/19 - 14:53
 */
public class TestORM {
    public static void main(String[] args) {
        // 创建一个学生对象
        StudentInfo studentInfo = new StudentInfo(1001, "寒梅", "男", 20);

        int row = BeanUtil.save(studentInfo);

        System.out.println(row > 0 ? "成功" : "失败");
    }
    }

我的数据库是这样的 :表名T_STUDENT 字段:STUDENT_ID,STUDENT_NAME,STUDENT_AGE,STUDENT_SEX

测试一下,我们已经将这一条信息添加到数据库中了!

大家理解了大概的过程了吗? 去动手吧,写出自己想要的功能!框架其实并没有那么困难,要多动手多动脑!

对你有帮助的就点个赞,让天下没有难学的编程



版权声明:本文为Area007原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。