2012年5月30日水曜日

[python]MySQLdb

pythonでMySQLdbを使ってMySQLに接続したかった。
簡単なはずなのにけっこうハマった。
windows7 pro + traclightning という環境が仇になった。。。


traclightning インスコしてると 環境変数 PATH に TRACPATH が設定されてて、
これによってpython2.6にパスが通ってる。
なので TRACPATH の python周りのパスを外した。

※環境変数触るとき Rapid Environment Editor 使ってる。ちょっと楽。管理者権限で実行しないと値を変更できないよ。

で、python2.5なら
http://sourceforge.net/projects/mysql-python/files/
http://sourceforge.net/projects/mysql-python/files/mysql-python/
http://sourceforge.net/projects/mysql-python/files/mysql-python/1.2.2/
あたりにWindows用のexeがある。

python2.5でもよかったんだけれど、
http://www.codegood.com/archives/129
にpython2.7 Windoes用のexeがあったんで2.7を使うことにした。

ちなみにsetup.py build とか install やると
error: Unable to find vcvarsall.bat
で怒られて
http://stackoverflow.com/questions/2817869/error-unable-to-find-vcvarsall-bat
mingw云々でめんどくさなったので放置。
あとmysql_root ってクライアントツールのパスなん?ってのがよくわからんかったのでさらに放置。
http://techracho.bpsinc.jp/baba/2010_04_01/1286

で、元に戻ると、python2.7インスコしてなかったらexeからYesYesYesしてインスコ。
環境変数の設定で PYTHONHOMEで インストールディレクトリ C:\Python27 を指定しつつ、PATH に
%PYTHONHOME%\
%PYTHONHOME%\Scripts
%PYTHONHOME%\DLLs\
%PYTHONHOME%\Lib
%PYTHONHOME%\Lib\lib-tk
%PYTHONHOME%\Lib\site-packages
を追加。

where python とか python --version でパスやバージョンを確認しとく。
2.7が実行されてればOK。
なってなかったらコマンドプロンプト一回落とすとかOS再起動とかして環境変数ちゃんと設定されていたらパスも変わる。

んで、easy_install 使って楽したいので python ez_setup.py を実行してインスコ。

これでpython2.7環境できたので、

http://www.codegood.com/archives/129
から落としてきたexe実行してYesマンする。

import sys
print sys.path とかでpython2.7のsite-packagesにパス通ってるかみとくといいかも。
ImportError: No module named MySQLdb
とかなっちゃからね。

したらば、次のコードでselectしてみる。
http://taichino.com/programming/1259


#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
print sys.path

import MySQLdb
from MySQLdb.cursors import DictCursor

def process_row(row):
    print row

con = MySQLdb.connect(host='192.168.X.X', db='XXX', user='XXX', passwd='XXX', charset="utf8")

# SELECT
cur = con.cursor()                        # connectionから取得したcursorからsqlを発行する
print cur.execute('SELECT * FROM ur.diaries')  # executeは行数を返却
res = cur.fetchall()                      # fetchone, fetchmany, fetchallで結果取得
for row in res:
    process_row(row)                        # 各行はtuple (10L, 'Book A', 1000L, None)


文字コード的なところが…。
ちゃんと文字コード指定したらいけるみたい。
文字コード指定してなかったらselectの結果、日本語文字列が??????になってたけど
ちゃんと指定したらユニコード文字になってた。

updateとかinsertもきっといけるだろう。

もうおなかいっぱい。

2012年5月23日水曜日

[java]springbatchのジョブ設定


