.NETでのDB2 JDBCの鋸引き

NETがクロスプラットフォームになり、JAVAがさらに便利になりました。 しかし、私たちは共通の明るい未来に一緒に動いていますが、サポートする必要のある多くの継承された決定があります。 そして、これはジグソーパズルとファイルの助けを借りて可能ですが...

前に


現在、ドライバーはプロジェクトで必要な機能のみをサポートしています。 Wintegra.Dataプロジェクトにいつでも参加できます。

物語

(せっかちな人は、 Pilimアイテムに直接アクセスできます)

最近では、リレーショナルデータベースはどうやら誇大広告ではなく、誰にでもCAPシステムを提供します-速く、便利で、時には一貫性があり、スケーラブルです。 それにもかかわらず、インタープライズは古くて実績のあるソリューションに基づいており、その1つはBlue Giant-IBMのデータベースであり、その名前はDB2です。

適度に高速で信頼性が高く、OracleとMS SQLに劣らず、劣らない。 何かはありますが、何かはそうではありませんが、一般的にSQLは表記をサポートします(ただし、@犬に不快感がなかったわけではありません)

.NETのベースを使用するために、IBMは一般にODBCドライバーを提供しています。悪くはありませんが、ネイティブコードの小さなカートを使用しています。

一方、JAVAには、サードパーティdllを使用せずに完全に機能するドライバーがあり、その名前はJCC(JDBC 4.0ドライバー(db2jcc4.jar))です。

そして、IBMが.NETの下で「正しい」ダイバーを実装することを妨げる理由は明らかではありません。

.NET Coreはそれほど遠くないので、C#を使用してJAVAのコードを大量に書き直すのは奇妙に見えるので、1石で2羽の鳥を殺してはいけません。コードを残してJDBCドライバーを使用してください。

まず、 IKVM.NETを使用してjarをdllに変換し、.NETで使用します。 著者は、 Minecraftを安全に実行できると主張しています。 したがって、JDBCドライバーは問題を引き起こしませんでした。

必要なライブラリを作成するには、次のコマンドを実行するだけです:

ikvmc.exe -classloader:ikvm.runtime.AppDomainAssemblyClassLoader -target:library db2jcc4.jar db2jcc_license_cu.jar -out:db2jcc4.dll 

必要なのは、作成したdllをソリューションに接続して使用することだけです。

