CodeZine(コードジン)

特集ページ一覧

SQL Anywhere 12.0.1でのNHibernate 3.2.0 GAの使用

原文:Using NHibernate 3.2.0 GA with SQL Anywhere 12.0.1

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2011/12/01 14:00

(原文:Using NHibernate 3.2.0 GA with SQL Anywhere 12.0.1、2011/08/10投稿)

 去る7月30日に、NHibernateオブジェクトリレーショナルマッピングツールキットの3.2.0 GAリリースが公開されました。

 Julian Maughanの尽力のおかげで、このNHibernateの基本ディストリビューションにはSQL Anywhere用の方言が2つ含まれています。それらはSybaseSQLAnywhere10Dialect.csとSybaseSQLAnywhere11Dialect.csという名前が付けられていて、それぞれSQL Anywhereのバージョン10と11において、ほとんど変更することなく使用することができます。しかし、SQL Anywhereのソフトウェア構成は12.0.1リリースになっていくつかの点で変更されており、NHibernate 3.2.0ディストリビューションにはSQL Anywhere 12用のNHibernate方言が含まれていません。そこで、SQL Anywhere 12.0.1に対するNHibernateのサポートについて再び検討したいと思います。

 私は2010年1月に、SQL Anywhere 12.0.0用のNHibernate方言に関する記事を投稿していますが、これはNHibernate 2.1.0リリースについてのものでした。NHibernateのバージョン3はいくつかの変更をもたらしており、たとえば、自分でNHibernateをビルドするにはMicrosoft Visual Studio 2010が必要になります。実際、最善のソフトウェア構成でNHibernateとSQL Anywhere 12.0.1とを利用するには、自分でビルドする必要があります。

 重要な点を要約すると、以下のようになります。

  • 私が2010年1月に投稿したSQL Anywhere 12用の方言は、ほとんど変更がありませんが、小さな変更が2点だけあります。第一の変更点は、そのクラス名がSQLAnywhere12DialectからSybaseSQLAnywhere12Dialectに変更されたことです。これは、JulianがNHibernate 3.1.0ディストリビューションに加えたSQL Anywhere 11および12用の方言のクラス名に対する変更と一致させるためです。第二の変更点は、この方言がメタデータクラスであるSybaseSQLAnywhere11DataBaseMetaDataを参照することです。この点については次の項を参照してください。
  • /NHibernate/src/NHibernate/Dialect/Schemaディレクトリに含まれる新規のメタデータクラスであるSybaseSQLAnywhere11DataBaseMetaDataは、GetReservedWords() APIを適切にサポートしています。これはPaul Gibsonが以前に報告した問題への対応です。
  • SQL Anywhere 12.0.1においてSQL Anywhere ADO.NETプロバイダ用のADO.NETライブラリの名前に加えられた変更をサポートするには、/Nhibernate/src/NHibernate/Driver内にSybaseSQLAnywhere12Driverという名前の新しいドライバが必要です。

 新しいソースファイルの.zipアーカイブは、ここに保存されています。このアーカイブには、NHibernate 3.2.0 DLLを再コンパイルした後で妥当性テストに使用できるHelloNHibernateのインスタンスも含まれています。ただし、このアーカイブに含まれているVisual Studioソリューションを使用するためには、SQL Anywhereの.NETプロバイダDLLであるiAnywhere.Data.SQLAnywhere.v4.0.dllをHelloNHibernate\bin\Debugディレクトリにコピーすることが必要になるので注意してください。また、このアーカイブに含まれているhelloseq.dbデータベースをSQL Anywhere 12.0.1サーバ上で実行することも必要になります。このデータベースには、プライマリキーとしてシーケンスを用いているEmployeeテーブルが含まれています。

