Druid与Dbutils集成实现查询结果集封装

本文详述了DBUtils与Druid在Java项目中的整合应用,通过具体案例展示了如何使用DBUtils简化JDBC操作,以及Druid作为高效数据库连接池的配置与使用。文章深入介绍了核心组件的功能,并提供了代码实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、前言

       DBUtils是apache下的一个小巧的JDBC轻量级封装的工具包,其最核心的特性是 结果集的封装 ,可以直接将查询出来的结果集封装成JavaBean,这就为我们做了最枯燥乏味、最容易出错的一大部分工作。
       核心类介绍:
       1:DbUtils:连接数据库对象----jdbc辅助方法的集合类,线程安全。作用:控制连接,控制驱动加载。与druid集成的话,此类用不到
       2:QueryRunner:SQL语句的操作对象。作用:可以设置查询结果集的封装策略,线程安全
       3:ResultSetHandle:封装数据的策略对象------将封装结果集中的数据,转换到另一个对象。策略:封装数据到对象的方式(eg:将数据库保存在自定义java bean、或保存到数组、保存到集合)

       Druid号称是Java语言中最好的 数据库连接池 。Druid能够提供强大的监控和扩展功能 。监控配置可参阅:

       https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE>

       https://github.com/alibaba/druid/wiki/配置_StatFilter

        本文以postgresql为例对druid与DBuitls的集成做一总结

2、需求

       项目中需要对postgresql数据库中存储的代理IP做检查,定时将过期的IP记录删除。定时功能看下一篇。

3、代码

       先添加依赖

dependencies {
    compile "commons-dbutils:commons-dbutils:${commonsDbutilsVersion}"
    compile 'org.apache.logging.log4j:log4j-core:2.8.2'
    compile "com.alibaba:druid:${druidVersion}"
    compile "mysql:mysql-connector-java:${mysqlConnectorVersion}"
    compile 'org.postgresql:postgresql:42.2.5'
}
ext {
    commonsDbutilsVersion = '1.6'
    druidVersion = '1.0.18'
    mysqlConnectorVersion = '5.1.37'
}

       代理IP的属性如下所示,其中字段ip包含了IP地址和端口,提前在数据库中新建表,字段与该类属性对应。

public class IPItem
{
    private Long id;
    private String did;
    private String ip;
    private String type;
    private String position;
    private String speed;
    private String lastCheckTime;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getDid() {
        return did;
    }

    public void setDid(String did) {
        this.did = did;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getPosition() {
        return position;
    }

    public void setPosition(String position) {
        this.position = position;
    }

    public String getSpeed() {
        return speed;
    }

    public void setSpeed(String speed) {
        this.speed = speed;
    }

    public String getLastCheckTime() {
        return lastCheckTime;
    }

    public void setLastCheckTime(String lastCheckTime) {
        this.lastCheckTime = lastCheckTime;
    }
}

       我们一般基于Druid做数据库连接池封装(通常设为单例),即读取配置文件中的数据库相关配置。在与dbutils集成的时候暴露一个QueryRunner对象,供其他类调用。将封装的数据库连接池命名为PostgreSQLPool ,代码如下:

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.**.ConfigParser;
import com.**.Logger;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.dbutils.QueryRunner;

public class PostgreSQLPool {
    private static PostgreSQLPool instance = null;
    private static final Logger logger = Logger.getLogger(PostgreSQLPool.class);
    private DruidDataSource dds;
    private QueryRunner runner;
    private Properties properties;

    public QueryRunner getRunner() {
        return this.runner;
    }

    private PostgreSQLPool() {
        ConfigParser parser = ConfigParser.getInstance();
        String dbAlias = "postgresql-data";
        Map<String, Object> dbConfig = parser.getModuleConfig("database");
        Map<String, Object> postgresqlConfig = (Map)parser.assertKey(dbConfig, dbAlias, "database");
        Properties properties = new Properties();
        String url = (String)parser.assertKey(postgresqlConfig, "url", "database." + dbAlias);
        String username = (String)parser.assertKey(postgresqlConfig, "username", "database." + dbAlias);
        String password = (String)parser.assertKey(postgresqlConfig, "password", "database." + dbAlias);
        properties.setProperty("url", url);
        properties.setProperty("username", username);
        properties.setProperty("password", password);
        properties.setProperty("maxActive", "20");
        this.properties = properties;

        try {
            this.dds = (DruidDataSource)DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception var10) {
            logger.error("Failed to connect data PostgreSQL db,Exception:{}", var10);
        }

        this.runner = new QueryRunner(this.dds);
    }

    public static PostgreSQLPool getInstance() {
        if (instance == null) {
            Class var0 = PostgreSQLPool.class;
            synchronized(PostgreSQLPool.class) {
                if (instance == null) {
                    instance = new PostgreSQLPool();
                }
            }
        }

        return instance;
    }
}

       这里可以自定义数据库配置文件的读取方式,将ConfigParser用你的读取方式替换即可,测试用的话也可先写死在里面。ConfigParser类为我司爬虫项目的配置读取类,主要是读取yaml文件,解析层次结构的配置对象。"config.yml"为项目路径src/main/resource或src/main/config目录下的配置文件名,ConfigParser代码如下:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Map;
import org.yaml.snakeyaml.Yaml;

public class ConfigParser {
    private static final Logger logger = Logger.getLogger(ConfigParser.class);
    private static ConfigParser instance = new ConfigParser();
    private static final String CONFIG_FILENAME = "config.yml";
    private Yaml yaml = null;
    private Object config;

