盒子
盒子
文章目录
  1. 一、本篇主要完成查询功能
  2. 二、完成queryRows,多行多列查询
    1. 2.1 多表联查
  3. 三、查询一行多列
  4. 四、一行一列
  5. 五、完善代码的小优化
    1. 5.1 优化代码的重用性
    2. 5.2 优化查询的代码重用性
    3. 5.3 优化连接池

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

  • 其中需要明白一点,就原始数据的类型即使扩大范围了,但是得到class还是原始的类型,如下

    1
    2
    3
    String s = "hjh";
    Object s1 =s;
    System.out.println(s1.getClass()); //java.lang.String
  • queryRows代码如下,这里需要用到查询结果数据库的元数据通过java.sql.ResultSet.getMetaData(),\
    之前获取表结构是通过connection.getMetaData()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    @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方法省略)

1
2
3
4
5
6
7
8
9
10
11
12
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;
}

测试代码

1
2
3
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);

结果:

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

三、查询一行多列

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

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

四、一行一列

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@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用于对不同的结果集的处理
    代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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,代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    /**
    * 基础的查询
    * @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 优化连接池

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

连接池代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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中的连接部分代码就可以这样写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 从连接池中获取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);
}

希望对您有所帮助
May all the ordinary are great, all the ignoble bloom
  • smile