.NETのJAVAコード
 using System; using com.ibm.db2.jcc; using java.sql; using Thread = java.lang.Thread; using Class = java.lang.Class; using String = System.String; using Connection = java.sql.Connection; using Statement = java.sql.Statement; using DriverManager = java.sql.DriverManager; using ResultSet = java.sql.ResultSet; namespace jdbc { class Program { static void Main(string[] args) { Class.forName( typeof(com.ibm.db2.jcc.DB2Driver).AssemblyQualifiedName, true, Thread.currentThread().getContextClassLoader()); String url = "jdbc:db2://192.168.72.135:50000/DB1:user=root;password=password;"; using (Connection conn = DriverManager.getConnection(url)) { String sql = "SELECT * FROM TABLE(VALUES( CAST( :p AS VARCHAR(100)) , 'It is work')) AS T(ID, LOG)"; using (var stmt = conn.prepareCall(sql)) { stmt.setString("p","1234"); using (ResultSet rs = stmt.executeQuery()) { while (rs.next()) { //Retrieve by column name String id = rs.getString("ID"); String log = rs.getString("LOG"); global::System.Console.WriteLine("LOG: " + id + " : " + log); } } } } } } } 


ただし、.NET開発者はjava.langの形式の構文を高く評価するとは思いません。

のこぎり


ジグソーパズルとファイルを準備したら、必要な量の自転車を作成します。

 public sealed class Db2Connection : DbConnection, ICloneable 

接続を作成してDapperを動作させるだけで十分です。

Db2connection
JAVAの世界からのちょっとした魔法

 static Db2Connection() { Class.forName( typeof(com.ibm.db2.jcc.DB2Driver).AssemblyQualifiedName, true, Thread.currentThread().getContextClassLoader()); } 

JAVAとほぼ同じように、JDBCドライバーをロードします。

 Class.forName("com.ibm.db2.jcc.DB2Driver"); 

データベース接続は、1対1のJAVAです。

 connector = DriverManager.getConnection(_connectionString); 

接続文字列はJDBCと同じであることに注意してください(ただし、必要に応じてDb2ConnectionStringBuilderを使用して独自の設定やODBCパラメータとの互換性を設定できます)。

 jdbc:db2://192.168.72.135:50000/DB1:currentSchema=DB01;user=root;password=password;fullyMaterializeLobData=true;DB2NETNamedParam=1; 

サーバー、ポート、データベース、回線、ログイン、パスワードへの接続。 Blue Giant Webサイトで、JDBCおよびSQLJタイプ4接続用のIBM Data Server Driverの構成URL形式を確認できます

面白そうなものはすべて...

データベースにメリットをもたらすには、コマンドが必要です。

 public sealed class Db2Command : DbCommand, ICloneable 

私がいじくり回さなければならなかったのはDapperです。

db2command
Dapperは、パラメーターXxmlDocumentのXML_BODYを不明瞭(:XML_BODY1 ,: XML_BODY2)に頑固に置き換えます -単純なレギュラーを作成する必要がありました。

 new Regex(@"\(:(?<n>\w+)\d+,:\1\d+\)"); 

はい、ただし好奇心D盛Dapper-ulongがSystem.ArgumentExceptionをスローしているため、TypeHandlerをファイルすることはできました。 しかし、第一に、ODBC上のラッパーはXmlDocumentで既に機能し、第二に、アプリケーションの開始時にフォームのマジックコマンドを使用していました。

 SqlMapper.AddTypeHandler(DapperULongHandler.Default); 

常にエラーが発生します。

マジックメソッドは何回も


 internal static CallableStatement PrepareExecute(Db2Connection connection, string query, Db2ParameterCollection parameters) 

準備されたクエリを返します。

特に注意すべきは、prepareStatement( PreparedStatement )の代わりにprepareCall( CallableStatement )を使用することの説明です。 位置パラメータの使用は簡単ですが、場合によっては便利ではないため、名前付きパラメータを使用するには追加の作業が必要になり、アクションがすぐに減少し、コードの複雑さが増します。 名前付きパラメーターが優先されました。これは、サーバーに送信される要求をデバッグする方が簡単であり、複数の要求が実行される場合にボーナスを与えるためです。

彼は魔法のために他の2つを使います。

マジックメソッド2


 internal static string PrepareCommandText(string query, Db2ParameterCollection parameters, ref int parameter) 

位置パラメータを名前付きのものに置き換えます。

Dapperの小さな欺ceptionには2つの後藤がかかりました。 そして、それらなしで行うことは可能でしたが、解決策は単純であり、満足のいくものでした。

ref intパラメーター -Db2DataReaderおよびマルチクエリの実装に必要です。

最後の魔法の方法


 private static void PrepareParameters(CallableStatement stmt, Db2ParameterCollection parameters) 

パラメータを設定します。

緑豊かなifと多くのメソッドを呼び出すstmt.setXXX(名前、

コマンドには、パラメーターが必要です。

 public sealed class Db2ParameterCollection : DbParameterCollection, IList<Db2Parameter> 

 public sealed class Db2Parameter : DbParameter, ICloneable 

Db2パラメーター
Dapperの場合、メソッドに対して少し魔法をかける必要があります。

 public override DbType DbType 

型キャストに使用されるため、そうでない場合はインターフェイスを実装します。

そして、あなたはメソッドについて非常に注意する必要があります:

 public object Clone() 

複数のリクエストのパラメーターのコピーを作成する必要があるため。

Db2ParameterCollection
メソッドの最適化がわずかに存在するため、注意が必要です。

 public override int IndexOf(string parameterName) 
それ以外の実質的なものはありません

そして最後に:

 internal sealed class Db2DataReader : DbDataReader 

JDBCを介したマルチクエリの実行の実装。

Db2datareader
おそらくこれは最善の解決策ではありませんが、なぜそうではありません。

最初に 、リクエストをサブクエリに分割する必要があります。 驚きのない単純な正規表現

 new Regex(@"(?<q>[^;]+);?"); 

次に 、サブクエリごとに独自のパラメータセットを準備する必要があります。 活用する

 new Regex(@"(?<n>:\w+)"); 
位置パラメータを名前付きパラメータに置き換えるための既におなじみのDb2Command.PrepareCommandTextメソッド(ボーナスとして:Dapperは通常、マルチリクエストで名前付きパラメータを処理できます)。

第三に 、あるコマンドから次のコマンドに移動するためのパブリックオーバーライドbool NextResult()の小さなハックです。
 var b = _statement.getMoreResults(); if (b) { _rs = _statement.getResultSet(); } else { _statement = PrepareStatementAndResultSet(); if (_statement != null) { _rs = _statement.executeQuery(); b = true; } } 


まとめ


もちろん、プロジェクトはまだ完了していませんが、将来的には、db2jcc4.jar上のシェル実装を不要なスクワットなしの「通常の」.NETドライバー実装に置き換えたいという要望があります。

何が必要でしょうか?

まず、ユーザーのマシンにODBCドライバーをインストールすることはできませんが、ボリュームで勝つことはできません。
第二に、monoを使用してバックエンドをlinuxにゆっくり移行できます。 これは、 Dockerのささやきで非常に便利です。

そしてもちろん、ファンにとってはそれほど重要ではありません。

選択
 SELECT r.ID , r.INCOME , r.GU_CODE as GuCode , r.NO as No , r.DIVISION , d.NAME as DivisionName , u.LNAME || ' ' || u.FNAME || nvl(' ' || u.MNAME, '') as CreaterName , r.SIGNED , r.SIGNED_SYSUSER_ID as SignedID , su.LNAME || ' ' || su.FNAME || nvl(' ' || su.MNAME, '') as SignedName --, (select count(*) from PACK_ENTRY p where p.REGISTRY_ID = r.ID) as PackCount -- Use C# code --, (select count(*) -- from PACK_ENTRY p -- join FILE_ENTRY f on f.PACK_ID=p.ID -- where p.REGISTRY_ID = r.ID) as FileCount -- Use C# code -- TODO 2016-04-13 emiya:  .      REGISTRY_ENTRY , (select p.CLOSE_DATE from PACK_ENTRY p join REGISTRY_TO_PACK p2p on p2p.REGISTRY_ID = r.ID and p2p.PACK_ID = p.ID order by p.INCOME fetch first 1 rows only) as CloseDate , (select p.CATEGORY_ID from PACK_ENTRY p join REGISTRY_TO_PACK p2p on p2p.REGISTRY_ID = r.ID and p2p.PACK_ID = p.ID order by p.INCOME fetch first 1 rows only) as CategoryCode , (select l.SHORTNAME from PACK_ENTRY p join CATEGORY_LIST l on l.ID = p.CATEGORY_ID join REGISTRY_TO_PACK p2p on p2p.REGISTRY_ID = r.ID and p2p.PACK_ID = p.ID order by p.INCOME fetch first 1 rows only) as CategoryName , p2p.REGISTRY_ID , p.ID , p.INCOME , p.CLOSE_DATE as CloseDate , p.CATEGORY_ID as CategoryCode , l.SHORTNAME as CategoryName , p.GU_CODE as GuCode , p.NO , p.DIVISION , p.SYSUSER_ID as SysUserID , (select count(*) from FILE_ENTRY where PACK_ID = p.ID) as Count , r.SIGNED , p.PLACE , p2p.STATUS , p2p.NOTE from REGISTRY_ENTRY r join DIVISION d on d.CODE=r.DIVISION join SYSUSER u on u.ID=r.SYSUSER_ID LEFT JOIN SYSUSER su ON su.ID=r.SIGNED_SYSUSER_ID join REGISTRY_TO_PACK p2p on p2p.REGISTRY_ID = r.ID join PACK_ENTRY p on p.ID = p2p.PACK_ID join CATEGORY_LIST l on l.ID = p.CATEGORY_ID WHERE r.ID=? AND @DIVISION order by r.ID,p.GU_CODE,p.NO 


NuGetのパッケージにリンクします。

Source: https://habr.com/ru/post/J303878/


All Articles