一、本篇主要完成查询功能
- 多行多列 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);
}