一、前言
本文提供提供一套JDBC流式查询的工具类。
二、
当遇到大数据量数据读取时,jdbc的流式读取可以提高一定的查询速度,对内存占用的优化比较明显。
/**
* JDBC读取数据功能(普通/流式)
* @param url 数据库地址
* @param username 数据库用户名
* @param password 数据库密码
* @param sql 执行语句
* @param clazz 结果集类型
* @param off 流式查询(true/开,false/关)
* @param size 每次读取条数
* */
public static <T> List<T> readResult(String url, String username, String password, String sql, Class<T> clazz, boolean off, int size){
Connection conn = null;
ResultSet rs = null;
try {
//获得数据库驱动的Class对象
//数据库版本在6.0以上的用(com.mysql.cj.jdbc.Driver)
Class.forName("com.mysql.jdbc.Driver");
//连接数据库
conn = DriverManager.getConnection(url,username,password);
PreparedStatement preparedStatement = null;
if (off){
//JDBC预编译
preparedStatement = conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
//一次性获取多少行数据
preparedStatement.setFetchSize(size);
preparedStatement.setFetchDirection(ResultSet.FETCH_REVERSE);
}else{
//JDBC预编译
preparedStatement = conn.prepareStatement(sql);
}
rs = preparedStatement.executeQuery();
//获取结果集的源数据
ResultSetMetaData metaData = rs.getMetaData();
//获取结果集列数
int columnCount = metaData.getColumnCount();
List<T> list = new ArrayList<>();
while (rs.next()){
T t = clazz.newInstance();
for (int i = 0;i<columnCount;i++){
//参数内容
Object columValue = rs.getObject(i+1);
//参数名
String columName = metaData.getColumnName(i+1);
//参数名驼峰式命名
String name = firstCharacterToUpper(columName);
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
field.set(t,columValue);
}
list.add(t);
}
return list;
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
try {
rs.close();
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
因为是用反射方法获取的实体类参数,所以需要对jdbc读取的字段名进行驼峰处理,这里再提供一个工具类
/**
* 参数名驼峰式命名
* @param srcStr 参数名
*/
public static String firstCharacterToUpper(String srcStr) {
if (StringUtils.isBlank(srcStr)){
return srcStr;
}
StringBuilder sbf = new StringBuilder();
String[] split = srcStr.split("_");
for (int i = 0, index = split.length; i < index; i++){
if (i == 0){
sbf.append(split[i]);
}else{
String s = split[i].substring(0, 1).toUpperCase() + split[i].substring(1);
sbf.append(s);
}
}
return sbf.toString();
}
测试类传参示例:
@Test
public void jdbcSelect(){
long startTime = System.currentTimeMillis();
List<UserJdbc> userList = JdbcUtils.readResult(druidDatabaseDefinition.getUrl(),druidDatabaseDefinition.getUsername(),druidDatabaseDefinition.getPassword(),"select * from t_user", UserJdbc.class,true,2000);
System.out.println("消耗时间:"+(System.currentTimeMillis()-startTime)/1000+"秒");
System.out.println("查询结果:"+userList.size()+"条");
}
工具类:
public class jdbcUtil {
/**
* JDBC读取数据功能(普通/流式)
* @param className 数据库驱动
* @param url 数据库地址
* @param username 数据库用户名
* @param password 数据库密码
* @param sql 执行语句
* @param off 流式查询(true/开,false/关)
* @param size 每次读取条数
* */
public static <T> List<T> readResult(String className, String url, String username, String password, String sql, Class<T> clazz, boolean off, Integer size){
Connection conn = null;
ResultSet rs = null;
try {
//获得数据库驱动的Class对象
//数据库版本在6.0以上的用(com.mysql.cj.jdbc.Driver)
Class.forName(className);
//连接数据库
conn = DriverManager.getConnection(url,username,password);
PreparedStatement preparedStatement = null;
if (off){
//JDBC预编译
preparedStatement = conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
if (ObjectUtils.isEmpty(size)){
size = 5000;
}
//一次性获取多少行数据
preparedStatement.setFetchSize(size);
preparedStatement.setFetchDirection(ResultSet.FETCH_REVERSE);
}else{
//JDBC预编译
preparedStatement = conn.prepareStatement(sql);
}
rs = preparedStatement.executeQuery();
//获取结果集的源数据
ResultSetMetaData metaData = rs.getMetaData();
//获取结果集列数
int columnCount = metaData.getColumnCount();
List<T> list = new ArrayList<>();
//获取对象中所有字段名
HashMap<String, Field> variateMap = new HashMap<>();
Field[] declaredFields =clazz.getDeclaredFields();
for (Field declaredField : declaredFields){
variateMap.put(declaredField.getName(),declaredField);
}
while (rs.next()){
T t = clazz.newInstance();
for (int i = 0;i<columnCount;i++){
//参数内容
Object columValue = rs.getObject(i+1);
//参数名
String columName = metaData.getColumnName(i+1);
//参数名驼峰式命名
String name = firstCharacterToUpper(columName);
//对象中没有的字段名直接跳过不赋值
if (!variateMap.containsKey(name)){
continue;
}
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
Object var = specialHanding(field,columValue);
field.set(t,var);
}
list.add(t);
}
return list;
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
try {
rs.close();
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
//mysql特殊字段类型特殊处理
private static Object specialHanding(Field field, Object columValue) {
Object var = columValue;
if (field.getType().equals(Integer.class) && (org.apache.commons.lang.ObjectUtils.equals(columValue,true) || org.apache.commons.lang.ObjectUtils.equals(columValue,false))){
if (columValue.equals(true)){
var = 1;
}else{
var = 0;
}
}
return var;
}
/**
* @param srcStr 参数名
*/
public static String firstCharacterToUpper(String srcStr) {
if (StringUtils.isBlank(srcStr)){
return srcStr;
}
//参数名驼峰式命名
StringBuilder sbf = new StringBuilder();
String[] split = srcStr.split("_");
for (int i = 0, index = split.length; i < index; i++){
if (i == 0){
sbf.append(split[i]);
}else{
String s = split[i].substring(0, 1).toUpperCase() + split[i].substring(1);
sbf.append(s);
}
}
return sbf.toString();
}
}
三、结尾
有不足的地方大家尽管指出,学无止境。也希望这篇文章可以帮到有需要的小伙伴。