分类 编程技术 下的文章

上次在慕课网看了一个Java分页原理与实践(上)的视频,看了觉得没有什么难得。昨天晚上无聊就想写下分页,毕竟这两年工作,我没有自己写过分页功能,只是用写好的,这个功能还是很重要的。

当然分页不只是Java才会有这种功能,跟编程语言没有丝毫关系。只是前几天,我在oschina上看到一个mybatis的分页插件mybatis-paginator,就像拿来做下实验。

分页,pagination,这个功能的作用我认为主要有两点:

  1. 服务端和客户端之间,可以减少数据传输量,不用每次都将所有的数据下发到客户端,每次下发一页或者几页就可以了;
  2. 服务端内,数据库不用每次都将所有的数据查出来,每次查指定页的数据即可,减轻了数据库的压力。

- 阅读剩余部分 -

本周测试同事,在测试我做的一个需求的时候,发现了这么一个bug。
CDMS下发下游系统的数据中,有几条数据有问题,因为其中两个字段是有关联的,都是从同一个字段扩展来的,明眼一看就有问题。
可是这几条数据,都是在cursor里处理的,不会出现,前一个字段扩展用的值是一个样,后一个字段扩展用的是另一个。
我就很费解,并且我在开发环境无法重现。我试了几组数据,下发都没有问题。
然后我就开始检查测试环境出错的数据,发现了猫腻。
现在我就拿Java代码模拟一下这个BUG。

下发下游的数据为Security,如下类所示,我简单的建了三个字段。

package com.chengjf.fxdemo;

public class Security {
    // 证券ID
    private String securityID;
    // 证券名称
    private String securityName;
    // 证券市场
    private String market;

    public Security(String securityID, String securityName, String market) {
        this.securityID = securityID;
        this.securityName = securityName;
        this.market = market;
    }

    public String getSecurityID() {
        return securityID;
    }

    public void setSecurityID(String securityID) {
        this.securityID = securityID;
    }

    public String getSecurityName() {
        return securityName;
    }

    public void setSecurityName(String securityName) {
        this.securityName = securityName;
    }

    public String getMarket() {
        return market;
    }

    public void setMarket(String market) {
        this.market = market;
    }

}

下发逻辑如下:

public class OracleException {

    // 模拟数据库
    private Map<String, String> db = new HashMap<String, String>(8);

    public static void main(String[] args) {

        OracleException exception = new OracleException();

        Collection<Security> collection = getSecurities();
        Iterator<Security> iterator = collection.iterator();

        // 要插入数据库的字段
        String systemID = null;
        String tradeID = null;

        while (iterator.hasNext()) {
            Security security = iterator.next();
            try {
                // 对systemID进行处理
                // !这块处理的时候,某些数据不符合条件,导致没有生成systemID
                // !而是用上一次处理生成的ID
                if ("1".equals(security.getMarket())) {
                    systemID = security.getSecurityID() + "_SYSTEM";
                }
                // 对tradeID继续处理
                tradeID = security.getSecurityID() + "_TRADE";

                // 插入数据库
                exception.insertIntoDB(systemID, tradeID);

            } catch (IllegalArgumentException e) {
                System.err.println(e);
            }
        }

        // 输出数据库结果
        System.out.println(exception.db);
        /*        
         * 000002_SYSTEM=600002_TRADE, 
         * 000001_SYSTEM=000001_TRADE, 
         * 000003_SYSTEM=000003_TRADE, 
         * 000004_SYSTEM=600004_TRADE
         * 000002竟然对应600002,00004竟然对应600004
         * 显然错误
         */
    }

    /**
     * 插入数据库,如果systemID没有则插入,有则更新 借助Map结构的特性
     * 
     * @param systemID
     * @param tradeID
     */
    private void insertIntoDB(String systemID, String tradeID) {
        if (systemID != null &amp;&amp; tradeID != null) {
            this.db.put(systemID, tradeID);
        } else {
            throw new IllegalArgumentException();
        }
    }

    /**
     * 数据源Cursor
     * 
     * @return
     */
    private static Collection<Security> getSecurities() {
        Security security1 = new Security("000001", "平安银行", "1");
        Security security2 = new Security("000002", "万科A", "1");
        Security security3 = new Security("600001", "邯郸钢铁", "2");
        Security security4 = new Security("600002", "齐鲁石化", "2");
        Security security5 = new Security("000003", "PT金田A", "1");
        Security security6 = new Security("000004", "国农科技", "1");
        Security security7 = new Security("600003", "ST东北高", "2");
        Security security8 = new Security("600004", "白云机场", "2");

        return Arrays.asList(new Security[] { security1, security2, security3,
                security4, security5, security6, security7, security8 });
    }
}

