2013/06/07

C#製の簡易O/Rマッパー PetaPoco を試してみる

PetaPoco

PetaPoco .NETの簡易O/Rマッパー。

とりあえずPostgreSQLで試してみる。 Getting Started With PetaPoco and Postgres

環境

  • Windows XP SP3
  • .Net Framework 4
  • PostgreSQL 8.4

プログラム作成手順

Databaseの準備

  • server: localhost
  • database: petapoco
  • owner: postgres

Visual Studioでコーディング

プロジェクト作成

コンソールアプリケーションのプロジェクトを作成

  • 名前: PetaPocoTest

NuGet Package追加

NuGetすごい便利。
以下のパッケージを追加

  • PetaPoco.CORE
  • Npgsql
  • log4net

PetaPocoを追加すると、勝手にModelsフォルダが生成される。

POCOクラスの作成

いわゆるModel Class。
usersテーブルに合わせて以下のように作成

主キーが複数の場合は [PetaPoco.PrimaryKey("key1,key2")]

PetaPocoに管理させたくないプロパティには [PetaPoco.Ignore]

DataManagerクラスの作成

Databaseに接続し、PetaPocoを使用してデータを取得するクラス。

コンストラクタ

DbProviderFactoriesの指定は app.config にも書けるけど、記述が面倒なので引数で渡した。

GetUserBySystemIdメソッド

PetaPoco.Sql.BuilderにてSQLを組み立て。
きっと引数とかはちゃんとエスケープしてくれるのだろう。

テーブル作成時にキチンと指定していないけど、 system_idはユニークな想定なので、Fetchで返されたListの先頭を取得している。

Program.cs

Databaseからuserを取得し、名前をログに出力。

あたりまえだが、実際には GetUserBySystemId から返ってきた値が null かどうかチェックしないといけない。


参考

2013/05/15

WindowsでGitを使う(proxyとかFirewallの設定)

企業内のネットワーク配下でGitを使う際の設定について。
Windows XPでやってます。

Git for Windows をインストール

ウィザード内の選択肢で、コマンドプロンプトでgitを使うよう選択する。

以降の作業はコマンドプロンプトとGit bashどちらでも可能。

初期設定

git config --global user.name kazunori.kimura
git config --global user.email kazunori.kimura.js@gmail.com

proxy設定

Proxy配下の場合は以下の設定を行う。
環境変数 HTTP_PROXY を設定してたけど、見てくれなかった。

git config --global http.proxy http://{proxy_host}:{proxy_port}
git config --global https.proxy http[s]://{proxy_host}:{proxy_port}

gitプロトコルの対応

Firewallで git:// の通信が阻害されてしまう場合は。。。

$ git clone git://github.com/Kazunori-Kimura/MyPowerShellSnippet.git
Cloning into 'MyPowerShellSnippet'...
fatal: unable to connect to github.com:
github.com[0: 204.232.175.90]: errno=No such file or directory

以下の設定によって、自動的に git:// を https:// に置き換えて通信してくれる。

$ git config --global url."https://".insteadOf git://



2013/05/13

node で Oracle に接続する

Oracle接続

Windowsにて、nodeでOracle 11gに接続し、値を取得したい。

結論

node-odbcで動いた。

事前準備

  • python
  • node-gyp

    • gyp: Generate Your Projects
    • Google Chromeのためのビルドシステム
    • pythonで書かれている
    • node-gyp: gypによるビルドファイルの生成からコンパイルまで行える
  • python2.7をインストール後、node-gypをglobalオプション付きでインストール
    npm install -g node-gyp

node-odbcインストール

npm install node-odbc

node-gypのbuildで3箇所警告が出たが、一応インストールは成功した。

動作確認

とりあえず、単純なselect文を投げて結果を取得してみる

  • app.js
/*
 * oracle test
 */

var odbc = require("odbc");

var db = new odbc.Database();
var cs = "DSN=<DataSourceName>;UID=<UserName>;PWD=<Password>";
db.open(cs, function(err){
    var sql = "select foo, bar from HOGE order by foo";
    db.query(sql, function(err, rows, rs){
        //console.log(rows);
        var i=0;
        for (i=0; i<rows.length; i++) {
            console.log([
                i,
                rows[i]["foo"],
                rows[i]["bar"]
            ].join(", "));
        }

        db.close(function(){
            console.log("close.");
        });
    });
});

試行錯誤の経緯