SybaseSQLAnywhere12Dialect.cs

 新しい名前に変更されたSybaseSQLAnywhere12Dialectクラスは、NHibernate 3.2.0ディストリビューションに含まれているSybaseSQLAnywhere11Dialectをベースにしています。SybaseSQLAnywhere12Dialectクラスは主として、現在SQL Anywhere 12でサポートされているISO標準SQLシーケンスに対するサポートを提供します。それ以外に小さな変更点が2つあります。1つは、TIMESTAMP WITH TIME ZONEデータ型(DATETIMEOFFSETとも呼ばれます)のサポートです。そしてもう1つは、前述のように、修正されたメタデータクラスSybaseSQLAnywhere11DataBaseMetaDataを使用することです。次に、SybaseSQLAnywhere12Dialect.csのコードを紹介します。このコードはNHibernate/src/NHibernate/Dialectディレクトリに配置します。

using System;
using System.Collections;
using System.Data;
using System.Data.Common;
using System.Text.RegularExpressions;
 
using NHibernate.Dialect.Function;
using NHibernate.Dialect.Lock;
using NHibernate.Dialect.Schema;
using NHibernate.Engine;
using NHibernate.Exceptions;
using NHibernate.Mapping;
using NHibernate.SqlCommand;
using NHibernate.Type;
using NHibernate.Util;
 
using Environment = NHibernate.Cfg.Environment;
 
namespace NHibernate.Dialect
{
    /// <summary>
    /// SQL Dialect for SQL Anywhere 12 - for the NHibernate 3.2.0 distribution
    /// Copyright (C) 2011 Glenn Paulley
        /// Contact: http://iablog.sybase.com/paulley
    ///
    /// This NHibernate dialect for SQL Anywhere 12 is a contribution to the NHibernate
        /// open-source project. It is intended to be included in the NHibernate 
        /// distribution and is licensed under LGPL.
    ///
    /// This library is free software; you can redistribute it and/or
    /// modify it under the terms of the GNU Lesser General Public
    /// License as published by the Free Software Foundation; either
    /// version 2.1 of the License, or (at your option) any later version.
    ///
    /// This library is distributed in the hope that it will be useful,
    /// but WITHOUT ANY WARRANTY; without even the implied warranty of
    /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    /// Lesser General Public License for more details.
    ///
    /// You should have received a copy of the GNU Lesser General Public
    /// License along with this library; if not, write to the Free Software
    /// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    ///
    /// </summary>
    ///
        /// <remarks> The SybaseSQLAnywhere12Dialect
        /// uses the SybaseSQLAnywhere11Dialect as its base class.
        /// SybaseSQLAnywhere12Dialect includes support for ISO SQL standard
        /// sequences, which are defined in the catalog table <tt>SYSSEQUENCE</tt>. 
    /// The dialect uses the SybaseSQLAnywhere11MetaData class for metadata API
    /// calls, which correctly supports reserved words defined by SQL Anywhere.
    /// </remarks>
    public class SybaseSQLAnywhere12Dialect : SybaseSQLAnywhere11Dialect
    {
        /// <summary></summary>
        public SybaseSQLAnywhere12Dialect()
        {
            RegisterDateTimeTypeMappings();
            RegisterKeywords();
        }
 
        new protected void RegisterKeywords() 
        {
            RegisterKeyword( "NEAR" );
            RegisterKeyword( "LIMIT" );
            RegisterKeyword( "OFFSET" );
            RegisterKeyword( "DATETIMEOFFSET" );
        }
 
        new protected void RegisterDateTimeTypeMappings() 
        {
            RegisterColumnType(DbType.DateTimeOffset, "DATETIMEOFFSET");
        }
 
 
        // DDL support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
        /// <summary> 
        /// SQL Anywhere supports <tt>SEQUENCES</tt> using a primarily SQL Standard 
            /// syntax. Sequence values can be queried using the <tt>.CURRVAL</tt> identifier, and the next
            /// value in a sequence can be retrieved using the <tt>.NEXTVAL</tt> identifier. Sequences
            /// are retained in the SYS.SYSSEQUENCE catalog table. 
        /// </summary>
        public override bool SupportsSequences
        {
            get { return true; }
        }
 