適当なジョブ設定ファイルのサンプル。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


    <!-- enables the functionality of JobOperator.startNextInstance(jobName) -->
    <bean id="jobParametersIncrementer"
          class="org.springframework.batch.core.launch.support.RunIdIncrementer"/>


    <job id="accountBkJob" xmlns="http://www.springframework.org/schema/batch" incrementer="jobParametersIncrementer">
        <step id="accountBk" parent="accountBkStep"/>
    </job>


    <step id="accountBkStep" xmlns="http://www.springframework.org/schema/batch">
        <tasklet>
            <chunk reader="accountBkSource" writer="accountBkWriter"
            commit-interval="${job.commit.interval}"/>
        </tasklet>
    </step>


    <bean id="accountBkSource" class="org.springframework.batch.item.database.JdbcPagingItemReader">
        <property name="dataSource" ref="dataSource"/>
        <property name="rowMapper">
            <bean class="sample.domain.internal.AccountBkMapper"/>
        </property>
        <property name="queryProvider">
            <bean class="org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean">
                <property name="dataSource" ref="dataSource"/>
                <property name="fromClause" value="from proto_spring_batch.account "/>
                <property name="selectClause" value="select proto_spring_batch.account.* "/>
                <property name="sortKey" value="ID"/>
            </bean>
        </property>
        <property name="pageSize" value="10"/>
    </bean>


    <!--<bean id="accountBkSource" class="org.springframework.batch.item.database.JdbcCursorItemReader">-->
        <!--<property name="dataSource" ref="dataSource"/>-->
        <!--<property name="rowMapper">-->
            <!--<bean class="sample.domain.internal.AccountBkMapper"/>-->
        <!--</property>-->
        <!--<property name="sql">-->
            <!--<value>-->
                <!--select proto_spring_batch.account.* from proto_spring_batch.account order by-->
                <!--proto_spring_batch.account.id asc-->
            <!--</value>-->
        <!--</property>-->
        <!--<property name="verifyCursorPosition" value="${batch.verify.cursor.position}"/>-->
    <!--</bean>-->


    <bean id="accountBkWriter" class="sample.domain.internal.JdbcAccountBkDao">
        <property name="dataSource" ref="dataSource"/>
    </bean>


    <bean id="accountBkProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="properties">
            <value>
                job.commit.interval=5
            </value>
        </property>
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
        <property name="ignoreUnresolvablePlaceholders" value="true"/>
        <property name="order" value="1"/>
    </bean>


</beans>

これはDBからページング機能を利用してデータを読み込んで、DBに出力するジョブの設定。
コメントアウトでカーソル機能版。
ちなみにこの設定では、1ページ10レコード読み込んで5件ごとにコミット。
1ページで2回コミットするわけだけど、1回目のコミット後に例外発生して処理が失敗しても
1ページ目の続きからリランしてくれる。
なかなか便利。
MySQLな人には当たり前だけどMyISAMだとロールバックできないからテーブルのtype=InnoDBを忘れずに。
show tables status; とかで見えたはず。

具体的には、同じテーブル構成のaccountテーブルからaccount_bkテーブルにデータを少しずつコピーしていくだけ。
SQLクエリで一発だけど理解するために。
whereClauseを外してるけど、Where句を使用するときには指定する。
Where句のパラメータを動的に渡したい時はどうやるんかな…。

MapperとかItemWritterとかはたいして特別なことはせずにサンプルを見ながら適当に実装。

ItemWritterとか。
package sample.domain.internal;

import org.springframework.batch.item.ItemWriter;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;
import sample.domain.internal.model.Account;

import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * User: hoge
 * Date: 12/05/23
 * Time: 18:58
 * To change this template use File | Settings | File Templates.
 */
public class JdbcAccountBkDao extends SimpleJdbcDaoSupport implements ItemWriter<Account> {

    public static final String INSERT_ACCOUNT_BK =
            "INSERT into proto_spring_batch.account_bk (id, account_id, account_name, insert_date, update_date, delete_flg)" +
                    " values (:id, :accountId, :accountName, :insertDate, :updateDate, :deleteFlg) ON DUPLICATE KEY UPDATE " +
                    "account_id = :accountId, account_name = :accountName, update_date = :updateDate";

    public void write(List<? extends Account> accounts) throws Exception {
        for (Account account : accounts) {
            getSimpleJdbcTemplate().update(INSERT_ACCOUNT_BK, new BeanPropertySqlParameterSource(account));
        }
        if (accounts != null) {
            throw new Exception("任意で発生させたエラーだよ");
        }
    }
}


Mapperとか。
package sample.domain.internal;

import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
import sample.domain.internal.model.Account;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;

/**
 * Created with IntelliJ IDEA.
 * User: hoge
 * Date: 12/05/23
 * Time: 18:48
 * To change this template use File | Settings | File Templates.
 */
public class AccountBkMapper implements ParameterizedRowMapper<Account> {
    public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
        Account account = new Account();
        account.setId(rs.getLong(1));
        account.setAccountId(rs.getString(2));
        account.setAccountName(rs.getString(3));
        account.setInsertDate(new Date());
        account.setUpdateDate(new Date());
        account.setDeleteFlg(false);
        return account;
    }
}

そんなもんかな。

[java]springbatch

springbatchを動かすのに何が主役なのかってのがよくわからなかった。
EEなことは全然知らないのでBeanとかけっこうきつい。

