2011-10-26
C#でREST APIをPOSTリクエストで呼び出す
とあるASPサービスを利用しようところリクエストがPOSTでした。過去よく利用していたものは大抵はREST形式でかつGETリクエストによる取得で例えば
http://localhost/service/analysis?id=****&password=****&text=%e4%bb%8a%e6%97%a5%e3%81%af%e3%81%84%e3%81%84%e5%a4%a9%e6%b0%97%e3%81%a0%e3%80%82
というようにURLにクエリーパラメータを付与しクエリーに対する結果を得るというものです。そもそもPOSTでリクエストして結果を受け取れるのか?という思い調べてみました(実はこのあたりきちんと理解していなかったのが実情です(笑)。
| method | 意味 |
|---|---|
| GET | 指定されたURIのリソースを取り出す。HTTPの最も基本的な動作で、HTTP/0.9では唯一のメソッド。 |
| POST | GETとは反対にクライアントがサーバにデータを送信するメソッドである。Webフォームや電子掲示板、Wikiなどに投稿する。GETの場合と同じくサーバはクライアントにデータを返すことができる。 |
Hypertext Transfer Protocol - Wikipediaより
というようにPOSTでもデータを受け取るようなので早速C#で実装してみました。基本的にはWebサイトアクセスで利用するHttpWebRequest/HttpWebResponseクラスで実行可能みたいです。具体的には
- リクエストメソッドはデフォルトがGETとなっているためPOSTに指定
- リクエスト要求データのデータ形式の指定
- リクエスト要求データをHttpWebRequestクラス内のStreamオブジェクトに書き込む
が必要みたいです。以下は要求データの形式がXML形式時のPOSTリクエストです。
HttpWebResponse response; try { Uri uri = new Uri("http://localhost/service/rest/xml/analysis"); message = String.Format( "<?xml version=\"1.0\" encoding=\"utf-8\" ?><request xmlns=\"http://localhost\"><id>{0}</id><password>{1}</password><text>{3}</text></request>", ID, Password, Text); HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri); byte[] data = Encoding.UTF8.GetBytes(message); // HTTPメソッドをPOSTに指定 request.Method = "POST"; // 送信するコンテンツ種別をXMLに指定 request.ContentType = "text/xml"; request.ContentLength = data.Length; // 送信データの書き込み Stream reqSteam = request.GetRequestStream(); reqSteam.Write(data, 0, data.Length); reqSteam.Close(); response = (HttpWebResponse)request.GetResponse(); // 送信後サーバーから受信したデータの取得(XML形式で取得) StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8); Console.WriteLine(reader.ReadToEnd()); reader.Close(); } catch (WebException ex) { Console.WriteLine(ex.ToString()); StreamReader errStream = new StreamReader(ex.Response.GetResponseStream(), Encoding.UTF8); Console.WriteLine(errStream.ReadToEnd()); errStream.Close(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); }
要求データの形式JSONの場合はコンテンツタイプおよび送信データを以下のように書き換えます。
Uri uri = new Uri("http://localhost/service/rest/json/analysis");
message = "{" + String.Format(
"\"id\" : \"{0}\", \"password\" : \"{1}\", \"text\" : \"{2}\"", ID, Password, Text) + "}";
request.ContentType = "application/json";
これでめでたくREST APIをPOSTで実行および結果取得に成功しました。
参考サイト
2011-10-25
Linuxで2TBを超えるディスクの初期化
Windows Storage Server 2008でNASを構築して他のサーバーからNFS共有をしていたのですが、どうもNAS自体(というかWindows Storage Serverそのもの)の調子が悪く定期的に応答不能になってました。そのためOSをCentOSにしてLinuxでNASを構築することにしました(NASの構築については「NFS(Network File System)の構築 - とみんのブログ」を参考にしてください)。
該当サーバーはディスクを多く積んでおりRAID10構成でも12TB近いディスクになるためOSインストール後に別途フォーマットしようと思い、fdiskでディスク確認をしたところ
# fdisk -l Disk /dev/sda: 146.1 GB, 146163105792 bytes 255 heads, 63 sectors/track, 17769 cylinders Units = シリンダ数 of 16065 * 512 = 8225280 bytes デバイス Boot Start End Blocks Id System /dev/sda1 * 1 131 1052226 83 Linux /dev/sda2 132 914 6289447+ 82 Linux swap / Solaris /dev/sda3 915 17769 135387787+ 83 Linux WARNING: GPT (GUID Partition Table) detected on '/dev/sdb'! The util fdisk doesn't support GPT. Use GNU Parted.
となりました。fdiskでは2TBを超えるディスク設定ができないようで警告にもあるようにpartedというコマンドでディスク作成する必要があるみたいです。ということで早速partedコマンドで初期化しようと思ったのですが、これだけのディスクサイズなのでフォーマット形式をext3ではなくext4にすることにしました。
ext4はCentOS 5.5から正式対応されたのですが、標準ではインストールされないためまずはext4関連のモジュールをインストールします。
# yum search ext4 Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile ============================================== Matched: ext4 =============================================== e4fsprogs.x86_64 : Utilities for managing the fourth extended (ext4) filesystem e4fsprogs-devel.i386 : Ext4 filesystem-specific static libraries and headers e4fsprogs-devel.x86_64 : Ext4 filesystem-specific static libraries and headers e4fsprogs-libs.i386 : Ext4 filesystem-specific static libraries and headers e4fsprogs-libs.x86_64 : Ext4 filesystem-specific static libraries and headers # yum install e4fsprogs.x86_64
partedコマンドでディスク分割をおこないます。この時ファイルシステムの種類を聞かれたのでext4と入れたのですが、どうもext4はpartedでは指定できないようです*1。partedはディスクを分割するだけなのでここではext3を指定しました。
# parted /dev/sdb GNU Parted 1.8.1 /dev/sdb を使用 GNU Parted へようこそ! コマンド一覧を見るには 'help' と入力してください。 (parted) print モデル: DELL PERC H700 (scsi) ディスク /dev/sdb: 12.0TB セクタサイズ (論理/物理): 512B/512B パーティションテーブル: gpt 番号 開始 終了 サイズ ファイルシステム 名前 フラグ (parted) mkpart パーティションの名前? []? primary ファイルシステムの種類? [ext2]? ext4 parted: invalid token: ext4 ファイルシステムの種類? [ext2]? ext3 開始? 0 終了? 12.0TB (parted) print モデル: DELL PERC H700 (scsi) ディスク /dev/sdb: 12.0TB セクタサイズ (論理/物理): 512B/512B パーティションテーブル: gpt 番号 開始 終了 サイズ ファイルシステム 名前 フラグ 1 17.4kB 12.0TB 12.0TB primary (parted) quit 通知: 必要であれば /etc/fstab を更新するのを忘れないようにしてください。
ディスク分割が完了したらmkfsでディスクの初期化をおこないます。ext4のモジュールをインストールしないと当然ですがext4によるディスクフォーマットはできません。
# mkfs -t ext4 /dev/sdb1
mke4fs 1.41.12 (17-May-2010)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
732364800 inodes, 2929459191 blocks
146472959 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=4294967296
89400 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
102400000, 214990848, 512000000, 550731776, 644972544, 1934917632,
2560000000
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done
This filesystem will be automatically checked every 34 mounts or
180 days, whichever comes first. Use tune4fs -c or -i to override.
必要に応じてtune4fsコマンドでファイルシステムのパラメータを調整します。今回はファイルシステムのチェックを無効にしてみました。通常は上記mkfsコマンド実行後のメッセージにあるように34ヶ月 or 180日ごとにe2fsckによるファイルシステムチェックが行われるようですが、今回は一切おこなわないようにしてみました。
# tune4fs -c 0 -i 0 /dev/sdb1 tune4fs 1.41.12 (17-May-2010) Setting maximal mount count to -1 Setting interval between checks to 0 seconds
フォーマットが完了したらマウントできるか確認します。
# mount -t nfs /dev/sdb1 /export/nfs # df Filesystem 1K-ブロック 使用 使用可 使用% マウント位置 /dev/sda3 131147752 5095076 119283288 5% / /dev/sda1 1019208 46616 919984 5% /boot tmpfs 3049496 0 3049496 0% /dev/shm /dev/sdb1 11533963164 216518016 10731553312 2% /export/nfs
参考サイト
2011-10-14
Perlでログ出力
現在稼動しているPerlプログラムのログ出力方法が今一つなので改善したいと思い色々調べていたところPerlにもlog4xxの系統というべきlog4perlというモジュールがあるみたいなので早速試して見ました。まずCPANでモジュールをインストールします。
cpan> install Log::Log4perl
次にlog4perlを利用するにあたりログの出力形式などを定義する設定ファイル(log4perl.conf)を用意します。
log4perl.rootLogger=DEBUG, LOGFILE
;ログの出力形式(今回はファイル形式)
log4perl.appender.LOGFILE = Log::Log4perl::Appender::File
;出力するファイル名
log4perl.appender.LOGFILE.filename = /root/debug.log
;書き込み方法(append:追記)
log4perl.appender.LOGFILE.mode = append
;出力レイアウトパターン
log4perl.appender.LOGFILE.layout = Log::Log4perl::Layout::PatternLayout
;レイアウトパターンの指定
log4perl.appender.LOGFILE.layout.ConversionPattern = %d{yyyy/MM/dd HH:mm:ss.SSSS} [%-5p] %F %L %c - %m%n
この記載方法自体は他のプログラミング言語のlog4xxとあまり変わりありません。ただlog4jやlog4netの場合はXML形式で記載する方が多いみたいです。ログ出力のレイアウトパターンは以前にC#のlog4xxであるlog4netについて「log4netによるログ出力 (4) - とみんのブログ」でまとめているので参考にしてください。なお今回は先程のページで説明していない新たなレイアウトパターンを利用しています。
| パターン表記 | 意味 |
|---|---|
| %F | ログを出力しているソースコードファイル名 |
| %L | ログを出力しているソースコードの行番号 |
これらのパターンはlog4xxでも利用できるのですが、log4jのマニュアルには
WARNING Generating caller location information is extremely slow and should be avoided unless execution speed is not an issue.
(和訳) ロケーション情報の呼び出しは極端に遅いため速度に問題がない限り利用は避けるべきです
PatternLayout (Apache Log4j 1.2.16 API)より
とあるので、もしPerlでも同様なことが言える場合は利用は控えたほうがいいみたいです。log4perlの実際に利用する場合は以下のようにコードを記載します。
#!/usr/bin/perl use utf8; use strict; use Log::Log4perl; binmode(STDOUT, ":utf8"); Log::Log4perl::init('/root/log4perl.conf'); my $logger = Log::Log4perl::get_logger(); $logger->debug("This is Debug message"); $logger->info("This is Info message"); $logger->warn("This is Warn message"); $logger->error("This is Error message");
上記のプログラムを実行するとdebug.logが生成され、以下のように出力されました。
2011/10/14 11:32:04.7752 [DEBUG] logtest.pl 13 main - This is Debug message 2011/10/14 11:32:04.7753 [INFO ] logtest.pl 14 main - This is Info message 2011/10/14 11:32:04.7754 [WARN ] logtest.pl 15 main - This is Warn message 2011/10/14 11:32:04.7755 [ERROR] logtest.pl 16 main - This is Error message
ここまでは単純なログ出力でしたが、log4xx同様に様々な出力(ex. メール、データベースなど)が可能です。よく利用されるログファイルのローテーションをおこないたい場合は別途Log::Dispatch::FileRotateパッケージをインストールする必要があります。FileRotateパッケージをインストール後は設定ファイルに以下のように記載します。
log4perl.rootLogger=DEBUG, LOGFILE
log4perl.appender.LOGFILE = Log::Dispatch::FileRotate
log4perl.appender.LOGFILE.filename = /root/debug.log
log4perl.appender.LOGFILE.mode = append
; ファイルの最大サイズ(単位はバイトみたい)
log4perl.appender.LOGFILE.size=10
; ローテーションさせるファイル数
log4perl.appender.LOGFILE.max=5
log4perl.appender.LOGFILE.layout = Log::Log4perl::Layout::PatternLayout
log4perl.appender.LOGFILE.layout.ConversionPattern = %d{yyyy/MM/dd HH:mm:ss.SSSS} [%-5p] %F %L %c - %m%n
これで上記のプログラムを実行するとdebug.log.1〜debug.log.5までが生成されました。
参考サイト
2011-10-13
O/Rマップフレームワーク Hibernate
随分以前にとあるシステムを構築する上でO/Rマップフレームワークの検証をしていました。その時の記録が残っていたのでここに転記します。
本当は改めて再検証してみてもいいのですが当面利用する機会がなさそう、ということもあるのですが。今後に参考になるかどうか・・・も不明ですね(汗)
Hibernateの利用準備
MyEclipseでおこなう場合は「Hibernate 開発 - 日本語MyEclipse」の5.1〜5.2が分かりやすいためここでは割愛。
Hibernate Toolsを用いてデータベースからテーブルクラスの自動生成
「Hibernate 開発 - 日本語MyEclipse」の5.4が詳細なのでここでは割愛。なおMySQLデータベースに接続するためにMySQL Connector/Jをインストール必要があるため簡単に紹介(スクリーンショットを残しておけばよかった・・・)。
- MyEclipseを起動している場合は終了する。
- MySQLサイトから最新版のMySQL Connector/Jをダウンロードする(当時の最新版は5.1.7)。
- 解凍してmysql-connector-java-5.1.7-bin.jarをJava\jkd1.6.0_10\jre\lib\extフォルダにコピーする。
- MyEclipseを起動する。
- hibernate.cfg.xmlをクリックし、以下の設定をする。
- 上記で設定された内容が入力される。最後にダイアレクトをMySQLにし、「JDBCドライバーをコピーしてクラスパスに追加」をクリックし、hibernate.cfg.xmlを保存する。
Hibernateによる接続方法
「Hibernate 開発 - 日本語MyEclipse」の8.にあるのでここでは割愛。なおAMP_DBのAccountテーブルに新規データーを登録するサンプルプログラムを以下に示す(SELECT、INSERTとも動作確認済み)。
package example.test.db.accountdb; import java.util.*; import javax.transaction.Transaction; import example.db.accountdb.*; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.HibernateException; import org.hibernate.cfg.*; public class AccountDbAccessTest { public static void main(String[] args) { AccountDAO dao = new AccountDAO(); Account account = new Account(); org.hibernate.Transaction transaction = dao.getSession().beginTransaction(); try { account.setCreateAccountId(new Integer(0)); account.setServiceId(new String("no")); //account.setCreateDate(new Date()); account.setDepartment(new String("Information Technology")); account.setFaxNumber(new String("0120-XXX-XXXX")); account.setId(new Integer(0)); account.setLoginId(new String("0")); account.setLoginPassword(new String("******")); account.setMailAddress(new String("info@example.com")); account.setName(new String("tomandjelly")); account.setPasswordChangedFlag(new Byte("0")); account.setRoleType(new String("no")); account.setTelephoneNumber(new String("03-****-****")); account.setTitle(new String("test")); account.setUpdateAccountId(new Integer(0)); //account.setUpdateDate(new Date()); dao.save(account); transaction.commit(); // ID0のアカウント情報を検索 //Account account2 = dao.findById(0); } catch (HibernateException e){ System.err.println("Faild Insert"); e.printStackTrace(); transaction.rollback(); } finally { dao.getSession().close(); } } }
Hibernate利用にあたっての検証
検証1:Hibernate Toolによるデータベースからテーブルクラス生成
2.で述べた方法で生成することが可能。テーブルクラス時に各テーブル毎に以下のファイルが作成される。
- Abstruct[テーブル名].java、[テーブル名].java
該当テーブルの各列に対応する永続化プロパティーの public な getter メソッドおよび setter メソッドなど、必須の Hibernate 互換性機能が用意されたテーブルクラスを生成。 - [テーブル名]DAO.java
HibernateのSessionオブジェクト等を意識せずにテーブルへのアクセスが可能なクラスを生成。INSERT、UPDATE、DELETE、SELECT(SELECT ALL、単一行の任意列の取得)の関数が自動で生成される。 - [テーブル名].hbm.xml
データベースのテーブルとテーブルクラス間のマッピング方法を記載したファイル。 なお[テーブル名]DAO.javaはHibernate3から生成可能になった機能。設定変更で[テーブル名]DAO.javaを生成しないこともできる。
検証2:Hibernateによる複数データベースの接続/切り替え
1.の利用準備でHibernateSessionFactory.javaというファイルが作成され、その中のSessionオブジェクト作成時に
configuration.configure(configFile); sessionFactory = configuration.buildSessionFactory(); ・・・ session = (sessionFactory != null) ? sessionFactory.openSession() : null
という処理があるが、configFileにデータベースの接続設定であるHibernate.cfg.xmlを指定すると、設定したデータベースへの接続が可能。複数のデータベースに接続したい場合は各データベースのHibernate.cfg.xmlを用意し、接続したいデータベース毎に設定ファイルの読み込み変更をおこなうことで可能。
なお手っ取り早く複数のデータベースの接続をおこないたい場合は
- 各データベース単位でパッケージを作成(AMP_DBはamp.db.ampdb、MAP_DBはamp.db.mapdb)
- 各パッケージ内でHibernateの設定(hibernate.cfg.xmlで接続先データベース情報を設定し、2.で述べた方法によるテーブルクラスを構築する)
さらに検証1で述べた[テーブル名]DAO.javaでコネクション生成やオープンクローズ等も気にせず楽におこなうことが可能。
検証3:レプリケーション構成のデータベースでマスター/スレーブへの接続切り替えをおこないたい
簡単におこないたい場合は検証2で述べた方法で、マスター側/スレーブ側それぞれのパッケージを作成し、必要に応じて呼び出しを変えることで可能と思われる。ただし、
- パッケージ名が違うだけで作成されるクラスや関数は全く同一になるはずなので、クラス参照等をするとき、パッケージ名込みでクラス名を記載しないとどちらを呼び出しているが分からない。
- テーブル数が多く、構成変更が頻繁におこなわれるのであれば管理が大変(一元管理が楽)。
といった問題等もある。もし条件に応じてマスター/スレーブに切り替えておこないたいのであればマスター/スレーブ共通のパッケージにして以下のようにすればよいかもしれない。
- マスター/スレーブそれぞれのHibernate.cfg.xmlを用意する。
- 初期時にマスター/スレーブそれぞれのSessionFactoryを作るようにHibernateSessionFactoryクラスを変更する。
- HibernateSessionFactoryクラスのSession取得関数でマスター/スレーブ切り替えを受け付ける関数を用意。
- 切り替えの引数値に応じてマスター/スレーブのSessionを返すようにする。
こうすると引数でマスター/スレーブの接続切り替えが可能。ただしこの場合[テーブル名]DAO.javaは利用できなくなる。[テーブル名]DAO.javaは元々HibernateSessionFactoryクラスを呼び出している。上記のような切り替え処理を追加するとそれを呼び出している[テーブル名]DAO.javaまでも修正する必要となり、テーブル数が多ければ多いほど手間がかかる。
対象となるところをEclipseのリファクタリングで置き換えて問題なければいいのだが・・・リファクタリングした箇所すべての動作確認を使用としたら大変そう・・・
気になること
Hibernateを利用する場合に以下の点を考えなければならない
- 複数レコードを1回の呼び出しでおこなう
O/Rマッピングの性質上、複数のレコード取得は可能だが、新規登録は1レコードずつしかできない(バルクインサート等は不可能)。ただHQL(Hibernate Query Language)クエリだと通常のように自由なSQL文記述ができるので実現可能かどうか念のため確認してみた。
【bulk insert処理部分(前後省略)】Session session = HibernateSessionFactory.getSession(); String value01 = "0, 'no', null, 'Information Technology', '0120-***-****'," + "0, '******', 'info@example.com', 'tomandjelly'," + "'0', 'no', '03-****-****', 'test', 0, null"; String value02 = "1, 'no', null, 'Information Technology', '0120-***-****'," + "0, '******', 'info@example.com', 'tomandjelly2'," + "'0', 'no', '03-****-****', 'test', 1, null"; String sqlString = "insert into Account values(" + value01 + "), (" + value02 + ")"; int entrySize = session.createQuery(queryString).executeUpdate();
【実行結果】Faild Insert org.hibernate.hql.ast.QuerySyntaxException: expecting OPEN, found 'values' near line 1, column 21 [INSERT INTO Account values(0, 'no', null, 'Information Technology', '0120-***-****',0, '*****', 'info@example.com', 'tomandjelly','0', 'no', '03-****-****', 'test', 0, null), (1, 'no', null, 'Information Technology', '0120-***-****', 0, '******', 'info@example.com', 'tomandjelly2','0', 'no', '03-****-****', 'test', 1, null)] (以下hibernateのソースファイルの行番号情報が表示されているため割愛)
やはりバルクインサートには対応していなかった(value部分でエラーとなっている)。ちなみにinsert into [テーブル名] (・・・) select ・・・ from [別のテーブル名]には対応しており、上記の処理のSQL部分を以下のように変更(事前にAccount_Historyテーブルにデータを登録済み)String sqlString = "insert into Account (id, roleType, loginId, loginPassword, name, telephoneNumber, faxNumber, mailAddress, department, title, passwordChangedFlag, aspServiceId, createAccountId, createDate, updateAccountId, updateDate) select a.id, a.roleType, a.loginId, a.loginPassword, a.name, a.telephoneNumber, a.faxNumber, a.mailAddress, a.department, a.title, a.passwordChangedFlag, a.aspServiceId, a.createAccountId, a.createDate, a.updateAccountId, a. updateDate from AccountHistory a";
結果は・・・Accountテーブルへの登録成功!
- 任意の複数列のみ取得
特定の1列 or すべての列に対してのSELECT実行は可能だが、任意の複数列に対してのSELECT実行ができない。
- 複雑な条件の登録/更新/削除/取得
=条件はSessionオブジェクト関数、[テーブル名]DAOクラスで用意されているが条件が2つ以上になる場合や、=条件以外のもの(>やorderなど)についてはクラスで用意された関数ではおこなえない。 Hibernateを利用するため、基本的には上記の点についてはあまり考えないように思える。もし上記の点に対しても実現したいならば、利用しやすい形のラッパークラスやAPIを用意する必要がある。
追跡調査の結果、CriteriaクエリAPIを利用することで複雑な条件文を記述することが可能。またAND、ORの多重条件も可能。ただしCriteriaはあくまでselect専用のAPIのようでupdateやdeleteで複雑な条件を記載したい場合はHQLを使うしかない(かも?)Criteria APIで利用可能な条件は以下の通り。- =、!=(等しい、等しくない)
- <=, >=(以上、以下)
- <, >(より大きい、未満)
- between A and B(範囲)
- like
- in
- is empty, is not empty(空、空でない)
- is null, is not null(null、nullでない)
- GROUP BY
- ORDER BY
- limit A,B (検索件数の部分取得)
またwhere句以前の条件で以下のものも利用可能。- distinct(重複をはずす)
- count(*), distinct count(*) (カウント数)
- sum (和)
- クエリ結果件数
上記以外についてはCriteria APIのリファレンスを見る限りではできない?かもしれない(例:regexp(正規表現)など、JOINはできるっぽい)
参考サイト
- Hibernate - JBoss Community
Hibernateライブラリ(Java、.NET)、Hibernate Tools(Javaのみ)などを配布 - Hibernate入門
- Hibernate 開発 - 日本語MyEclipse
2011-10-12
Perlでデータベースにアクセス
DBIはPerlでデータベースにアクセスするための共通インタフェースで、実際データベースにアクセスするモジュールがDBDで各データベースごとに用意されているようです。
以下はデータベース接続のサンプルコードです。PostgreSQLに接続する場合は8行目をコメントアウトして、10行目を有効にすることで可能となります。
#!/usr/bin/perl use DBI; use DBD::mysql; use DBD::pg; # MySQLに接続する場合 my $host = 'DBI:mysql:' . '[データベース名]' . ':[ホストアドレス]:[ポート番号]'; # PostgreSQLに接続する場合 # my $host = 'DBI:Pg:' . '[データベース名]' . ':[ホストアドレス]:[ポート番号]'; my $user = 'tomandjelly'; my $password = 'tomandjelly'; my $connection = DBI->connect($host, $user, $password); my $sql = 'select count(*) from article_data'; my $sth = $connection->prepare($sql); $sth->execute(); my $count = $sth->fetchrow_array(); if ($count > 0) { $sql = 'select * from article_data order by crawldate DESC limit 100'; $sth = $connection->prepare($sql); $sth->execute(); while (my @list = $sth->fetchrow_array()) { (my $urlhash, my $url, my $id, my $crawldate, my $title, my $sentence) = @list; } } $sql = 'insert into article_data values(?, ?, ?, ?, ?, ?); $sth = $connection->prepare($sql); $sth->execute('S31P0XWTGHX24LXMM361Y7BV98QOID5C', 'http://d.hatena.ne.jp/tomandjelly/', 1, now(), 'とみんのブログ', 'とみんのブログにようこそ。…');
SQL実行により反映された行数や取得される行数は
$sth->rows
で取得可能ですが、この値はSELECT文に対しての信頼性がないとのことです。よってSELECTの件数を取得したい場合は上記のコードのように予めselect count(*)で件数を取得する必要があるとのことです。結構めんどくさいですね・・・
上記のサンプルではSQLの設定、データベースハンドラーの取得、SQLの実行および結果の取得、とそれぞれ処理をしていますが、selectrow_arrayという関数を用いれば1行でこれらすべてを取りまとめておこなう関数もあるみたいです(いろんなページをみてても大体がこのようなコード記載だったのでこれが鉄則と思っていました・・・)。DBIには他にも関数が用意されているのでもう少し勉強してみる価値がありますね。
参考サイト