            /// <summary>
            /// Pooled sequences does not refer to the CACHE parameter of the <tt>CREATE SEQUENCE</tt>
            /// statement, but merely if the DBMS supports sequences that can be incremented or decremented
            /// by values greater than 1. 
            /// </summary>
        public override bool SupportsPooledSequences
        {
            get { return true; }
        }
 
        /// <summary> Get the <tt>SELECT</tt> command used retrieve the names of all sequences.</summary>
        /// <returns> The <tt>SELECT</tt> command; or NULL if sequences are not supported. </returns>
        public override string QuerySequencesString
        {
            get { return "SELECT SEQUENCE_NAME FROM SYS.SYSSEQUENCE"; }
        }
 
 
        public override string GetSequenceNextValString( string sequenceName )
        {
            return "SELECT " + GetSelectSequenceNextValString(sequenceName) + " FROM SYS.DUMMY";
        }
 
        public override string GetSelectSequenceNextValString( string sequenceName )
        {
            return sequenceName + ".NEXTVAL";
        }
 
        public override string GetCreateSequenceString( string sequenceName )
        {
            return "CREATE SEQUENCE " + sequenceName; // by default, is START WITH 1 MAXVALUE 2**63-1
        }
 
        public override string GetDropSequenceString( string sequenceName )
        {
            return "DROP SEQUENCE " + sequenceName;
        }
 
            // Informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
        public override IDataBaseSchema GetDataBaseSchema( DbConnection connection )
        {
            return new SybaseSQLAnywhere11DataBaseMetaData( connection );
        }
 
    }
}

 この方言ファイルについて最後に注意点を1つだけ補足しておきます。NHibernate 3.2.0ディストリビューションに含まれているSybaseSQLAnywhere10Dialect.csには、バイナリ型のマッピングに入力ミスが含まれています。つまり、このコードではLONG VARBINARYという型を参照していますが、これは誤りで、正しくはLONG BINARYです。これは私のミスでした。今後リリースされるNHibernateでは、(Julianを介して)この点が確実に修正されるようにします。

SybaseSQLAnywhere11MetaData.cs

 NHibernate 3.2.0ディストリビューションのNHibernate/src/NHibernate/Dialect/SchemaにはSybaseAnywhereMetaDataクラスが含まれています。ただしこのクラスには、iAnywhere ADO.NETプロバイダと連動するために必要なGetReservedWords() API呼び出しのオーバーライドが含まれていません。次に示すSybaseSQLAnywhere11MetaData.csの中の新しいクラスであるSybaseSQLAnywhere11DataBaseMetaDataには、この呼び出しが含まれています。

using System;
using System.Data;
using System.Data.Common;
using Iesi.Collections.Generic;
 
namespace NHibernate.Dialect.Schema
{
    /// <summary>
    /// Metadata support for connections using the iAnywhere.Data.SQLAnywhere 
    /// ADO.NET provider for SQL Anywhere 11 - for the NHibernate 3.2.0 distribution
    /// 
        /// Contact: http://iablog.sybase.com/paulley
    ///
    /// This NHibernate dialect for SQL Anywhere 12 is a contribution to the NHibernate
        /// open-source project. It is intended to be included in the NHibernate 
        /// distribution and is licensed under LGPL.
    ///
    /// This library is free software; you can redistribute it and/or
    /// modify it under the terms of the GNU Lesser General Public
    /// License as published by the Free Software Foundation; either
    /// version 2.1 of the License, or (at your option) any later version.
    ///
    /// This library is distributed in the hope that it will be useful,
    /// but WITHOUT ANY WARRANTY; without even the implied warranty of
    /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    /// Lesser General Public License for more details.
    ///
    /// You should have received a copy of the GNU Lesser General Public
    /// License along with this library; if not, write to the Free Software
    /// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    ///
    /// </summary>
    /// <remarks>
        /// The SQLAnywhere11DataBaseMetaData, SQLAnywhere11TableMetaData, and the like offer
        /// support for the .NET metadata APIs from within NHibernate. This file is now versioned
        /// with "SQL Anywhere 11" to accomodate API changes with later SQL Anywhere releases.
    /// </remarks>
    public class SybaseSQLAnywhere11DataBaseMetaData : AbstractDataBaseSchema
    {
        public SybaseSQLAnywhere11DataBaseMetaData(DbConnection connection) : base( connection ) 
        {
        }
 