    private ConfigParser() {
        if (this.yaml == null) {
            this.yaml = new Yaml();
        }

        File f = ResourceUtils.loadResouces("config.yml");

        try {
            this.config = this.yaml.load(new FileInputStream(f));
            logger.info("file {} is loaded", f.getAbsoluteFile());
        } catch (FileNotFoundException var3) {
            var3.printStackTrace();
        }

    }

    public static ConfigParser getInstance() {
        return instance;
    }

    public Object getConfig() {
        return this.config;
    }

    public Map<String, Object> getModuleConfig(String name) {
        return this.getModuleConfig(name, this.config);
    }

    public Map<String, Object> getModuleConfig(String name, Object parent) {
        Map<String, Object> rtn = (Map)((Map)parent).get(name);
        return rtn;
    }

    public Object assertKey(Map<String, Object> config, String key, String parent) {
        Object value = config.get(key);
        if (value == null) {
            logger.error("{}.{} is a mandatory configuration", new Object[]{parent, key});
            System.exit(0);
        }

        return value;
    }

    public Object getValue(Map<String, Object> config, String key, Object def, String parent) {
        Object value = config.get(key);
        if (value == null) {
            logger.warn("{}.{} is't configured, default value {} is used", new Object[]{parent, key, def});
            config.put(key, def);
            return def;
        } else {
            return value;
        }
    }

    public void dumpConfig() {
        System.out.println(this.yaml.dump(this.config));
    }
}

       config.yml配置如下:

apps:
  ## 基本属性
  spider-ipxundaili:
    common:
      group: ipproxy-xundaili-zhg
      cron: "0 */5 * * * ?"
      firstpage: 1
      totalpages: 1
      distribute: false
      fixed: true
      order: desc
    ## 数据源
    source:
      baseurl: http://api.xdaili.cn/xdaili-api//greatRecharge/getGreatIp?spiderId=***&orderno=***&returnType=2&count=10
      listpageregex: "http://www\\.xdaili\\.cn/"
    ## 存储路径
    storage:
      ## dbType: MySQL HBase Hive MongoDB Kafka
      dbtype: PostgreSQL
      dbalias: postgresql-data
    filter:
      searchfilter: false
      contentfilter: false
    ## 反爬虫
    antirobot:
      ipproxy: false
      listipproxy: false
      # 15分钟提取一次 一天提取96次 一次10个 共计960个IP
      sleeptime: 900000
    analysis:
      sentiment: false

distribute:
  scheduler: com.cetiti.ddc.scheduler.MemberScheduler
#  dbtype: redis
#  dbalias: redis

database:
  postgresql-data:
    url: "jdbc:postgresql://ip:port/dbname"
    username: postgres
    password: ***
    table-pre: ip_
    table: proxy
  redis:
    ip: 10.0.30.75
    port: 6379

       编写Dao层代码,delete方法为根据入参item的id删除ip_proxy表中的代理IP记录,另一个getAllItemFromDB方法是在ip_proxy表中查询出前2000条代理IP记录。其中查询结果集用new BeanListHandler(IPItem.class)封装。

import com.**.IPItem;
import com.**.PostgreSQLPool;
import com.**.logger.Logger;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class PostgreSQLDao {

    private static final Logger logger = Logger.getLogger(PostgreSQLDao.class);  
    private static QueryRunner runner;

    public PostgreSQLDao(){
        runner = PostgreSQLPool.getInstance().getRunner();
    }

    public void delete(IPItem item){
        String iSql = "delete from ip_proxy where id = ?";
        Object[] iParams={
                item.getId()
        };
        try {
            runner.insert(iSql,new MapHandler(),iParams);
        } catch (SQLException e) {
            logger.error("Failed to storage item to PostgreSQL db,Exception:{}",e);
        }
    }

    @SuppressWarnings("unchecked")
    public List<IPItem> getAllItemFromDB(){
        try {
            String qSql =  "select * from ip_proxy limit 2000";
            @SuppressWarnings("rawtypes")
            BeanListHandler blh = new BeanListHandler(IPItem.class);
            //以自定义类IPItem的list封装查询结果集
            return (ArrayList<IPItem>) runner.query(qSql,blh);
        } catch (SQLException e) {
            logger.error("getAllIpFromDB", e);
            return null;
        }
    }
}

封装处理策略如下:

  • ArrayHandler:把结果集中的第一行数据转成对象数组。
  • ArrayListHandler:把结果集中的每一行数据都转成一个对象数组,再存放到List中。
  • BeanHandler:将结果集中的第一行数据封装到一个对应的Java Bean实例中。
  • BeanListHandler:将结果集中的每一行数据都封装到一个对应的Java Bean实例中,存放到List里。
  • ColumnListHandler:将结果集中某一列的数据存放到List中。
  • KeyedHandler:将结果集中的每一行数据都封装到一个Map里,然后再根据指定的key把每个Map再存放到一个Map里。
  • MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
  • MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List。
  • ScalarHandler:返回指定列的一个值或返回一个统计函数的值,通常用于封装类似count、avg、max、min、sum······函数的执行结果

       接口调用(先查询出表中记录,然后逐一删除之):

public static void main(String[] args)  {
    List<IPItem> items =  postgreSQLDao.getAllItemFromDB();
    for(IPItem item : items){
        postgresql.delete(item);
        logger.info("cancel the proxy IP {} ! ",item.getIp());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值