JDBC ORM原理剖析

一.简介

Mybatis、Mybatis Plus 对数据结果集与实体类的映射关系都是通过反射技术实现,对于Mybatis Plus 的@TableField 注解映射实体也是非常有魅力。

二.实现

1.TableField.java

package com.vincent.db.utils;


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 TableField {
    String name() default "";
}

2.DBUtisl.java

package com.vincent.db.utils;

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

public class DBUtils {

    public static Connection getConnection(String driverCls, String url, String username, String password) throws ClassNotFoundException, SQLException {
        Class.forName(driverCls);
        return DriverManager.getConnection(url,username,password);
    }

    public static int update(Connection connection,String sql,Object ... pams) throws SQLException{
        PreparedStatement statement = setStatement(connection,sql,pams);
        return statement.executeUpdate();
    }

    private static PreparedStatement setStatement(Connection connection,String sql,Object[] pams) throws SQLException{
        PreparedStatement statement = connection.prepareStatement(sql);
        if(pams != null){
            for(int i=1;i<=pams.length;i++){
                statement.setObject(i,pams[i-1]);
            }
        }
        return statement;
    }

    public static <T> List<T> select(Connection connection, String sql, Object[] pams, Class<T> entityCls) throws SQLException {
        ResultSet rstSet = setStatement(connection,sql,pams).executeQuery();

        //获取查询结果列名,数据库列索引从1开始
        ResultSetMetaData metaData = rstSet.getMetaData();
        String[] names = new String[metaData.getColumnCount()];
        for(int i=1;i<=names.length;i++){
            names[i-1] = metaData.getColumnLabel(i);
        }

        //解析cls 类字段信息
        Map<String,Field> fieldMap = new HashMap<>();
        for(Class cls=entityCls;cls != null;){
            for(Field field : cls.getDeclaredFields()){
                fieldMap.put(field.getName(),field);
            }
            cls = cls.getSuperclass();
        }

        //建立数据库列名-字段对应关系
        Map<String,Field> entityMap = new HashMap<>();
        for(int i=0;i<names.length;i++){
            String name = names[i];

            //优先解析使用注解映射的字段信息
            Field field = null,nameField = null;

            for(Map.Entry<String,Field> entry : fieldMap.entrySet()){
                TableField tableField = entry.getValue().getAnnotation(TableField.class);

                if(nameField == null && name.equals(entry.getValue().getName())){
                    nameField = entry.getValue();
                }
                if(field == null && tableField != null && name.equals(tableField.name())){
                    field = entry.getValue();
                }
            }

            if(field == null && nameField != null){
                field = nameField;
            }
            entityMap.put(name,field);
        }


        List<T> rst = new ArrayList<>();
        while(rstSet.next()){
            try {
                T entity = (T)entityCls.newInstance();
                for(int i=0;i<names.length;i++){
                    Field field = entityMap.get(names[i]);
                    if(field != null) {
                        field.setAccessible(true);
                        field.set(entity, rstSet.getObject(i + 1));
                    }
                }
                rst.add(entity);
            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
		if(rstSet != null){
            rstSet.close();
        }
        return rst;
    }

    public static void close(Statement statement,Connection connection){
        try{
            if(statement != null){
                statement.close();
            }
        }
        catch (SQLException e){

        }

        try{
            if(connection != null){
                connection.close();
            }
        }
        catch (SQLException e){

        }
    }
}

3.User.java

package com.vincent.model;


import com.vincent.db.utils.TableField;
import lombok.Data;

@Data
public class User {
    private Integer id;

    @TableField(name = "username")
    private String name;
    private Integer age;

}

4.App.java

package com.vincent;

import com.vincent.db.utils.DBUtils;
import com.vincent.model.User;

import java.sql.*;
import java.util.List;


/**
 * Hello world!
 *
 */
public class App {
    public static void main( String[] args ) throws Exception{
        Connection connection = DBUtils.getConnection("com.mysql.jdbc.Driver","jdbc:mysql://localhost:3306/db_test","root","root");

        for(int i=0;i<20;i++){
            DBUtils.update(connection,"insert into t_user(username,age) values(?,?)","name"+i,i);
        }

        List<User> users = DBUtils.select(connection,"select * from t_user",null, User.class);
        System.out.println(users);
    }
}

效果:

在这里插入图片描述

三.总结

1.反射是Java 开发利器,尤其对于框架开发

2.注解+反射使得繁琐的配置文件编写不在是噩梦

3.注解+反射 对于权限检查、方法参数注入都是非常实用,如springboot HandlerInterceptor 自定义需要拦截的mapping,HandlerMethodArgumentResolver 给mapping 方法添加注解参数实体

猜你喜欢

转载自blog.csdn.net/Zllvincent/article/details/107141443