        public override ITableMetadata GetTableMetadata(DataRow rs, bool extras)
        {
            return new SybaseSQLAnywhere11TableMetaData(rs, this, extras);
        }
 
        public override DataTable GetTables(string catalog, string schemaPattern, string tableNamePattern, string[] types)
        {
            var restrictions = new[] {schemaPattern, tableNamePattern, null};
            DataTable objTbl = Connection.GetSchema("Tables", restrictions);
            return objTbl;
        }
 
        public override DataTable GetIndexInfo(string catalog, string schemaPattern, string tableName)
        {
            var restrictions = new[] {schemaPattern, tableName, null};
            DataTable objTbl = Connection.GetSchema("Indexes", restrictions);
            return objTbl;
        }
 
        public override DataTable GetIndexColumns(string catalog, string schemaPattern, string tableName, string indexName)
        {
            var restrictions = new[] {schemaPattern, tableName, indexName, null};
            DataTable objTbl = Connection.GetSchema("IndexColumns", restrictions);
            return objTbl;
        }
 
        public override DataTable GetColumns(string catalog, string schemaPattern, string tableNamePattern,
                                             string columnNamePattern)
        {
            var restrictions = new[] {schemaPattern, tableNamePattern, null};
            DataTable objTbl = Connection.GetSchema("Columns", restrictions);
            return objTbl;
        }
 
        public override DataTable GetForeignKeys(string catalog, string schema, string table)
        {
            var restrictions = new[] {schema, table, null};
            DataTable objTbl = Connection.GetSchema("ForeignKeys", restrictions);
            return objTbl;
        }
 
        public override ISet<string> GetReservedWords()
        {
            var result = new HashedSet<string>();
            DataTable dtReservedWords = Connection.GetSchema(DbMetaDataCollectionNames.ReservedWords);
            foreach (DataRow row in dtReservedWords.Rows)
            {
                result.Add(row["reserved_word"].ToString());
            }
            return result;
        }
 
    }
 
    public class SybaseSQLAnywhere11TableMetaData : AbstractTableMetadata
    {
        public SybaseSQLAnywhere11TableMetaData(DataRow rs, IDataBaseSchema meta, bool extras) : base(rs, meta, extras) {}
 
        protected override IColumnMetadata GetColumnMetadata(DataRow rs)
        {
            return new SybaseSQLAnywhere11ColumnMetaData(rs);
        }
 
        protected override string GetColumnName(DataRow rs)
        {
            return Convert.ToString(rs["COLUMN_NAME"]);
        }
 
        protected override string GetConstraintName(DataRow rs)
        {
            // The ADO provider for SQL Anywhere 11 does not support constraint names. 
            // Hence we simply return the column name here ...
            return Convert.ToString(rs["COLUMN_NAME"]);
        }
 
        protected override IForeignKeyMetadata GetForeignKeyMetadata(DataRow rs)
        {
            return new SybaseSQLAnywhere11ForeignKeyMetaData(rs);
        }
 
        protected override IIndexMetadata GetIndexMetadata(DataRow rs)
        {
            return new SybaseSQLAnywhere11IndexMetaData(rs);
        }
 
        protected override string GetIndexName(DataRow rs)
        {
            return (string) rs["INDEX_NAME"];
        }
 