出现BUG原因的,就是在对数据进行处理的时候,没有对每条数据的context进行初始化。下一条数据因为一些逻辑,某些值竟然和上一条数据的某些值相同或者有关系。最终造成了数据不一致的结果。

然而,我发现这种代码在系统中很多,特别是在PL/SQL中。一般变量只在begin开头初始化,每次cursor的loop里都没有重新初始化,都有这种BUG的隐患。

同时,这种BUG应该是最简单的一个BUG,但是可能也是最容易犯的。这种BUG发生的时候跟处理的数据有关系,一般数据正常的时候不会出现这种BUG,但是数据如果有问题,就不灵了。

这种BUG不容易发现,但是应该谨记:

数据初始化非常重要。保证数据处理时的context一切正常。

最近学习PHP,使用Slim Framework搭建,其中要实现一个获取当日天气的小功能来练手,使用了网上推荐的Requests库来用。

昨晚一切正常,使用的是百度提供的API。今天早上开机,发现获取不到数据了,查看报错信息,发现Requests总在我的URL后加上1080端口进行访问。

我看了下报错信息:

'cURL error 7: Failed connect to apistore.baidu.com:1080; No error'

而1080端口是我本地ShadowSocks的端口,我为了加快某些服务的速度,使用的代理,比如Git,Composer等。我打开ShadowSocks,果然,又可以获取到天气数据了。我确认是这个1080端口有问题。

但是,我并没有指定Requests的代理啊,按照官网上说的,指定代理方法为:

$options = array(
    'proxy' => '127.0.0.1:3128'
);
Requests::get('http://httpbin.org/ip', array(), $options);

我并没有这样做啊。

Requests使用的是PHP的cURL,难道我配置了cURL的某些变量?我又在想,我搜了Requests的代码,发现有一处设置CURLOPT_PROXY的地方:

public function curl_before_send(&$handle) {
        curl_setopt($handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
        curl_setopt($handle, CURLOPT_PROXY, $this->proxy);

        if ($this->use_authentication) {
            curl_setopt($handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
            curl_setopt($handle, CURLOPT_PROXYUSERPWD, $this->get_auth_string());
        }
    }

可是这块的$this->proxy是在Requests中配置的,所以并不是这块有问题。

那就可能是PHP的cURL本身配置了一些东西。cURL有配置属性的方法curl_setopt,但是并没有相对应的获取属性的方法,只有一个curl_getinfo,这个方法只能获取部分属性,我需要的代理属性并不能获取到。

So,现在仍然没辙……

纵观各大语言及Web开发框架(先挖个坑),配置URL和Controller的方式有以下几种:

  1. XML配置
  2. 注解
  3. 代码

XML配置

来个Spring MVC的:

Go语言的Web框架Revel采用配置文件,但不是XML格式的,我觉得可以放在这里(代码来自Revel官方文档

# conf/routes
# This file defines all application routes (Higher priority routes first)
GET    /login                Application.Login       # A simple path
GET    /hotels/              Hotels.Index            # Matches with or without trailing slash
GET    /hotels/:id           Hotels.Show             # Extract an embedded argument
WS     /hotels/:id/feed      Hotels.Feed             # WebSockets.
POST   /hotels/:id/:action   Hotels.:action          # Automatically route some actions.
GET    /public/*filepath     Static.Serve("public")  # Assets served from /public/...
*      /:controller/:action  :controller.:action     # Catch all; Automatic URL generation

- 阅读剩余部分 -

在用Java开发的时候,往往会使用依赖注入框架,最常见的莫过于Spring或者Google Guice。

最常见的注入方式,一时XML配置文件(XML Configuration),二是注解(Annotation)。

然而,我非常抵触使用注解,只有不得不用的时候采用,比如@Override(当然Override也可以不写,当时我还是觉得写上可能更清晰些),或是其他一些消除警告的注解。

我不明白我抵触注解的原因是什么,我在写python的时候,经常使用装饰器(Decorator),类似于Java的注解,然而在写Java的时候,却不喜欢这样,这个也是令人困惑。

最近写代码的时候,我需要用依赖注入框架开管理我的Controller,Service,Dao,Model等对象。我使用了Spring,可是Spring太重了,我只是单单想使用它的DI功能,就需要好几个Jar包。如果你需要使用它来管理数据源的话,可能会引入更多的,如jdbc,tx,aop等。并且网上说这些组合起来非常方便,一套下来正好。我不喜欢这样。


2015-09-09更新
我最后还是放弃了XML配置的方式,也许只是目前放弃了XML配置的方式。
现在用Guice对项目进行了改造,原来用的是Spring,全部采用了注解的方式。