Android 仲介なしでMS SQLを使用します





警告:

一般的に、記事の情報は特定のケースにのみ適用されます。 たとえば、隔離されたネットワークのセグメント内でアプリケーションが実行される場合。 しかし、一般的なケースでは、httpサーバーである仲介者がまだ必要です。 少なくとも説明した方法では、データベースにアクセスするためのログイン/パスワードはアプリケーションで保護され、ネットワーク経由で送信されます。



この記事は彼が以前の投稿で書いた仕事の続きです。 最初は、この部分を書きたくありませんでした(警告を参照)が、このトピックはまだハブでカバーされておらず、一般にネットワークに関する情報は少ないです。

したがって、AndroidでMS SQLを直接操作する方法に興味がある場合(他のデータベースで行うと仮定するのは当然ですが、実際にはこれを行いませんでした)、catにようこそ。



Java(およびAndroid)では、リモートデータベースへの接続はJDBCドライバーを使用して行われます 。 私の特定のケースでは、Microsoftサーバー、およびそのための2つのドライバーがあります。MicrosoftからのものとJTDSのオープンな代替物です。 さらに、開発者によると、後者は公式のものよりも高速で安定しています。 ここで使用します。



レーキ:投稿の執筆時点でのJTDS 現在のバージョンは1.3.1です。 ただし、バージョン1.3.0以降、ドライバーはJava 7との互換性のために書き直され、Androidで動作するこれらのバージョンの問題に関するネットワーク上のメッセージがあります。 したがって、Java 6用の1.2。* Branch (1.2.8) の最新の安定バージョンを使用する必要があります



SQLサーバーは、TCP / IPを介し動作するように構成する必要があります



データ検索


ドライバーは、Android Cursorに似たResultSetインターフェースで要求データを返しますが、 ResultSetを カーソルに移動させる簡単な方法が見つかりませんでした。 したがって、異なる動作をします。ResultSetのデータはJSONArrayに変換され、メインアプリケーションロジックに返されます。ここから、それらを使用して何でもできます。



潜在的に時間がかかる操作としてのすべてのデータ交換は、非同期で行われます。 結果は、MS SQLに対するクエリのための次のような素晴らしいクラスです。



編集: 基本に従って、例のConnection、Statement、およびResultSetのクロージャーを書き直しました。



public final class AsyncRequest extends AsyncTask<String, Void, JSONArray> { final static String MSSQL_DB = "jdbc:jtds:sqlserver://<YOUR_DB_IP>:<YOUR_DB_PORT>:/<YOUR_DB_NAME>;" final static String MSSQL_LOGIN = "<YOUR_DB_LOGIN>"; final static String MSSQL_PASS= "<YOUR_DB_PASS>"; @Override protected JSONArray doInBackground(String... query) { JSONArray resultSet = new JSONArray(); try { Class.forName("net.sourceforge.jtds.jdbc.Driver"); Connection con = null; Statement st = null; ResultSet rs = null; try { con = DriverManager.getConnection(MSSQL_DB, MSSQL_LOGIN, MSSQL_PASS); if (con != null) { st = con.createStatement(); rs = st.executeQuery(query[0]); if (rs != null) { int columnCount = rs.getMetaData().getColumnCount(); //    JSONArray while (rs.next()) { JSONObject rowObject = new JSONObject(); for (int i = 1; i <= columnCount; i++) { rowObject.put(rs.getMetaData().getColumnName(i), (rs.getString(i) != null) ? rs.getString(i) : ""); } resultSet.put(rowObject); } } } } catch (SQLException e) { e.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } finally { try { if (rs != null) rs.close(); if (st != null) st.close(); if (con != null) con.close(); } catch (SQLException e) { throw new RuntimeException(e.getMessage()); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } return resultSet; } @Override protected void onPostExecute(JSONArray result) { // TODO:   } }
      
      





Webサーバーからデータを受信して​​いるかのように、クラスへの入力にリクエストが送信され、既製のJSONArrayが出力されます。 別のスレッドで、AsyncTaskはサーバーに接続し、ResultSetでデータを受信し、それらからJSONを形成します。 一般に、コードは原始的であり、説明を必要としないと思います。



同様の原理で動作するシステムを構築するには、入力に純粋な選択クエリを送信するのではなく、必要な選択を取得できるパラメーターを渡して既製のT-SQL関数をサーバーに書き込む方が良いでしょう。



挿入および更新。 サーバーへのデータ転送


残念ながら、ここではトランザクションでInsertを実行するだけで、これ以上良いものは思いつきませんでした。 さらに、このメソッドは正常に機能し、数百のレコードを挿入するには許容時間(100行ごとに約1秒、上記の例よりも実際のプロジェクトに多くのフィールドがあります)がかかります



編集: eyeless_watcherのアドバイスに従って、PreparedStatementにデータを入力するときにaddBatch()メソッド使用します。 現在、データ挿入は1つのトランザクションで実際に迅速に実行されます。 例が変更されました。



 public final class AsyncInsert extends AsyncTask<String, Void, JSONArray> { private static final String REMOTE_TABLE = "dbo.TableName"; private static final String SQL = "INSERT into " + REMOTE_TABLE + "([" + ListItemScanned.BARCODE + "],[" + ListItemScanned.NR_ID + "],[" + ListItemScanned.DATE + "],[" + ListItemScanned.STATUS + "]) values(?,?,?,?)"; private final List<ListItemScanned> mData; public AsyncInsert(List<ListItemScanned> data) { this.mData = data; } @Override protected JSONArray doInBackground(String... proc_params) { JSONArray resultSet = new JSONArray(); try { Class.forName("net.sourceforge.jtds.jdbc.Driver"); Connection con = null; PreparedStatement prepared = null; try { con = DriverManager.getConnection(MSSQL_DB, MSSQL_LOGIN, MSSQL_PASS); if (con != null) { prepared = con.prepareStatement(SQL); for (ListItemScanned item : mData) { prepared.setString(1, item.get(ListItemScanned.BARCODE)); prepared.setString(2, item.get(ListItemScanned.NR_ID)); prepared.setString(3, item.get(ListItemScanned.DATE)); prepared.setString(4, item.get(ListItemScanned.STATUS)); prepared.addBatch(); resultSet.put(item.get(ListItemScanned.ID)); } prepared.executeUpdate(); return resultSet; } } catch (SQLException e) { e.printStackTrace(); } finally { try { if (prepared != null) prepared.close(); if (con != null) con.close(); } catch (SQLException e) { throw new RuntimeException(e.getMessage()); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } return resultSet; }
      
      





PreparedStatementは 、目的の値を挿入するために使用されます。 何らかの理由でのフィールドの番号付けは、ユニットから始まります(ドキュメントを参照)。 そして、残り-すべてが明確でなければなりません。 updateも同様に、 executeUpdateを使用して実装できます。



上記のアプローチは、「戦闘」アプリケーションで初めて使用されました。

実際には、安定して動作することが判明しました。 データベースとの接続時間は数秒かかる場合があります(Wi-Fi経由で接続し、サーバーは企業全体に共通です)が、トランザクション自体は迅速に実行されます。



エキストラと批判は大歓迎です:)



All Articles