1. 程式人生 > >帶你手寫一個簡單的ORM框架領悟mybatis,hibernate,jpa物件關係對映的祕密

帶你手寫一個簡單的ORM框架領悟mybatis,hibernate,jpa物件關係對映的祕密

1、ORM介紹     物件關係對映     解決了一個問題: 物件模型和關係模型之間阻抗。     物件模型                     關係模型     物件名稱 Student             表           t_student     物件屬性型別 Integer         列的值的型別 int     物件屬性名稱 stuId           列名         student_id    1、ORM思想就誕生=>Java是一門面向物件的語言。    2、操作物件中屬性的值間接操作資料庫中表。    實現方式:hibernate框架    Student.hbm.xml

       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

測試一下,我們已經將這一條資訊新增到資料庫中了!

大家理解了大概的過程了嗎? 去動手吧,寫出自己想要的功能!框架其實並沒有那麼困難,要多動手多動腦!

對你有幫助的就點個贊,讓天下沒有難學的程式設計