Druid與Dbutils集成實現查詢結果集封裝

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());
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章