• Javascript对象中使用setTimeout和setInterval的问题

        前两天写一个js时遇到个问题。就是在js对象内部如果用setTimeout或者setInterval调用该对象的一个方法,然后在被调用方法中就无法使用this获取该对象。因为经过setTimeout调用后,this就成了浏览器了,原来那个对象就丢失了。

          如:

    function TestObj(){
        this.property = "test";
        this.called = function(){
           alert(this.property);
         }
        this.setTimeCall = function(){
             setTimeout(this.called,500);
         }
     }
    
    var test = new TestObj();
    test.setTimeCall();  
    

     

    如果直接调用test.called方法是没有问题的。但如果通过setTimeout调用,也就是调用
    test.setTimeCall(),this.property就为空了。因为这里的this成浏览器了。同样的问题也发生在监听浏览器的其他事件
    上。没办法,于是用了个比较糟糕的办法。

     

    function TestObj(){
       this.property = "test";
       this.called = function(){
           alert(this.property);
    }
       this.setTimeCall = function(){
        setTimeout("test.called()",500);
    }
    }
    var test = new TestObj();
    test.setTimeCall();  
    

     

     之所以说这样做糟糕,是因为在定义TestObj时,就必须提前知道TestObj被创建时的变量名。后来在IBM社区看到一篇探讨javascript有限状态机的文章,用了一个很巧妙的办法避免这个问题。

    function TestObj(){
        this.property = "test";
        var self = this;
         this.called = function(){
             alert(self.property);
         }
    
         this.setTimeCall = function(){
             setTimeout(self.called,500);
         }
     }
    
     var test = new TestObj();
     test.called();
     test.setTimeCall();  
    

     

    在TestObj中定义一个变量,self,然后指向this。这样用self调用this,就不会发生和浏览器的this对象冲突的问题了。

     虽然是很简单的一个问题,但有时候人脑子转不过弯也没办法。

     

    还有一个网友评论提供的办法就是:

     

    function TestObj(){
    
        this.called = function(){
            alert("OK");
        }
    
        this.setTimeCall = function(){
            setTimeout(this.called.bind(this),500);
        }
    }   
    

     

  • mysqldump导出数据到mssql

    mysqldump -u root -ppassword --no-create-db --no-create-info --complete-insert --compatible=mssql
     --default-character-set=utf8 --skip-opt --compact  --extended-insert=false
     dbname tablename|sed "s/\'/''/g">tablename.sql
    

     

    –no-create-db 不输出建database的脚本

    –no-create-info 不输出创建table的脚本

    –complete-insert   完整的插入,输出每个字段(如: insert into table(field1,field2,….) values(value1,value2,…))

    –compatible=mssql 教本兼容格式,这里是mssql 这样教本里就会把table的名字和字段名用“号引起来,而不是mssql不能识别的`号。

    –default-character-set=utf8 默认编码

    –compact 输出尽量少的信息

    –extended-insert=false  禁用它,可以每行生成一句insert语句。否则只输出一个insert,如:insert
    into table
    values(value1,value2,…),(value1,value2,…),…(value1,value2,..)。这种格式
    sqlserver不识别。

     

     

    mysql中用转义 字符串里的 单引号等字符,而sqlserver的字符串中两个单引号表示一个单引号。没找到mysqldump指定转义字符的选项。有个fields-escaped-by选项,但只有在输出文本格式的时候有用,输出sql语句的时候不能用。

     

    所以只好用sed替换一下。

     

    sed "s/\'/''/g"
    

     

  • Spring自动装配(autowire)导致quartz不能运行

    在spring中配置了一个简单的quartz任务,结果报错:

    Failure obtaining db row lock: 第 1 行: 只有 DECLARE CURSOR 才允许使用 FOR UPDATE 子句。
    
    只有 DECLARE CURSOR 才允许使用 FOR UPDATE 子句
    
    at net.sourceforge.jtds.jdbc.SQLDiagnostic.addDiagnostic(SQLDiagnostic.java:368)
    	at net.sourceforge.jtds.jdbc.TdsCore.tdsErrorToken(TdsCore.java:2816)
    	at net.sourceforge.jtds.jdbc.TdsCore.nextToken(TdsCore.java:2254)
    	at net.sourceforge.jtds.jdbc.TdsCore.getMoreResults(TdsCore.java:631)
    	at net.sourceforge.jtds.jdbc.JtdsStatement.executeSQLQuery(JtdsStatement.java:477)
    	at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.executeQuery(JtdsPreparedStatement.java:777)
    	at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:91)
    	at org.quartz.impl.jdbcjobstore.StdRowLockSemaphore.executeSQL(StdRowLockSemaphore.java:92)
    

     

    很郁闷,以为是事务控制的问题。

    结果最后发现是自动配置(quartz)的问题。

    spring的SchedulerFactoryBean中有个dataSource属性。如果这个属性不为空,则用数据库来持久化储存quartz的任务。结果正好我的spring中的DataSource的名字就叫dataSource,结果就导致了上面的错误。

    这个哥们也遇到了同样的问题:

    所以spring的autowire还是尽量少用为好。

  • Lucene范围查询(RangeQuery)的几个问题

    对要进行范围查询(RangeQuery)的字段 创建索引的时候要按照 储存 不分词 的方式创建索引。

    new Field(name, value, Field.Store.YES, Field.Index.UN_TOKENIZED);

    1.数字的范围查询

    对数字进行范围查询,必须先把数字格式化为一样长度的字符串。位数不够的在前面补零。

    如:

    NumberFormat format = NumberFormat.getIntegerInstance();
    //设置数字的位数 由实际情况的最大数字决定
    format.setMinimumIntegerDigits(6);
    //是否按每三位隔开,如:1234567 将被格式化为 1,234,567。在这里选择 否
    format.setGroupingUsed(false);
    
    format.format(number);
    

     

    这样 1 将被格式化为 000001

    查询的时候也必须补零。

    查询语法: fieldName:[000001 TO 000100]

    演示:http://so.1ting.com/singer.do?q=singerIdRange:[000001 TO 000100]&sort=singerId

     

    2:日期范围查询

    日期范围查询必须需要用lucene的时间转换工具类(DateTools
    )的

    static String

    dateToString (Date date, DateTools.Resolution resolution)
    方法
    ,先把时间转换为字符串进行索引。

    查询语法: date:[6/1/2005 TO 6/4/2005]

    还可以overwrites
    QueryParser类的getRangeQuery(String, String, String, boolean)方法实现自定义的范围搜索。

     

     

  • ActiveMQ的一个问题

    安装了activemq,然后要配置成数据库持久化的。

    参考文档:

    这是个pdf教程,比较详细:

    启动之后报错:

     

    java.io.IOException: Failed to get last broker message id:
    com.mysql.jdbc.exceptions.MySQLSyntaxErrorException: Table 'activemq.activemq_acks' doesn't exist
    

     

     按道理数据表会在启动的时候自动创建。因为journaledJDBC有个属性createTablesOnStartup默认是true。并且查看了一下数据库,有两个表:

     

    ACTIVEMQ_LOCK
     ACTIVEMQ_MSGS
    

     

     

    搜索了一下,在activemq的邮件列表里找到答案:

     

    Specified key was too long; max key length is 1000 bytes is the error not
    allowing the creation of ACTIVEMQ_ACKS
    
    You are probably using utf8/some other multibyte encoding as the collation
    in your database....
    
    Switch it to latin1 or ASCII... The varchar fields that the key is composed
    of add up to less than 1000 characters but with a multibyte encoding the key
    length is over 1000 bytes.
    

     

     

    我创建activemq数据库的时候用的character-set是utf-8.把character-set改成latin1就好了。

  • 四个有害的java习惯

    本文不算严格意义上的翻译,算是该文章的摘要以及读后感

    文章原始地址:

    John O’Hanley 的这篇文章列举了四个有害的java编码习惯,并提出了改正方案。

    这四个习惯普遍存在于java程序员中,并且已经被广泛接受,大家都习以为常,流毒甚远。

    1.对局部变量(local variables),字段(fields),参数(method arguments)这三种变量的命名没有区分,造成了代码的阅读困难,增加了代码的维护成本。

    作者举了个例子:

     

    public boolean equals (Object arg) {
      if (! (arg instanceof Range)) return false;
      Range other = (Range) arg;
      return start.equals(other.start) && end.equals(other.end);
    }
    

     

     

    在这个方法中,arg直接用argument的缩写,虽然大家一看就知道这是参数了,但这种命名方式却丢失了参数代表的对象本身的含义。大家知道这
    是参数,却不知道这是什么参数。如果方法的参数多一点,都按照arg1,arg2这样的方式命名,阅读代码的时候很头疼。另外两个字段变量,start和
    end,突然凭空而出,想一下才知道这应该是字段。当然,这个方法很短,造成的困难还不大,如果这个方法比较长的话,突然看到start和end两个变
    量,一般会先在前面找一下是不是局部变量,然后才能确定是类的字段变量。

    这个问题貌似微不足道,但为什么要让代码阅读者花费额外时间在这些琐碎的问题上呢?如果有个方案能让代码阅读者一目了然的明白变量是那种变量,为什
    么不采用呢?就如同Steve McConnell在
    《代码大全》中说的:”让人费神去琢磨神秘杀人凶手这没有问题,但你不需要琢磨程序代码,代码是用来阅读的。”

    作者提出了解决方案:

    • 方法参数用前缀a开始
    • 类字段变量用前缀f开始
    • 局部变量不用前缀

    修正后的代码样式应该是:

     

    public boolean equals (Object aOther) {
      if (! (aOther instanceof Range)) return false;
      Range other = (Range) aOther;
      return fStart.equals(other.fStart) && fEnd.equals(other.fEnd);
    }
    

     

     

    这样的代码看起来一目了然,如果你没有一目了然,说明还是习惯问题,习惯养成了就好了。

     

    不过作者的方案里,给类字段变量前面加 f 前缀,如果用代码生成工具生成get,set方法是会比较麻烦。get,set方法中我们不希望出现个f。不过这个问题可以用修改代码生成工具的方式解决。如果这个习惯普遍被java界接受,这些应该都不成问题了。

     

    作者引用了一句名言:

     

    “By relieving the brain of all unnecessary work, a good notation sets
    it free to concentrate on more advanced problems, and in effect
    increases the mental power of the race. Before the introduction of the
    Arabic notation, multiplication was difficult, and the division even of
    integers called into play the highest mathematical faculties. Probably
    nothing in the modern world would have more astonished a Greek
    mathematician than to learn that … a large proportion of the
    population of Western Europe could perform the operation of division
    for the largest numbers. This fact would have seemed to him a sheer
    impossibility … Our modern power of easy reckoning with decimal
    fractions is the almost miraculous result of the gradual discovery of a
    perfect notation.”
    – Alfred North Whitehead, An Introduction To Mathematics

     

    个人理解大意为:

    把大脑从不必需的工作中解放出来,一个好的记号法可以让大脑去关注更高级的问题,提升民族的整体智力。在阿拉伯数字记号法被介绍进来以前,乘法是非
    常困难的,计算整除也需要非常高的数学才能。一个古希腊的数学家可能非常诧异于西欧的大多数人口可以计算很大数字的除法。这个事实对他来说似乎是不可能
    的。我们现代人很容易用小数算帐的能力几乎可以说是由逐渐探索出的一套完美的计数法而造成的奇迹。

    2.包(package)的命名和划分按照行为和层次划分(package-by-layer)而不是根据特征和功能划分(package-by-feature)

     

    这个问题在我刚学java的时候就遇到了,在看了众多的网上开源程序后,我也慢慢习惯了按层次命名包。

    作者举了个例子:

     

    • com.blah.action
    • com.blah.dao
    • com.blah.model
    • com.blah.util

    我们已经习惯了按照层次分类或者叫按照行为分类,model一个包,dao一个包,service一个包,action一个包。这样就把具有同样特
    征或者功能的类划分到了不同的包里。这样的习惯,把java的包内私有(package-private)这个作用域给完全扔掉了,而包内私有是java
    的默认作用域。(ps:我学java来好像很少用过java的包内私有这个作用域,汗一个)

    这中包的划分习惯也违反了面向对象编程的核心原则之–尽量保持私有以减少影响,因为这种习惯强迫你必须扩大类的作用域.

     

    下面的包命名方式是按照特征划分命名:

    • com.blah.painting
    • com.blah.buyer
    • com.blah.seller
    • com.blah.auction
    • com.blah.webmaster
    • com.blah.useraccess
    • com.blah.util

    举个例子,在一个web应用中,com.blah.painting包可能包含下面的成员:

    • Painting.java: model对象
    • PaintingDAO.java: dao对对象
    • PaintingAction.java:controller or action 对象
    • statements.sql: SQL文件
    • view.jsp: JSP文件

     

    值得注意的是这种情况下,包里包含的不仅仅是java源码文件,同时也包含其他与该特征相关的文件。这点上好像违反大多数java程序员的习惯,并且如果要打包为jar好像也不方便,真实环境中如何应用,有没有别的麻烦,还要待实践一下。

     

    作者列举了这种包划分方式的优点:

    • 包是高内聚的,并且模块化,包与包之间的耦合性被降到最低。

    • 代码的自文档性(或自描述性 self-documenting)增强. 读者只需看包的名字就对程序有些什么功能或特征有了大概的印象。在《代码大全》中,
      Steve McConnell 将自文档化(self-documenting)的代码比作 “the Holy Grail of legibility.”(不知道怎么翻译)

    • 把类按照每个特征和功能区分开可以很容易实现分层设计。

    • 相关的成员在同一个位置。不需要为了编辑一个相关的成员而去浏览整个源码树。

    • 成员的作用域默认是包内私有。只有当另外的包需要访问某个成员的时候,才把它修改为public.
      (需要注意的是修改一个类为public,并不意味着它的所有类成员都应该改为public。public成员和包内私有(package-
      private)成员是可以在同一个类里共存的。)

    • 删除一个功能或特征只需要简单的删除一个文件夹。
    • 每个包内一般只有很少的成员,这样包可以很自然的按照进化式发展。如果包慢慢变的太大,就可以再进行细分,把它重构为两个或者更多新的包,类似于物种进化。而按照层次划分的方式,就没办法进化式发展,重构也不容易。

    作者引用了一句Effective Java中的名言:

    “The single most important factor that distinguishes a well-designed module from a poorly designed one is the degree to which
    the module hides its internal data and other implementation details from other modules.”
    – Joshua Bloch, Effective Java

     

     

    3.习惯用JavaBeans而不是不可变对象

     

    按照javabeans的说明书(JavaBeans specification
    ),javabeans是用来解决特殊领域的问题:在图形界面程序的设计中充当小部件。但现在通常用javabean来做数据库记录的映射。作者反对javabean的这种用法。他提出了一个问题:

    假如你要从数据库记录集映射一行为对象,不考虑现有的持久化方案和框架,你会将这个对象设计成什么样子?跟javabean相似呢还是完全不一样?

     

    作者倾向于设计一个完全不一样的,他列举他的设计的几个特点:

     

    • 它不包含一个无参数构造方法(这一特征是javabean必备的。)。作者认为一个数据库记录的对象如果不包含任何数据是没有意义的。一个数据库表的所有字段都是可选的情况有多少?
    • It would likely not
      have anything to say about events and listeners.(不太明白作者的意思)

    • 它不强迫你用可变的对象。
    • 它内部有一个数据验证机制。这样一个验证机制对大多数数据库应用非常重要。(记住对象的第一原则:一个对象应该同时封装数据和对数据的操作。在这种情况下,操作就是验证数据。)

    • 数据验证机制可以给最终用户(end user)报错。

     

    作者的这一点我没看太明白。希望达人指点。作者不满于现有的数据库持久化框架滥用javabean,提出了几个观点,但没有具体的代码演示。据我的理解,作者的意思和前一段时间javaeye上争论的充血模型和贫血模型有点类似。

     

    4.类成员的排序没有按照成员的作用域(scope) 的大小从大到小排列,而是喜欢把private放在前面。

    作者举了一个常见的类样式:

     

    public class OilWell implements EnergySource {
       private Long id;
       private String name;
       private String location;
       private Date discoveryDate;
       private Long totalReserves;
       private Long productionToDate;
    
       public Long getId() {
          return id;
       }
       public void setId(Long id) {
          this.id = id;
       }
    
      //..elided
    }
    

     这种方式将private变量放在最前面。作者认为应该倒过来,把private变量的申明放在最后面。

    因为人们认识一个事物的通常过程都是从一般到特殊,从抽象层次来说,是从高到底的认识过程。如果你倒过来的话,就不能从整体上把握事物,也不能抓住事物的本质,只能在一堆具体的片段事实中迷失。

    整体的抽象允许你忽略细节。抽象的层次越高,你可以忽略越多的细节。读者阅读一个类时可以忽略的细节越多他会越高兴。脑袋里填充太多的细节是痛苦的,所以细节越少越好。因此,将private成员方在最后会显得更富有同情心,因为这样阻止了不必要的细节显露给读者。

     

    原来C 程序的习惯也是把private成员放在最开始。然而,C 社区迅速的认识到这是一个有害的规范,这个规范现在已经被修正。(参看 a typical C style guide
    )

     

    写道

    注意:public 接口应该放在class的最开始,其次是protected成员,最后是private成员。原因是:

    • 程序员应该更关心接口而不是具体实现。
    • 当程序员需要用一个类的时候,他们需要的是接口而不是实现。

    把接口放在开始是非常有意义的。把实现部分,private 片段,放在开始是一个历史遗留问题。最后还是要反复强调一下,一个类的接口的重要性超过实现细节。

     

     

    将私有成员放在类的开始是一个等待打破的怀习惯,它似乎是sun早期的编码规范造成的。

    将代码按照javadoc的顺序编排是非常好的:首先是构造方法,然后是非私有方法,最后是私有字段和方法。这样读者阅读的时候很自然的从抽象层次的高向低运动。

    作者的这个观点确实很有道理,以前也没仔细思考过。可以考虑给IDE做个重构插件,专门按照这种排序格式化代码。

     

    作者介绍:

    John O’Hanley
    javapractices.com
    的专栏作者,
    同时也是 WEB4J framework 
    的创建者。他拥有10年编程经验,现在居住于Prince Edward Island, Canada.

     

    参考资料:

     

subscribe via RSS