試したmodule

  • node-oracle

    • 上手くインストールできない。コンパイルで失敗する。
    • node-gypで、-zオプションの使い方がおかしいといったエラー。
  • node-db-oracle

    • node-wafを使用しており、コンパイルで失敗する。
  • node-odp

    • インストールはできるが、うまく動かない。
    • module内にバイナリファイル nodeodp.node があるが、これがx64用?

2013/05/06

[備忘録] node.js環境構築

node.js環境 (nvm, npm) をインストールする手順。今回はMacにて行ったが、Gitを使える環境であればWindows/Linuxで(ほぼ)共通。

Githubからnvmを取得する。

cd ~
git clone git://github.com/creationix/nvm.git .nvm
. .nvm/nvm.sh

nvmでダウンロード可能なnodeの最新バージョンを確認する。

nvm ls-remote

最新バージョンのnodeをインストール

nvm install v0.11.1

インストールされたか確認。nvmインストール時に最新のnpmもインストールされている。

node -v
npm -v

次回ログイン時以降に .nvm/nvm.sh が読み込まれるように、~/.bash_profile に登録

. ~/.nvm/nvm.sh

2012/05/31

PowerShellでSQLServerのデータベースのバックアップ/リストアを行うスクリプト

SqlCmdコマンドを使用してSQL Serverのデータベースをバックアップ/リストアするスクリプト。

GitHub: Backup-Database.ps1

#Backup-Database.ps1
#simple backup & restore script for SQLServer2008

Param(
  [switch] $backup,
  [switch] $restore,
  [string] $database="",
  [string] $server=$env:computerName,
  [string] $file="",
  [string] $directory="",
  [switch] $help,
  [switch] $debug
)

$usage=@'

名前
    Backup-Database

概要
    SQL Serverデータベースの完全バックアップを作成/復元します。

構文
    Backup-Database {-Backup | -Restore} -Database  [-Server ] [-File  | 
     -Directory ]

説明
    SqlCmdコマンドを使用し、SQL Serverデータベースの完全バックアップを作成/復元します。
    SQL ServerにはWindows認証にて接続します。スクリプト実行アカウントにバックアップ/リストアを実施する
    権限がない場合、エラーとなります。
    
    File オプションを指定した場合、Directory オプションは無視されます。
    保存先とファイル名の両方を指定する場合 ( たとえば、D:\backup に backup.db という名前で作成したい場合 ) は
    File オプションにパスを指定してください。
    
    Backup時に File オプションが未指定の場合は DatabaseName よりバックアップファイル名を作成します。
    (.\_yyyyMMddHHmmss.bak)
    Restor時に File オプションが未指定の場合は例外が発生します。

関連するリンク
    http://msdn.microsoft.com/ja-jp/library/ms186865.aspx
    http://msdn.microsoft.com/ja-jp/library/ms186858.aspx
    http://msdn.microsoft.com/ja-jp/library/ms179313.aspx

'@

$query_backup="BACKUP DATABASE {database_name} TO DISK = '{backup_file}'"
$query_restore="RESTORE DATABASE {database_name} FROM DISK = '{backup_file}' WITH REPLACE"

# Sqlcmdを実行
function Exec-SqlCommand($query){
  Sqlcmd -S $server -Q "$query"
}

# Backupを実施
function Backup-Database(){
  # database未指定→Error
  if( $database.length -eq 0 ){
    throw "対象となる Database を指定してください。"
  }
  # file未指定→database名 + 日付とする
  if( $file.length -eq 0 ){
    #カレントディレクトリを取得
    $current_path = Convert-Path .
    
    if( $directory.length -gt 0 ){
      #ディレクトリ指定時
      if( Test-Path $directory ){
        $current_path = Convert-Path $directory
      }else{
        throw "指定されたディレクトリが存在しません。 [ $directory ]"
      }
    }
    
    #yyyyMMddhhmmss
    $date_time = Get-Date -format 'yyyyMMddHHmmss'
    #結合
    $file = $current_path + '\' + $database + '_' + $date_time + '.bak';
  }

  $now = Get-Date -format 'yyyy/MM/dd HH:mm:ss'
  $msg=@"

Backupを実施します。[ $now ]
`tDatabase:`t$database
`tBackupFile:`t$file

"@
  Write-Host $msg
  
  $q = $query_backup -replace '{database_name}', $database
  $q = $q -replace '{backup_file}', $file
  
  Write-Debug "query= $q"
  
  Exec-SqlCommand $q
  
  $now = Get-Date -format 'yyyy/MM/dd HH:mm:ss'
  
  Write-Host "`nBackupが完了しました。 [ $now ]`n"
}

