利用反射模拟ORM,完成对象与表的关系映射,返回指定对象。

前言:我们熟知的做数据持久层的框架有很多,比如点的Hibernate,Mybatis,Spring Data(基于Hibernate)。其中Hibernate是全自动化的ORM框架。也就是做到了对象和表的直接映射,sql查询直接可以返回java对象。而要做到这一点就离不开java 的反射机制。本文就是利用反射模拟返回对象的这一过程。可以对ORM框架有更深的理解。

1 导入pom文件


    4.0.0

    org.gosang
    jdbc01
    1.0-SNAPSHOT

    
        
            mysql
            mysql-connector-java
            5.1.46
        

        
            com.mchange
            c3p0
            0.9.5.2
        


    


2 配置c3p0数据库连接池

    
        com.mysql.jdbc.Driver
        jdbc:mysql://localhost:3306/mydb1
        xxx
        xxx

        50
        100
        2
        10
    


3 表和实体类
/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50562
 Source Host           : localhost:3306
 Source Schema         : mydb1

 Target Server Type    : MySQL
 Target Server Version : 50562
 File Encoding         : 65001

 Date: 22/07/2020 23:45:00
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`  (
  `Sid` int(10) NULL DEFAULT NULL,
  `Sname` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `Ssex` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (2, '钱电', '男');
INSERT INTO `student` VALUES (3, '孙风', '男');
INSERT INTO `student` VALUES (4, '李云', '男');
INSERT INTO `student` VALUES (5, '周梅', '女');
INSERT INTO `student` VALUES (6, '吴兰', '女');
INSERT INTO `student` VALUES (7, '郑竹', '女');
INSERT INTO `student` VALUES (9, '张三', '女');
INSERT INTO `student` VALUES (10, '李四', '女');
INSERT INTO `student` VALUES (11, '李四', '女');
INSERT INTO `student` VALUES (12, '赵六', '女');
INSERT INTO `student` VALUES (13, '孙七', '女');
INSERT INTO `student` VALUES (14, '孙七', '男');

SET FOREIGN_KEY_CHECKS = 1;
package com.gosang.test;

public class Student {
     
    private Integer Sid;
    private String Sname;
    private String Ssex;

    public Integer getSid() {
     
        return Sid;
    }

    public void setSid(Integer sid) {
     
        Sid = sid;
    }

    public String getSname() {
     
        return Sname;
    }

    public void setSname(String sname) {
     
        Sname = sname;
    }

    public String getSsex() {
     
        return Ssex;
    }

    public void setSsex(String ssex) {
     
        Ssex = ssex;
    }

    @Override
    public String toString() {
     
        return "Student{" +
                "Sid=" + Sid +
                ", Sname='" + Sname + '\'' +
                ", Ssex='" + Ssex + '\'' +
                '}';
    }
}

4 DBUtils通用工具类(重点)
package com.gosang.utils;


import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class DBUtils<T> {
     

    public T getPOJO(Connection connection,String sql,Class clazz) {
     
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        T t = null;
        try {
     
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();
            while (resultSet.next()){
     
                //从数据库中拿到对应的数据,然后就要把这些数据封装成对象。使用反射完成动态创建对象。
                //按照我们通常的思路,从数据库拿到值后,手动创建一个Student对象后,把值set进去就好了。但是这样写就相当于给Student写了个DBUitls,
                // 那DBUtils就失去了它作为通用工具的意义。故采用反射,动态的创建要返回对象。
                //1.通过构造器创建好对象
                Constructor<T> constructor  = clazz.getConstructor(null);
                t = constructor.newInstance();
                //数据解析并返回
                ResultSetMetaData metaData = resultSet.getMetaData();
                int columnCount = metaData.getColumnCount();
                //表有很多列,每一列的属性可能都不同。故做以下逻辑判断
                for (int i = 1;i<=columnCount;i++){
     
                    String columnName = metaData.getColumnName(i);
                    String typeName = metaData.getColumnTypeName(i);
                    Object columnVal = null;
                    switch (typeName){
     
                        case "INT":
                            columnVal = resultSet.getInt(columnName);
                            break;
                        case "VARCHAR":
                            columnVal = resultSet.getString(columnName);
                            break;
                    }


                    //2.拼接对象的set方法,用于设值。反射中的设置刚好和正常的思路反过来,是方法.invoke(对象,参数)/正常是对象.方法(参数)
                    //String method = "set"+columnName.substring(0,1).toUpperCase()+columnName.substring(1);
                    Field field = clazz.getDeclaredField(columnName);
                    //拿到方法需要方法名和返回值类型,也就是对应变量的属性
                    Method method = clazz.getMethod("set"+columnName.substring(0,1).toUpperCase()+columnName.substring(1),field.getType());
                    //设值
                    method.invoke(t,columnVal);

                }
            }
        } catch (Exception throwables) {
     
            throwables.printStackTrace();
        }finally {
     
            try {
     
                resultSet.close();
                statement.close();
                connection.close();
            } catch (SQLException throwables) {
     
                throwables.printStackTrace();
            }
        }


        return t;
    }


    public List<T> getList(Connection connection,String sql,Class clazz){
     
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        List<T> Tlist = new ArrayList();
        try {
     
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();
            while (resultSet.next()){
     
                //从数据库中拿到对应的数据,然后就要把这些数据封装成对象。使用反射完成动态创建对象。
                //按照我们平时的思路,从数据库拿到值后,手动创建一个Student对象后,把值set进去就好了。但是这样写就相当于给Student写了个DBUitls,
                // 那DBUtils就失去了它作为通用工具的意义。故采用反射,动态的创建要返回对象。
                //1.通过构造器创建好对象
                Constructor<T> constructor  = clazz.getConstructor(null);
                T t = constructor.newInstance();
                //数据解析并返回
                ResultSetMetaData metaData = resultSet.getMetaData();
                int columnCount = metaData.getColumnCount();
                for (int i = 1;i<=columnCount;i++){
     
                    String columnName = metaData.getColumnName(i);
                    String typeName = metaData.getColumnTypeName(i);
                    Object columnVal = null;
                    switch (typeName){
     
                        case "INT":
                            columnVal = resultSet.getInt(columnName);
                            break;
                        case "VARCHAR":
                            columnVal = resultSet.getString(columnName);
                            break;
                    }
                    //2.拼接对象的set方法,用于设值。反射中的设置刚好和正常的思路反过来。是方法.invoke(对象,参数)。正常是对象.方法(参数)
                    //String method = "set"+columnName.substring(0,1).toUpperCase()+columnName.substring(1);
                    Field field = clazz.getDeclaredField(columnName);
                    Method method = clazz.getMethod("set"+columnName.substring(0,1).toUpperCase()+columnName.substring(1),field.getType());
                    method.invoke(t,columnVal);

                }
                Tlist.add(t);
            }
        } catch (Exception throwables) {
     
            throwables.printStackTrace();
        }finally {
     
            try {
     
                resultSet.close();
                statement.close();
                connection.close();
            } catch (SQLException throwables) {
     
                throwables.printStackTrace();
            }
        }


        return Tlist;
    }

}


5 测试类
package com.gosang.test;

import com.gosang.utils.DBUtils;
import com.mchange.v2.c3p0.ComboPooledDataSource;

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

public class c3p0Test {
     
    public static void main(String[] args) {
     
        ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
        DBUtils dbUtils = new DBUtils<Student>();
        Connection connection = null;
        try {
     
            connection = dataSource.getConnection();
            //String sql = "SELECT * FROM student where SId = '02'";
            String sql = "SELECT * FROM student";

            /*Object pojo = dbUtils.getPOJO(connection, sql, Student.class);
            System.out.println(pojo);*/
            List<Student> list = dbUtils.getList(connection, sql, Student.class);
            System.out.println(list);
        } catch (SQLException throwables) {
     
            throwables.printStackTrace();
        }


    }
}

测试结果

利用反射模拟ORM,完成对象与表的关系映射,返回指定对象。_第1张图片
全代码(有其他的不用管)

你可能感兴趣的:(Java核心技术,demo)