简单ORM映射方案(JDBC)

  1. 简单ORM映射方案(JDBC)
    1. 代码
    2. 测试
    3. 集成INSERT,UPDATE自动组装

简单ORM映射方案(JDBC)

代码

package orm.factory;

import orm.annotation.TableColumn;
import orm.handler.TableColumnGetter;
import orm.handler.TableHandler;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.util.*;

public class TableObjectFactory {

    private static final String STRING = "java.lang.String";

    private static final String INTEGER = "java.lang.Integer";

    private static final String DOUBLE = "java.lang.Double";
    
    private static final String CHARACTER = "java.lang.Character";

    private static final String BOOLEAN = "java.lang.Boolean";

    private static final String SHORT = "java.lang.Short";

    private static final String BYTE = "java.lang.Byte";

    private static final String LONG = "java.lang.Long";

    private static final String FLOAT = "java.lang.Float";

    private static final String DATE = "java.util.Date";

    private static final Map<String,Class> TYPE_MAP = new HashMap<>();

    private static final Map<String,String> METHOD_MAP = new HashMap<>();

    static {
        TYPE_MAP.put(STRING,String.class);
        TYPE_MAP.put(INTEGER,Integer.class);
        TYPE_MAP.put(DOUBLE,Double.class);
        TYPE_MAP.put(BOOLEAN,Boolean.class);
        TYPE_MAP.put(SHORT,Short.class);
        TYPE_MAP.put(BYTE,Byte.class);
        TYPE_MAP.put(LONG,Long.class);
        TYPE_MAP.put(FLOAT,Float.class);
        TYPE_MAP.put(DATE, Date.class);

        METHOD_MAP.put(STRING,"String");
        METHOD_MAP.put(INTEGER,"Int");
        METHOD_MAP.put(DOUBLE,"Double");
        METHOD_MAP.put(BOOLEAN,"Boolean");
        METHOD_MAP.put(SHORT,"Short");
        METHOD_MAP.put(BYTE,"Byte");
        METHOD_MAP.put(LONG,"Long");
        METHOD_MAP.put(FLOAT,"Float");
        METHOD_MAP.put(DATE,"Date");

    }


    /**
     * 自动解析result为对应持久对象数组
     * @param resultSet 结果集
     * @param tClass 持久对象字节码
     * @param <T> 持久对象泛型
     * @return
     */
    public static <T>List<T> toObjectList(ResultSet resultSet, Class<T> tClass){

        List<TableColumnGetter> tableColumnGetterList = new ArrayList<>();

        Class tableClass = tClass;
        //获取属性标注的字段值,自动解析字段
        Field[] fields = tableClass.getDeclaredFields();
        if (tableClass == null){
            return new ArrayList<>();
        }
        for (Field field : fields) {
            TableColumn tableColumn = field.getAnnotation(TableColumn.class);
            if (tableColumn != null){
                //获取属性名
                String propertyName = field.getName();
                //set函数名
                String setMethodName = "set" + propertyName.substring(0,1).toUpperCase() + propertyName.substring(1);
                //字段名
                String columnLabel = tableColumn.value();
                //获取属性类型
                String typeName = field.getGenericType().getTypeName();
                try {
                    //获取到类型字节码
                    Class typeClass = TYPE_MAP.get(typeName);
                    //获取到set方法
                    Method objectFieldSet = tableClass.getMethod(setMethodName,typeClass);
                    //获取到字段对应的值
                    Class<? extends ResultSet> resultSetClass = resultSet.getClass();
                    //组装resultSet的get函数名称
                    String getMethodName = "get" + METHOD_MAP.get(typeName);
                    //获取到对应解析函数字节码
                    Method resultSetGet = resultSetClass.getMethod(getMethodName, String.class);
                    //创建获取字段注入器
                    TableColumnGetter tableColumnGetter = new TableColumnGetter(objectFieldSet, resultSetGet, columnLabel);
                    //整合字段注入器列表
                    tableColumnGetterList.add(tableColumnGetter);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }
        }
        //创建表处理器
        TableHandler tableHandler = new TableHandler(tableClass,tableColumnGetterList);
        //获取表java实例列表
        List objectList = tableHandler.getObjectList(resultSet);
        return objectList;
    }



}
  1. 首先,从tableClassMap的映射中获取T类对应的表类(tableClass)。
  2. 然后,获取tableClass的所有字段,并对每个字段进行检查。如果字段上有TableColumn注解,那么就创建一个TableColumnGetter对象,这个对象包含了字段的set方法、对应的ResultSet的get方法以及字段名。
  3. 创建TableColumnGetter对象的过程中,会根据字段的类型从TYPE_MAPMETHOD_MAP两个映射中获取相应的类型类和方法名。
  4. 最后,使用tableClasstableColumnGetterList创建一个TableHandler对象,并调用其getObjectList方法来获取对象列表。
package orm.handler;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSet;


public class TableColumnGetter {