        protected override void ParseTableInfo(DataRow rs)
        {
            Catalog = null;
            Schema = Convert.ToString(rs["TABLE_SCHEMA"]);
            if (string.IsNullOrEmpty(Schema))
            {
                Schema = null;
            }
            Name = Convert.ToString(rs["TABLE_NAME"]);
        }
    }
 
    public class SybaseSQLAnywhere11ColumnMetaData : AbstractColumnMetaData
    {
        public SybaseSQLAnywhere11ColumnMetaData(DataRow rs) : base(rs)
        {
            Name = Convert.ToString(rs["COLUMN_NAME"]);
            object objValue = rs["COLUMN_SIZE"];
            if (objValue != DBNull.Value)
            {
                ColumnSize = Convert.ToInt32(objValue);
            }
            objValue = rs["PRECISION"];
            if (objValue != DBNull.Value)
            {
                NumericalPrecision = Convert.ToInt32(objValue);
            }
            Nullable = Convert.ToString(rs["IS_NULLABLE"]);
            TypeName = Convert.ToString(rs["DATA_TYPE"]);
        }
    }
 
    public class SybaseSQLAnywhere11IndexMetaData : AbstractIndexMetadata
    {
        public SybaseSQLAnywhere11IndexMetaData(DataRow rs) : base(rs)
        {
            Name = (string) rs["INDEX_NAME"];
        }
    }
 
    public class SybaseSQLAnywhere11ForeignKeyMetaData : AbstractForeignKeyMetadata
    {
        public SybaseSQLAnywhere11ForeignKeyMetaData(DataRow rs) : base(rs)
        {
            // SQL Anywhere 11 does support constraint names but the version 11 ADO.NET 
            // provider does not offer an ability to retrieve them... so we merely
            // return the column name.
 
            Name = (string) rs["COLUMN_NAME"];
        }
    }
}

SybaseSQLAnywhere12Driver.cs

 思い出していただきたいのですが、自分のNHibernateアプリケーションのApp.configファイルには、データベースへの接続に使用するADO.NETプロバイダと自分のNHibernateアプリケーションとのリンケージを提供するNHibernateドライバの名前を指定したはずです。NHibernate 3.2.0ディストリビューションに含まれるSQL Anywhere用のドライバは、SybaseSQLAnywhereDriver.csです(余談ですが、NHibernate 3.2.0ディストリビューションに含まれるもう1つのSQL AnywhereドライバであるSybaseAsaClientDriver.csは、旧式のためもう使用すべきでないと考えてよいでしょう)。

 SQL Anywhere 12.0.1には、SQL Anywhere ADO.NETプロバイダの''2とおりの異なる実装''が用意されています。1つは.NET V4.0のサポートを提供するiAnywhere.Data.SQLAnywhere.v4.0.dllで、もう1つは.NET V3.5のサポートを提供するiAnywhere.Data.SQLAnywhere.v3.5.dllです。このバージョンの違いにより、SQL Anywhereドライバに文字列リテラルとして埋め込まれた「iAnywhere.Data.SQLAnywhere.dll」というDLL名が不適切になりました。そのため、ドライバを変更するか、また別の対策として、以前のSQL Anywhereリリースに含まれていた旧式のADO.NETプロバイダを使用するという方法をとらないと、アプリケーションはADO.NETライブラリを探し出す処理に失敗し、SQL Anywhere 12.0.1サーバへの接続の試みが失敗して、例外を生成します。

 そこで、私はSybaseSQLAnywhere12Driver.csという新しいドライバクラスを作成しました。次に示すとおり、このクラスにはSQL Anywhere Version 4.0 ADO.NETプロバイダの正しい名前が含まれています。

using System;
 