で、とりあえず雰囲気わかってきたのでメモ。

XMLの設定周りで大切なのは、基盤周りの設定を記述するものとジョブの設定。
サンプルみていると次の感じでインポートさせるのが良いっぽい。

sample.xml

    <import resource="classpath:/back-context.xml"/>
    <import resource="classpath:/META-INF/spring/job1.xml"/>

実行クラス:org.springframework.batch.core.launch.support.CommandLineJobRunner
実行引数:classpath:/sample.xml job1


試してみたのは、
  • CSVファイルを読み込んで、ファイル出力
  • OSコマンド実行
  • 独自のタスクレット
  • CSVファイルを読み込んで、DB出力
  • DBから読み込んで(カーソル)、DB出力
  • DBから読み込んで(ページング)、DB出力
くらいです。
DBから読み込んで(ページング)、DB出力があればカーソルはいらいない子かもです。

サンプルやドキュメント、ネットで調べながらなので苦労しましたがなんとか理解できはじめた感じ。


参考

参考サイト、とてもありがたいです。

[IntelliJ IDEA]ソースやjarなどの出力方法

intelliJ IDEAでjavaのファイルってclassはExcluded Foldersに出力されているんですが、
jarファイルで出力ってどうやんねん?
って思ったのでメモ。

簡単。

Artifactsで+押してJarのEmptyかFrom modules with dependenciesを選択。

From modules with dependenciesにすると依存しているjarすべてが含まれるが、
メンテナンスしづらいっす。

わかりやすいんですけどね。

事故起こすくらいなら別にオールインワンパッケージでいいけど、
まあ外部jarファイルは外部jarファイルにしたいからEmpty。

Nameに適当な名称を入力して、Output Layout の XXX.jar で+を押下してModuleOutputを選択。

上のほうのOutput directory で出力先を確認しとく。

あと Build on make にチェックしとく?

で、Ctrl + F9 でコンパイルしたら出力されるっぽい。



ソースも同じような感じで出力できるっぽい。


Artifactsで+押して Other を選択。



Output Layout で directory content を選択してプロジェクトのソースフォルダを選択。

したらばソースファイルもろもろ出力されているっぽいっす。



2012年5月22日火曜日

[IntelliJ IDEA]springbatchのpom.xmlから読み込んだ時だけのエラー

intelliJ IDEA11でspring-batch-2.1.8.RELEASE からサンプルプロジェクトを
File > OpenProject > pom.xml でプロジェクトを開き、
とりあえずサンプルを実行したら

[spring-batch-simple-cli] The JAR is empty: dot



Error: No value found for placeholder 'version'

になってコンパイルができていないって話に。

※実行パラメータとか
実行クラス:org.springframework.batch.core.launch.support.CommandLineJobRunner
実行引数:classpath:/launch-context.xml job1

で、ようわからんずに
OSGIの設定を削除したらいけた。

spring-batch-2.1.1.RELEASE は素直に動いてくれたのでこっちを参考にしてみた。

なんか、IDEAでpom.xmlからプロジェクトを開くときにクラスパスの設定がうまくいってないとかなんとか。

解決したのかどうかもよくわからないイシューだったので力技で解決。


2012年5月13日日曜日

[java]solrjでBasicAuth


Caused by: org.apache.solr.client.solrj.SolrServerException: org.apache.commons.httpclient.ProtocolException: Unbuffered entity enclosing request can not be repeated.
at org.apache.solr.client.solrj.impl.CommonsHttpSolrServer.request(CommonsHttpSolrServer.java:475)
at org.apache.solr.client.solrj.impl.CommonsHttpSolrServer.request(CommonsHttpSolrServer.java:244)
at org.apache.solr.client.solrj.request.AbstractUpdateRequest.process(AbstractUpdateRequest.java:105)
at org.apache.solr.client.solrj.SolrServer.deleteById(SolrServer.java:102)



公開前でまじであせった。

HttpClient httpClient = new HttpClient();
// 認証情報(ユーザ名とパスワード)の作成.
Credentials defaultcreds1 = new UsernamePasswordCredentials(user, password);
// 認証のスコープ.
AuthScope scope1 = new AuthScope(hostName, 80, AuthScope.ANY_REALM);
// スコープと認証情報の組合せをセット.
httpClient.getState().setCredentials(scope1, defaultcreds1);
httpClient.getParams().setAuthenticationPreemptive(true); // これをいれたらOK
server = new CommonsHttpSolrServer(_solrhost, httpClient);


