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

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

1 导入pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.gosang</groupId>
    <artifactId>jdbc01</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>

        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>


    </dependencies>

</project>
2 配置c3p0数据库连接池
<c3p0-config>
    <named-config name="mysql">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb1</property>
        <property name="user">xxx</property>
        <property name="password">xxx</property>

        <property name="initialPoolSize">50</property>
        <property name="maxPoolSize">100</property>
        <property name="minPoolSize">2</property>
        <property name="acquireIncrement">10</property>
    </named-config>
</c3p0-config>

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();
        }


    }
}

测试结果

测试结果
全代码(有其他的不用管)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值