写出你自己的ORM框架(三)

一、本篇主要完成查询功能

  • 多行多列 List< javaBean >
  • 一行多列 javaBean
  • 一行一列
    • 普通对象 object
    • 数值 Number (select count(*) from emp )

二、完成queryRows,多行多列查询

其实这里完成的很Low,这里为了方便,将sql语句交给用户完成,
测试代码如下:

 List queryRows = mysqlQuery.queryRows("select empName,age from emp where age>? and age<?",
                Emp.class, new String[]{"21", "30"});
  • 其中需要明白一点,就原始数据的类型即使扩大范围了,但是得到class还是原始的类型,如下
String s = "hjh";
        Object s1 =s;
        System.out.println(s1.getClass()); //java.lang.String
  • queryRows代码如下,这里需要用到查询结果数据库的元数据通过java.sql.ResultSet.getMetaData(),
    之前获取表结构是通过connection.getMetaData()
 @Override
    public List queryRows(String sql, Class clazz, String[] params) {
        PreparedStatement preparedStatement = null;
        ResultSet resultSet =null;
        Connection connection = DBManager.getConnection();
        // 将查询到的结果分装到List<JavaBean>中
        List result = new ArrayList();
        try {
            //sql =>>> select empName,age from emp where age>? and age<?
            preparedStatement  = connection.prepareStatement(sql);
            // select empName,age from emp where age>20 and age<30
            JDBCUtils.handleParams(preparedStatement,params);
            resultSet = preparedStatement.executeQuery();
            //得到查询结果的数据库元数据
            ResultSetMetaData metaData = resultSet.getMetaData();
            while (resultSet.next()){
                Object newInstance = clazz.newInstance();
                for (int i=0;i<metaData.getColumnCount();i++){
                    //得到查询结果列的标签也就是列名  如 empName,age 下表从一开始
                    String columnLabel = metaData.getColumnLabel(i + 1);
                    //得到查询结果列的指定column的值  如 empName='小米' 下表从一开始
                    Object columnValue = resultSet.getObject(i + 1);
                    //通过反射调用该方法的指定属性的set方法将其赋值  如果返回值 empName='小米' 那么columnValue.getClass() 就是java.lang.string
                    ReflectUtils.setMethodResult(newInstance,columnLabel,columnValue.getClass(),columnValue);
                }
                //一个对象完成,将其加入result集合中去
                result.add(newInstance);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.close(preparedStatement,resultSet,connection);
        }
        return result;
    }

2.1 多表联查

因为我们将sql语句的书写功能交给了使用者,而且我们只是根据数据库的查询结果分装成java对象,所以,只要查询数据库得到的字段在java中有对应的pojo,我们都可以完成,查询的分装
测试
两表查询,先建立两表查询结果的java对象,如下(set与get方法省略)

package cn.gxm.sorm.vo;

/**
 * @author GXM www.guokangjie.cn
 * @date 2019/5/18
 */
public class EmpToDept {
    private Integer empId;
    private String empName;
    private String deptName;
    private Integer deptId;
}

测试代码

        String sql = "SELECT e.id empId,e.empName,d.deptName,d.id deptId FROM emp AS e LEFT JOIN dept AS d ON e.deptId = d.id WHERE d.id > ?";
        List queryRows1 = mysqlQuery.queryRows(sql, EmpToDept.class, new String[]{"1"});
        System.out.println(queryRows1);

结果:

[EmpToDept{empId=1, empName='GXM', deptName='测试部', deptId=2}]

三、查询一行多列

很简单,直接调用前面的多行多列,取第一个即可,代码如下:

@Override
    public Object queryOne(String sql,Class clazz, String[] params) {
        return queryRows(sql,clazz,params).size()>0?queryRows(sql,clazz,params).get(0):null;
    }

四、一行一列

修改前面的多行多列的代码,直接执行取完第一列,第一行数据直接返回,代码如下:

@Override
    public Number queryNumber(String sql, String[] params) {
        return (Number) queryValue(sql, params);
    }


private Object queryValue(String sql,String[] params) {
        PreparedStatement preparedStatement = null;
        ResultSet resultSet =null;
        Connection connection = DBManager.getConnection();
        try {
            //sql =>>> SELECT COUNT(*) FROM emp WHERE deptId>?
            preparedStatement  = connection.prepareStatement(sql);
            // SELECT COUNT(*) FROM emp WHERE deptId>1
            JDBCUtils.handleParams(preparedStatement,params);
            resultSet = preparedStatement.executeQuery();
            //得到查询结果的数据库元数据
            ResultSetMetaData metaData = resultSet.getMetaData();
            while (resultSet.next()){
               return resultSet.getObject(1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.close(preparedStatement,resultSet,connection);
        }
        return null;
    }

五、完善代码的小优化

5.1 优化代码的重用性

对于我么到现在所做的操作跟数据库的类型关系并不大,因为都是基础的CRUD,因此,我们把MysqlQuery中的方法放到父类中去,将父类Query从接口变为抽象类,MysqlQuery继承这个抽象父类Query

5.2 优化查询的代码重用性

  • 这个优化的就是查询的部分,前面的几种查询,基本模板都是一样的,只是封装的结果集的方式不同,所以我们将结果抽出来,定义为一个接口CallResultSet,里面有一个方法handleResultSet用于对不同的结果集的处理
    代码如下:
package cn.gxm.sorm.core.query;

import java.sql.ResultSet;

interface CallResultSet{

    /**
     * 根据需求完成对查询结果的分装
     * @param resultSet 查询结果集
     * @param clazz 封装数据的对象
     * @param <T>
     * @return
     */
    <T> Object handleResultSet(ResultSet resultSet, Class<T> clazz);

}
  • 模板集baseQuery,代码如下
/**
     * 基础的查询
     * @param sql 查询的sql语句
     * @param params sql语句的参数值
     * @param callResultSet 查询的结果集处理器
     * @return
     */
    @Nullable private <T> Object baseQuery(@NotNull  String sql,
                                 @Nullable String[] params,
                                 @NotNull  Class<T> clazz,
                                 @NotNull  CallResultSet callResultSet) {
        PreparedStatement preparedStatement = null;
        ResultSet resultSet =null;
        Connection connection = DBManager.getConnection();
        try {
            //sql =>>> SELECT COUNT(*) FROM emp WHERE deptId>?
            preparedStatement  = connection.prepareStatement(sql);
            // SELECT COUNT(*) FROM emp WHERE deptId>1
            JDBCUtils.handleParams(preparedStatement,params);
            resultSet = preparedStatement.executeQuery();
            return callResultSet.handleResultSet(resultSet,clazz);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.close(preparedStatement,resultSet,connection);
        }
        return null;
    }
  • 每种查询的处理
查询的类型继承CallResultSet类的具体实现
多行多列QueryRowsCallResultSet implements CallResultSet
一行多列不用写直接根以前一样调用多行多列的方法即可
一行一列class QueryNumberCallResultSet implements CallResultSet

5.3 优化连接池

  • 连接池初始化,实现初始化多个连接放在集合中,以供使用
  • 获取连接,就直接从连接池中获取
    • 首先判断当前连接池中连接数量是否够用,不够用就增加连接知道到达最大值
    • 连接池中获取集合中的最后一个连接
  • 关闭连接,从连接池中关闭连接
    • 连接池判断,如果当前连接池中连接的数量已经超过最大值,就真的关闭连接
    • 如果没有就将其加入到连接池中

连接池代码如下:

package cn.gxm.sorm.pool;

import cn.gxm.sorm.core.DBManager;
import cn.gxm.sorm.utils.JDBCUtils;

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

/**
 * @author GXM www.guokangjie.cn
 * @date 2019/5/19
 *
 * 管理数据库的连接的连接池
 */
public class DBConnPool {
    /**
     * 连接池对象
     */
    private static List<Connection> pool;

    /**
     * 连接池中连接的最大连接数
     */
    private static Integer maxConnection = DBManager.getConfiguration().getMaxConnection();
    /**
     * 连接池中连接的最小连接数
     */
    private static Integer minConnection = DBManager.getConfiguration().getMinConnection();

    static {
        pool = new LinkedList<>();
        initPool();
    }

    /**
     * 初始化连接池
     */
    private static void initPool(){
        for(int i=0;i<maxConnection;i++){
            pool.add(DBManager.createConnection());
        }
    }

    /**
     * 从连接词中获取连接
     * @return
     */
    public static synchronized Connection poolCreateConnection(){
        // 连接池中的连接数量已经小于最低值,需要增加connection
        while (pool.size()<minConnection){
            pool.add(DBManager.createConnection());
        }

        Connection connection = pool.get(pool.size()-1);
        //从连接池中关闭
        pool.remove(connection);
        return connection;
    }

    /**
     * 从连接池中关闭连接
     * @param connection
     */
    public static void poolCloseConnection(Connection connection){
        // 连接池中的连接已经超过最大值,就真的关闭连接
        if(pool.size()>maxConnection){
            JDBCUtils.close(null,null,connection);
            return;
        }
        pool.add(connection);
    }

}

那么DBManager中的连接部分代码就可以这样写

/**
     * 从连接池中获取connection,向外提供connection
     * @return
     */
    public static Connection getConnection(){
        return DBConnPool.poolCreateConnection();
    }

    /**
     * 创建真正的连接
     * @return
     */
    public static Connection createConnection(){
        try {
            Class.forName(configuration.getDriver());
            return DriverManager.getConnection(configuration.getUrl(),
                    configuration.getUsername(), configuration.getPassword());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 从连接池中关闭连接
     */
    public static void closeConnection(Connection connection){
        DBConnPool.poolCloseConnection(connection);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值