# Restoreを実施
function Restore-Database(){
  # database未指定→Error
  if( $database.length -eq 0 ){
    throw "対象となる Database を指定してください。"
  }
  # file未指定→Error
  if( $file.length -eq 0 ){
    throw "リストアするファイルを指定してください。"
  }
  # 存在しないfileを指定
  if( Test-Path $file ){
    $file = Convert-Path $file
  }else{
    throw "指定されたファイルが存在しません。 [ $file ]"
  }

  $now = Get-Date -format 'yyyy/MM/dd HH:mm:ss'
  $msg=@"

Restoreを実施します。[ $now ]
`tDatabase:`t$database
`tBackupFile:`t$file

"@
  Write-Host $msg
  
  $q = $query_restore -replace '{database_name}', $database
  $q = $q -replace '{backup_file}', $file
  
  Write-Debug "query= $q"
  
  Exec-SqlCommand $q
  
  $now = Get-Date -format 'yyyy/MM/dd HH:mm:ss'
  
  Write-Host "`nRestoreが完了しました。 [ $now ]`n"
}

# 引数をチェックし、処理を分岐
function Get-CommandLineOptions(){
  if($debug){
    #デバッグ表示を有効にする
    $DebugPreference="continue"
  }

  if( $help ){
    # Helpの表示
    Write-Host $usage
  }elseif( $backup -and $restore ){
    # Backup と Resutore の両方を指定 -> Error
    throw "Backup|Restoreのどちらかを指定してください。"
  }elseif( $backup ){
    Backup-Database
  }elseif( $restore ){
    Restore-Database
  }else{
    # Helpの表示
    Write-Host $usage
  }
}

Get-CommandLineOptions

スイッチオプションの指定

Param に [switch]型の引数を定義すると、スクリプト実行時に該当オプションを引数に渡すと、対応する変数に $true がセットされる。

Param(
  [switch] $backup,
  [switch] $restore,
  [string] $database="",
  [string] $server=$env:computerName,
  [string] $file="",
  [string] $directory="",
  [switch] $help,
  [switch] $debug
)

上のように書いた場合...

PS> .\Backup-Database.ps1 -Backup

↑このように引数を指定すると、$backup に $true がセットされる。引数を指定しなければ $false が入る。

functionでも同じように[switch]を使用可能。

function hoge([switch]$a){
  if( $a ){
    Write-Host "foo"
  }else{
    Write-Host "bar"
  }
}

hoge -a   # >foo
hoge      # >bar

2012/05/30

DbUtilsのサンプル

Apache CommonsのDbUtilsを使用してデータの取得、更新を行うサンプル。

//JdbcTest.java
package pkg.sample;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import java.util.Iterator;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.log4j.*;
import org.apache.log4j.xml.*;
  
public class JdbcTest {
  static Logger logger = Logger.getLogger(JdbcTest.class);
  
  Connection con = null;
  QueryRunner qr = new QueryRunner();
  
  public void exec(){
    try{
      Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
      try{
        //DB接続
        con = DriverManager.getConnection("jdbc:sqlserver://localhost;database=ASN;integratedSecurity=true;");
        //オートコミットを無効に
        con.setAutoCommit(false);
        logger.info("connect.");
        
        //BeanListHandlerサンプル
        System.out.println("---登録データ---");
        select();
        
        //INSERTサンプル
        insert();
        
        //UPDATEサンプル
        update();
        
        //更新結果確認
        System.out.println("---更新結果---");
        select();
        
        //commit
        DbUtils.commitAndCloseQuietly(con);
        logger.info("commit.");
        
      }catch(SQLException se){
        DbUtils.rollbackAndCloseQuietly(con);
        logger.info("rollback.");
        se.printStackTrace();
        logger.error(se.getMessage());
      }

    }catch(ClassNotFoundException e){
      e.printStackTrace();
      logger.error(e.getMessage());
    }finally{
      //Close処理
      DbUtils.closeQuietly(con);
    }
  }
  
  /**
   * BeanListHandlerのサンプルコード
   * @throws SQLException
   */
  private void select() throws SQLException{
    String query = "SELECT * FROM SampleTable";
    
    ResultSetHandler rsh = new BeanListHandler(PersonInfo.class);
    List list = (List) qr.query(con, query, rsh);
    
    Iterator items = list.iterator();
    while(items.hasNext()){
      PersonInfo p = (PersonInfo) items.next();
      
      System.out.println(p.getName());
      System.out.println(p.getBirth());
      System.out.println(p.getRegist());
    }
  }
  