これでいけた。

2012年5月12日土曜日

[java]tomcat上のapache solrのBasic認証とマスタースレーブ設定


tomcatでのBasic認証はこちら。
http://osima.jp/blog/tomcat-auth/

$CATALINA_HOME/conf/tomcat-user.xml

    <?xml version='1.0' encoding='utf-8'?>
    <tomcat-users>
      <role rolename="role_foo"/>
      <user username="foo" password="hogehoge" roles="role_foo"/>
    </tomcat-users>



$CATALINA_HOME/webapps/mywebapp/WEB-INF/web.xml 
    <web-app>
    
    ...省略
    
     <security-constraint>
      <web-resource-collection>
       <web-resource-name>Test</web-resource-name>
       <url-pattern>/*</url-pattern>
      </web-resource-collection>
      <auth-constraint>
       <role-name>role1</role-name>
      </auth-constraint>
     </security-constraint>
     
     <login-config>
      <auth-method>BASIC</auth-method>
      <realm-name>Test</realm-name>
     </login-config>
     
     <security-role>
      <role-name>role1</role-name>
     </security-role>
    
    </web-app>
    



んで、もしSolrのマスターサーバがBasic認証かかるのであれば、
スレーブのsolrconfig.xmlで次の記述を入れる。
http://wiki.apache.org/solr/SolrReplication


        <str name="httpBasicAuthUser">username</str>
        <str name="httpBasicAuthPassword">password</str>




Slave


<requestHandler name="/replication" class="solr.ReplicationHandler" >
    <lst name="slave">

        <!--fully qualified url for the replication handler of master . It is possible to pass on this as a request param for the fetchindex command-->
        <str name="masterUrl">http://master_host:port/solr/corename/replication</str>

        <!--Interval in which the slave should poll master .Format is HH:mm:ss . If this is absent slave does not poll automatically.
         But a fetchindex can be triggered from the admin or the http API -->
        <str name="pollInterval">00:00:20</str>
        <!-- THE FOLLOWING PARAMETERS ARE USUALLY NOT REQUIRED-->
        <!--to use compression while transferring the index files. The possible values are internal|external
         if the value is 'external' make sure that your master Solr has the settings to honour the accept-encoding header.
         see here for details http://wiki.apache.org/solr/SolrHttpCompression
         If it is 'internal' everything will be taken care of automatically.
         USE THIS ONLY IF YOUR BANDWIDTH IS LOW . THIS CAN ACTUALLY SLOWDOWN REPLICATION IN A LAN-->
        <str name="compression">internal</str>
        <!--The following values are used when the slave connects to the master to download the index files.
         Default values implicitly set as 5000ms and 10000ms respectively. The user DOES NOT need to specify
         these unless the bandwidth is extremely low or if there is an extremely high latency-->
        <str name="httpConnTimeout">5000</str>
        <str name="httpReadTimeout">10000</str>

        <!-- If HTTP Basic authentication is enabled on the master, then the slave can be configured with the following -->
        <str name="httpBasicAuthUser">username</str>
        <str name="httpBasicAuthPassword">password</str>

     </lst>
</requestHandler>

2012年5月10日木曜日

[java]GAEのインデックス節約でマージジョインとかワイルドカード的な工夫とか

前、http://zonotex.blogspot.jp/2012/04/gae.html っていったけどこれ今なら解決できそう。

マージジョインで。

カラム1-asc、日付-desc

カラム2-asc、日付-desc

カラム3-asc、日付-desc

こういうインデックスあればコストはかかるけどマージジョインによる表結合でインデックスの問題は解消する。


まあこれだけならコンポジットインデックス作ったほうがいいんだけど、
組み合わせが多い(フロントの検索)をGAEのKVSで実現するならこういうインデックスを作ったほうがいい。


それか http://d.hatena.ne.jp/kissrobber/20100916/1284623867 にあるように、
ワイルドカード的な文字列をアプリを作る前に決めておいて
フロントで指定されなかった時にその文字列を検索対象として補完する仕組みにしておくとかね。

[python]list.sort() と sotred(list) の違い

pythonで配列をソートするのがすごい簡単。

list.sort()

new_list = sorted(list)

重複を省く集合を作るには

s = set(list)

集合をソートするなら

new_s = sorted(s)

重複していた項目を見るのはどうするんだろう。
原始的にやるなら連想配列のキーに設定して
キーの存在チェックかな。