    //持久对象属性set方法字节码
    private Method objectFieldSet;

    //resultSet对象get方法字节码
    private Method resultSetGet;

    //数据库字段名
    private String columnLabel;

    public TableColumnGetter(Method objectFieldSet, Method resultSetGet,String columnLabel) {
        this.objectFieldSet = objectFieldSet;
        this.resultSetGet = resultSetGet;
        this.columnLabel = columnLabel;
    }


    //为持久对象对应属性注入值
    public <T> void inject(ResultSet resultSet, T object) {
        try {
            Object result = resultSetGet.invoke(resultSet, columnLabel);
            objectFieldSet.invoke(object, result);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}

TableColumnGetter类是一个字段注入器,它包含一个Java对象的set方法、一个ResultSet的get方法和一个数据库字段名。它的inject方法用于将ResultSet中的值注入到Java对象的属性中。

package orm.handler;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class TableHandler<T> {

    //持久化对象列表
    private List<T> objectList;

    //持久化对象字节码
    private Class tableClass;

    //表列值获取器列表
    private List<TableColumnGetter> tableColumnGetterList;

    public TableHandler(Class tableClass, List<TableColumnGetter> tableColumnGetterList){
        this.tableClass = tableClass;
        this.tableColumnGetterList = tableColumnGetterList;
    }

    //获取持久对象列表
    public List<T> getObjectList(ResultSet resultSet) {
        if (objectList == null){
            objectList = new ArrayList<>();
        }
        while (true){
            try {
                if (!resultSet.next()) break;

                T object = (T)tableClass.newInstance();

                for (TableColumnGetter tableColumnGetter : tableColumnGetterList) {
                    tableColumnGetter.inject(resultSet,object);
                }

                objectList.add(object);

            } catch (SQLException throwables) {
                throwables.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }

        }
        return objectList;
    }
}

TableHandler类是一个表处理器,它包含一个Java对象列表、一个Java对象的类和一个字段注入器列表。它的getObjectList方法用于从ResultSet中获取Java对象列表。这个方法首先创建一个新的Java对象,然后使用字段注入器列表中的每一个注入器将ResultSet中的值注入到Java对象的属性中,最后将Java对象添加到列表中。

package orm.annotation;

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

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableColumn {
    String value();
}

测试

package com;

import orm.annotation.TableColumn;

import java.util.Date;

public class TravelWay {

    @TableColumn("wid")
    private Integer wid;

    @TableColumn("way_name")
    private String wayName;

    @TableColumn("info")
    private String info1;

    @TableColumn("sign_begin")
    private Date sb;

    public Integer getWid() {
        return wid;
    }

    public void setWid(Integer wid) {
        this.wid = wid;
    }

    public String getWayName() {
        return wayName;
    }

    public void setWayName(String wayName) {
        this.wayName = wayName;
    }

    public String getInfo1() {
        return info1;
    }

    public void setInfo1(String info1) {
        this.info1 = info1;
    }

    public Date getSb() {
        return sb;
    }

    public void setSb(Date sb) {
        this.sb = sb;
    }

    @Override
    public String toString() {
        return "TravelWay{" +
                "wid=" + wid +
                ", wayName='" + wayName + '\'' +
                ", info1='" + info1 + '\'' +
                ", sb=" + sb +
                '}';
    }
}
package com;

import orm.utils.ORMUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

public class Test {

    public static void main(String[] args) {

        try {
            Connection connection = JdbcUtils.getConnection();

            Statement statement = connection.createStatement();

            ResultSet resultSet = statement.executeQuery("select * from tb_travel_way");

            List<TravelWay> travelWays = ORMUtils.toObjectList(resultSet, TravelWay.class);

            for (TravelWay travelWay : travelWays) {

                System.out.println(travelWay);
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

    }

}

集成INSERT,UPDATE自动组装

package orm.utils;

import orm.annotation.Key;
import orm.annotation.Table;
import orm.annotation.TableColumn;
import orm.handler.TableColumnGetter;
import orm.handler.TableHandler;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.*;

public class ORMUtils {

    private static final String STRING = "java.lang.String";

    private static final String INTEGER = "java.lang.Integer";

    private static final String DOUBLE = "java.lang.Double";

    private static final String CHARACTER = "java.lang.Character";

    private static final String BOOLEAN = "java.lang.Boolean";

    private static final String SHORT = "java.lang.Short";

    private static final String BYTE = "java.lang.Byte";

    private static final String LONG = "java.lang.Long";

    private static final String FLOAT = "java.lang.Float";

    private static final String DATE = "java.util.Date";

    private static final Map<String,Class> TYPE_MAP = new HashMap<>();

    private static final Map<String,String> METHOD_MAP = new HashMap<>();

    static {
        TYPE_MAP.put(STRING,String.class);
        TYPE_MAP.put(INTEGER,Integer.class);
        TYPE_MAP.put(DOUBLE,Double.class);
        TYPE_MAP.put(BOOLEAN,Boolean.class);
        TYPE_MAP.put(SHORT,Short.class);
        TYPE_MAP.put(BYTE,Byte.class);
        TYPE_MAP.put(LONG,Long.class);
        TYPE_MAP.put(FLOAT,Float.class);
        TYPE_MAP.put(DATE, Date.class);

        METHOD_MAP.put(STRING,"String");
        METHOD_MAP.put(INTEGER,"Int");
        METHOD_MAP.put(DOUBLE,"Double");
        METHOD_MAP.put(BOOLEAN,"Boolean");
        METHOD_MAP.put(SHORT,"Short");
        METHOD_MAP.put(BYTE,"Byte");
        METHOD_MAP.put(LONG,"Long");
        METHOD_MAP.put(FLOAT,"Float");
        METHOD_MAP.put(DATE,"Date");

    }

    private static final String EMPTY = "";
    private static final String SPACE = " ";
    private static final String BASE_UPDATE = "UPDATE ";
    private static final String BASE_SET = " SET ";
    private static final String BASE_WHERE = " WHERE 1 = 1 ";
    private static final String AND = " AND ";
    private static final String BASE_INSERT = "INSERT INTO ";
    private static final String START = "(";
    private static final String END = ")";
    private static final String BASE_VALUES = "VALUES";

    private static final String COLUMN_FLAG = "`";

    private static final String STRING_FLAG = "'";

    private static final String SPLIT = ",";

    private static final String EQUAL = " = ";

    private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");

    public static <T>boolean update(T t,Connection connection){
        return update(EMPTY,t,connection);
    }

    /**
     *
     * @param sqlAppend 拼接到 WHERE 之后的语句
     * @param t 更新对象
     * @param connection 连接对象
     * @param <T>
     * @return
     */
    public static <T>boolean update(String sqlAppend,T t, Connection connection){
        Class tableClass = t.getClass();

        if (tableClass == null){
            return false;
        }

        Table table = (Table)tableClass.getAnnotation(Table.class);

        if (table == null){
            return false;
        }

        String sql = BASE_UPDATE + table.value() + BASE_SET;
        String where = BASE_WHERE;

        //获取属性标注的字段值,自动解析字段
        Field[] fields = tableClass.getDeclaredFields();

        StringBuilder sqlSB = new StringBuilder();
        StringBuilder keySB = new StringBuilder();
        sqlSB.append(sql);
        keySB.append(where);
        for (Field field : fields) {
            TableColumn tableColumn = field.getAnnotation(TableColumn.class);
            if (tableColumn != null){
                //获取属性名
                String propertyName = field.getName();
                //get函数名
                String getMethodName = "get" + propertyName.substring(0,1).toUpperCase() + propertyName.substring(1);
                //字段名
                String columnLabel = tableColumn.value();
                //获取属性类型
                String typeName = field.getGenericType().getTypeName();
                try {
                    //获取到类型字节码
                    Class typeClass = TYPE_MAP.get(typeName);
                    //获取到set方法
                    Method objectFieldGet = tableClass.getMethod(getMethodName);
                    Object value = objectFieldGet.invoke(t);
                    if (value != null){

                        Key key = field.getAnnotation(Key.class);

                        boolean isKey = false;
                        StringBuilder sb;
                        if (key != null){
                            keySB.append(AND);
                            isKey = true;
                            sb = keySB;
                        }else {
                            sb = sqlSB;
                        }

                        //拼接表字段名
                        sb.append(COLUMN_FLAG);
                        sb.append(columnLabel);
                        sb.append(COLUMN_FLAG);
                        sb.append(EQUAL);

                        //拼接值
                        if (typeClass.getTypeName().equals(STRING)){
                            sb.append(STRING_FLAG);
                            sb.append(value);
                            sb.append(STRING_FLAG);
                        }else if (typeClass.getTypeName().equals(DATE)){
                            sb.append(STRING_FLAG);
                            sb.append(SIMPLE_DATE_FORMAT.format(value));
                            sb.append(STRING_FLAG);
                        }else {
                            sb.append(value);
                        }
                        if (!isKey){
                            sb.append(SPLIT);
                        }
                    }
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }

        sql = sqlSB.toString();
        where = keySB.toString();

        //整理
        if (sql.endsWith(SPLIT)){
            sql = sql.substring(0,sql.length()-1);
        }

        //更新语句整合
        String updateSql = sql + SPACE + where;
        if (sqlAppend != EMPTY){
            updateSql += AND + sqlAppend;
        }
        //执行
        boolean execute = false;
        try {
            Statement statement = connection.createStatement();

            execute = statement.execute(updateSql);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return execute;

    }


    /**
     *
     * @param t
     * @param connection
     * @param <T>
     * @return
     */
    public static <T>boolean insert(T t, Connection connection){
        Class tableClass = t.getClass();

        if (tableClass == null){
            return false;
        }

        Table table = (Table)tableClass.getAnnotation(Table.class);

        if (table == null){
            return false;
        }

        String sql = BASE_INSERT + table.value() + START;
        String values = BASE_VALUES + START;

        //获取属性标注的字段值,自动解析字段
        Field[] fields = tableClass.getDeclaredFields();

        StringBuilder sqlSB = new StringBuilder();
        sqlSB.append(sql);
        StringBuilder valueSB = new StringBuilder();
        valueSB.append(values);
        for (Field field : fields) {
            TableColumn tableColumn = field.getAnnotation(TableColumn.class);
            if (tableColumn != null){
                //获取属性名
                String propertyName = field.getName();
                //get函数名
                String getMethodName = "get" + propertyName.substring(0,1).toUpperCase() + propertyName.substring(1);
                //字段名
                String columnLabel = tableColumn.value();
                //获取属性类型
                String typeName = field.getGenericType().getTypeName();
                try {
                    //获取到类型字节码
                    Class typeClass = TYPE_MAP.get(typeName);
                    //获取到set方法
                    Method objectFieldGet = tableClass.getMethod(getMethodName);
                    Object value = objectFieldGet.invoke(t);
                    if (value != null){

                        //拼接表字段名
                        sqlSB.append(COLUMN_FLAG);
                        sqlSB.append(columnLabel);
                        sqlSB.append(COLUMN_FLAG);
                        sqlSB.append(SPLIT);

                        //拼接值
                        if (typeClass.getTypeName().equals(STRING)){
                            valueSB.append(STRING_FLAG);
                            valueSB.append(value);
                            valueSB.append(STRING_FLAG);
                        }else if (typeClass.getTypeName().equals(DATE)){
                            valueSB.append(STRING_FLAG);
                            valueSB.append(SIMPLE_DATE_FORMAT.format(value));
                            valueSB.append(STRING_FLAG);
                        }else {
                            valueSB.append(value);
                        }
                        valueSB.append(SPLIT);
                    }
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }

        sql = sqlSB.toString();
        values = valueSB.toString();

        //整理
        if (sql.endsWith(SPLIT)){
            sql = sql.substring(0,sql.length()-1) + END;
        }
        //整理
        if (values.endsWith(SPLIT)){
            values = values.substring(0,values.length()-1) + END;
        }
        //插入语句整合
        String insertSql = sql + values;
        System.out.println(insertSql);
        //执行
        boolean execute = false;
        try {
            Statement statement = connection.createStatement();

            execute = statement.execute(insertSql);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return execute;

    }

    /**
     * 自动解析result为对应持久对象数组
     * @param resultSet 结果集
     * @param tClass 持久对象字节码
     * @param <T> 持久对象泛型
     * @return
     */
    public static <T>List<T> toObjectList(ResultSet resultSet, Class<T> tClass){

        List<TableColumnGetter> tableColumnGetterList = new ArrayList<>();

        Class tableClass = tClass;
        if (tableClass == null){
            return new ArrayList<>();
        }
        //获取属性标注的字段值,自动解析字段
        Field[] fields = tableClass.getDeclaredFields();
        for (Field field : fields) {
            TableColumn tableColumn = field.getAnnotation(TableColumn.class);
            if (tableColumn != null){
                //获取属性名
                String propertyName = field.getName();
                //set函数名
                String setMethodName = "set" + propertyName.substring(0,1).toUpperCase() + propertyName.substring(1);
                //字段名
                String columnLabel = tableColumn.value();
                //获取属性类型
                String typeName = field.getGenericType().getTypeName();
                try {
                    //获取到类型字节码
                    Class typeClass = TYPE_MAP.get(typeName);
                    //获取到set方法
                    Method objectFieldSet = tableClass.getMethod(setMethodName,typeClass);
                    //获取到字段对应的值
                    Class<? extends ResultSet> resultSetClass = resultSet.getClass();
                    //组装resultSet的get函数名称
                    String getMethodName = "get" + METHOD_MAP.get(typeName);
                    //获取到对应解析函数字节码
                    Method resultSetGet = resultSetClass.getMethod(getMethodName, String.class);
                    //创建获取字段注入器
                    TableColumnGetter tableColumnGetter = new TableColumnGetter(objectFieldSet, resultSetGet, columnLabel);
                    //整合字段注入器列表
                    tableColumnGetterList.add(tableColumnGetter);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }
        }
        //创建表处理器
        TableHandler tableHandler = new TableHandler(tableClass,tableColumnGetterList);
        //获取表java实例列表
        List objectList = tableHandler.getObjectList(resultSet);
        return objectList;
    }

}
package orm.annotation;

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

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Key {
}
package orm.annotation;

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

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String value();
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以邮件至 1300452403@qq.com

文章标题:简单ORM映射方案(JDBC)

字数:3k

本文作者:Os467

发布时间:2023-12-25, 18:43:30

最后更新:2023-12-25, 18:43:49

原始链接:https://os467.github.io/2023/12/25/%E7%AE%80%E5%8D%95ORM%E6%98%A0%E5%B0%84%E6%96%B9%E6%A1%88/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

×

喜欢就点赞,疼爱就打赏