  /**
   * QueryRunner.updateのテスト (1)
   * @throws SQLException
   */
  private void insert() throws SQLException{
    //三つ目のパラメータを SYSDATETIME() に置き換えると例外が出る。 "Wrong number of parameters" ???
    String query = "INSERT INTO SampleTable (name, birth, regist) VALUES (?, ?, ?)";
    
    qr.update(con, query, new Object[]{ "サトウ", "1980-01-01", getCurrentDatetime() });
    qr.update(con, query, new Object[]{ "タナカ", null, getCurrentDatetime() });
  }
  
  /**
   * SYSDATETIME()の代わり。 現在日時(ミリ秒単位)の文字列表現を取得
   * @return yyyy-MM-dd HH:mm:ss.SSS
   */
  private String getCurrentDatetime(){
    java.text.DateFormat df = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    return df.format(new java.util.Date());
  }
  
  /**
   * QueryRunner.updateのテスト (2)
   * @throws SQLException
   */
  private void update() throws SQLException {
    String query = "UPDATE SampleTable SET birth = ?, regist = SYSDATETIME() WHERE name = ?";
    
    qr.update(con, query, new Object[]{ "2000-12-31", "タナカ" });
  }
  
  /**
   * @param args
   */
  public static void main(String[] args) {
    //log4j設定ファイル読み込み
    DOMConfigurator.configure("logging.xml");

    JdbcTest j = new JdbcTest();
    j.exec();
  }
}

データベースから取得した値を格納するクラス。
BeanListHandlerを使用してQueryRunner.queryメソッドを実行すると結果がセットされて返される。

当然、Beanクラスのプロパティ名を取得対象テーブルのフィールド名と合わせる事。
取得対象テーブルのフィールド名とプロパティ名が一致しない場合は無視されるだけなので、Beanクラス側に追加のプロパティがあってもよい。

// Beanクラス(PersonInfo.java)
package pkg.sample;

public class PersonInfo {
  private String name;
  private String birth;
  private String regist;
  /**
   * @return the birth
   */
  public String getBirth() {
    return birth;
  }
  /**
   * @param birth the birth to set
   */
  public void setBirth(String birth) {
    this.birth = birth;
  }
  /**
   * @return the name
   */
  public String getName() {
    return name;
  }
  /**
   * @param name the name to set
   */
  public void setName(String name) {
    this.name = name;
  }
  /**
   * @return the regist
   */
  public String getRegist() {
    return regist;
  }
  /**
   * @param regist the regist to set
   */
  public void setRegist(String regist) {
    this.regist = regist;
  }
}

2012/05/07

PowerShellでSqlDataReaderを使用するサンプル

PowerShellでSqlDataReaderクラスを使用して データベースから値を取得するサンプル。
#
# 名前
#     Execute-Query
# 概要
#     指定されたQueryを実行し、テキストファイルに出力します。
#
# -Query  SQL文
# -Fields 出力するColumn名
# -File   出力するテキストファイル
function Execute-Query([string]$query, $fields, $file){

 [Data.SqlClient.SqlConnection]$conn = $null
 [Data.SqlClient.SqlCommand]$cmd = $null
 [Data.SqlClient.SqlDataReader]$reader = $null
 
 $funcName = "Execute-Query"
 
 # 改行をスペースに置換
 $tmp = $query -replace "`r`n", " "
 Write-Debug  "QUERY=[$tmp]`t$funcName"
 
 try{
  $conn = New-Object Data.SqlClient.SqlConnection($DB_CON_STR)
  $conn.Open()
  Write-Debug  "Connection Open.`t$funcName"
  
  $cmd = New-Object Data.SqlClient.SqlCommand
  $cmd.CommandText = $query
  $cmd.Connection = $conn
  
  #ExecuteReader
  $reader = $cmd.ExecuteReader()
  $count = 0
  
  #出力バッファ
  [Text.StringBuilder]$sb = New-Object Text.StringBuilder
  while( $reader.Read() ){
   foreach( $column in $fields ){
    $sb.Append($reader[$column].ToString())
   }
   $sb.AppendLine()
   $count++
  }
  $reader.Close()
  Write-Debug "出力行数= $count`t$funcName"
  
  #ファイルに出力
  Add-Content $file ($sb.ToString())
  
 }catch [Exception]{
  throw $Error[0]
 }finally{
  if(!([String]::IsNullOrEmpty($conn))){
   $conn.Close()
   Write-Debug  "Connection Close.`t$funcName"
  }
 }
}