namespace NHibernate.Driver
{
    /// <summary>
    /// SQL Dialect for SQL Anywhere 12 - for the NHibernate 3.2.0 distribution
    /// Copyright (C) 2011 Glenn Paulley
        /// Contact: http://iablog.sybase.com/paulley
    ///
    /// This NHibernate dialect for SQL Anywhere 12 is a contribution to the NHibernate
        /// open-source project. It is intended to be included in the NHibernate 
        /// distribution and is licensed under LGPL.
    ///
    /// This library is free software; you can redistribute it and/or
    /// modify it under the terms of the GNU Lesser General Public
    /// License as published by the Free Software Foundation; either
    /// version 2.1 of the License, or (at your option) any later version.
    ///
    /// This library is distributed in the hope that it will be useful,
    /// but WITHOUT ANY WARRANTY; without even the implied warranty of
    /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    /// Lesser General Public License for more details.
    ///
    /// You should have received a copy of the GNU Lesser General Public
    /// License along with this library; if not, write to the Free Software
    /// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    ///
    /// </summary>
    ///
    /// <remarks>
    /// The SybaseSQLAnywhere12Driver provides a database driver for Sybase SQL Anywhere 12
        /// using the versioned ADO.NET driver iAnywhere.Data.SQLAnywhere.v4.0.
    /// </remarks>
    public class SybaseSQLAnywhere12Driver : ReflectionBasedDriver
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="SybaseSQLAnywhereDriver"/> class.
        /// </summary>
        /// <exception cref="HibernateException">
        /// Thrown when the iAnywhere.Data.SQLAnywhere.v4.0 assembly is not and can not be loaded.
        /// </exception>
        public SybaseSQLAnywhere12Driver()
            : base("iAnywhere.Data.SQLAnywhere", "iAnywhere.Data.SQLAnywhere.v4.0", "iAnywhere.Data.SQLAnywhere.SAConnection", "iAnywhere.Data.SQLAnywhere.SACommand")
        {
        }
 
        public override bool UseNamedPrefixInSql
        {
            get { return false; }
        }
 
        public override bool UseNamedPrefixInParameter
        {
            get { return false; }
        }
 
        public override string NamedPrefix
        {
            get { return String.Empty; }
        }
    }
}

 残念なことですが、NHibernateには、ADO.NETライブラリやその設定に変更があってもNHibernate DLLを再コンパイルせずに対応できるようにする一連の構成パラメータはないようです。私は、この点を今後の改善課題としてJulianに提案するつもりです。それまでの間は、この新しいSybaseSQLAnywhere12Driver.csファイルをNHibernate/src/NHibernate/Driverディレクトリに置き、NHibernate DLLの再コンパイルをすることが必要です。再コンパイルしたDLLを使用するには、アプリケーションのApp.configファイルを変更しなければなりません。次に、私がHelloNHibernateアプリケーションで使用したApp.configファイルを紹介します。このファイルも前述の.zipアーカイブに含まれています。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="hibernate-configuration" 
             type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
  </configSections>
  <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
    <session-factory>
      <property name="connection.provider">
        NHibernate.Connection.DriverConnectionProvider
      </property>
      <property name="connection.driver_class">
        NHibernate.Driver.SybaseSQLAnywhere12Driver
      </property>
      <property name="connection.connection_string">
        uid=dba;pwd=sql
      </property>
      <property name="show_sql">true</property>
      <property name="dialect">NHibernate.Dialect.SybaseSQLAnywhere12Dialect</property>
    </session-factory>
  </hibernate-configuration>
</configuration>

 いつものように、NHibernateとそのSQL Anywhereサポートについて質問がある場合は、私まで直接お寄せください。

  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • Glenn Paulley(Glenn Paulley)

    カナダ オンタリオ州 ウォータールー R&amp;DセンターにてSQL Anywhere 開発における Director of Engineering としてクエリ・オプティマイザなどの開発をリードしている。 ・IvanAnywhere

All contents copyright © 2005-2022 Shoeisha Co., Ltd. All rights reserved. ver.1.5