2019年8月21日水曜日

Oracle SQL*Plus。MinGW32は癖がある (笑)

Oracle SQL*Plusを使うなんて20年ぶり
Windows環境で実行させて、結果をLinux環境に送り別の処理をする。

例えば、前日に新規登録されたデータだけを、翌日に抽出する場合。
Linux環境では、localtime_rで比較的簡単に日付指定の文字列を作れる。
ところが、Windows環境のMinGWは基本的にWindowsに依存しているので、
同じgccを使っても、localtime*は使用できない。
gccでも、違うんだぁ

Msys MinGWは、その環境で作った実行モジュールをWindowsのコマンドプロンプトで実行できない。
それをするなら、MinGWだけをインストールする必要がある。
MinGW | Minimalist GNU for Windows

でも、MinGWはWindowsに依存しているのでLinuxとの違いが発生する。
多分、今回のlocaltime*だけだと思うけど、、、

まぁ、やるといろいろ起こります。勉強勉強

で、今回のソースをドーンと

Peace!!


SQL*Plusを起動するバッチ

@ECHO OFF
:START
ECHO ---------------------------------------------------
ECHO 前日新規に登録された品目情報を出力します。
ECHO ---------------------------------------------------
CD %~dp0
DEL hinsabun.csv
hinsabun.exe > hinsabun.sql
@ECHO SQLを実行中です。しばらくお待ち下さい..
@ECHO -------------------------------
sqlplus /nolog @"hinsabun.sql" USER PASSWORD 192.168.XXX.XXX/ORCL
@ECHO -------------------------------
@ECHO 完了しました。
EXIT

localtime_r を使わない処理

#include <stdio.h>
#include <string.h>
#include <time.h>

#define PRS(A)  printf("%s\n", A)
char    *mn[]= {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

int main(int argc, char* argv[]){
    char    szTime[15];
    char    szBuf[256], szBuf1[64];
    char    szMn[4], szDmy[10];
    int     n = 1, iDay, i, iMn, iYr;
    struct tm tm;
    time_t t;
    
    time(&t);
    t -= (86400 * n);
    strcpy(szBuf1, ctime(&t));
    strcpy(szDmy, strtok(szBuf1, " "));
    strcpy(szMn, strtok(NULL, " "));
    iDay = atoi(strtok(NULL, " "));
    strcpy(szDmy, strtok(NULL, " "));
    iYr = atoi(strtok(NULL, " "));
    for (i = 0; i < 12; i++) {
        if (strcmp(szMn, mn[i]) == 0){
            iMn = i + 1;
            break;
        }
    }
    sprintf(szTime, "%04d%02d%02d", iYr, iMn, iDay);

    //localtime_r(&t, &tm);
    //sprintf(szTime, "%04d%02d%02d%02d%02d%02d",
    //        tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
    //        tm.tm_hour, tm.tm_min, tm.tm_sec);
    //sprintf(szTime, "%04d%02d%02d",
    //          tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
    
    PRS("connect &&1/&&2@&&3");
    PRS("spool off");
    PRS("set echo off");
    PRS("set heading off");
    PRS("set termout off");
    PRS("set pause off");
    PRS("set pagesize 0");
    PRS("set linesize 10000");
    PRS("--set trimout on");
    PRS("set trimspool on");
    PRS("set feedback off");
    PRS("set verify off");
    PRS("set colsep '|'");
    PRS("spool hinsabun.csv");
    sprintf(szBuf, "SELECT ITEM_CD, ITEM_DIV, ITEM_NAME1, ITEM_NAME2, STD_UNIT, STD_UNITPRICE, UNIT2, UNIT_CONV_RATE,ITEM_NAME4,REGIST_DATE, UPDATE_DATE from DFW_M040M where REGIST_DATE >= to_date('%s', 'yyyymmdd');", szTime);
    PRS(szBuf);
    PRS("spool off");
    PRS("exit");
    
    return 0;
}

2019年8月11日日曜日

AsyncTaskによるDB非同期処理をListenerを使ってクラス化

Android ActivityでDBを頻繁に使うプログラムを開発する場合に、
前項のようなコードをActivity内に組込むのはやめたい。
クラス化にあたっての課題は、処理の結果を呼び出したクラスに渡すか?
非同期処理処理を踏まえてどうするか。。。
で、先人の知恵を借りてListenerを使って呼び出し側で非同期処理の終了を
解るようにして、一件落着
ますます、便利になった。
Peace!!

ソースの先頭には、使い方を書いた。
サーバーサイドのDBプログラムは別途必要です。
ソースコード、どーんと(笑)

/***********************************************************
 * AsyncTask<型1, 型2,型3>
 *   型1 … Activityからスレッド処理へ渡したい変数の型
 *          ※ Activityから呼び出すexecute()の引数の型
 *          ※ doInBackground()の引数の型
 *   型2 … 進捗度合を表示する時に利用したい型
 *          ※ onProgressUpdate()の引数の型
 *   型3 … バックグラウンド処理完了時に受け取る型
 *          ※ doInBackground()の戻り値の型
 *          ※ onPostExecute()の引数の型
 *   ※ それぞれ不要な場合は、Voidを設定すれば良い
 * --------------------------------------------------------------
 * 呼び出す側に以下を追加する
 ********************************************************************************
 * 汎用DBアクセスクラス SqlOpeAsyncTask を使う場合のListener設定
 * クラスを使う側で設定する。
 * onDestroyは、使うクラス側で重ならなければ使えるかも。未確認
 * @return
 * /
    private SqlOpeAsyncTask.Listener createListener(){
        return new SqlOpeAsyncTask.Listener(){
            @Override
            public void onSuccess(String ret){
                // 以下は個々のプログラム毎に記述
                etKata.setText(ret);
            }
        };
    }
    @Override
    protected void onDestroy() {
        SqlOpeAsyncTask.setListener(null);
        super.onDestroy();
    }

    // 呼び出し例
        String in1 =  getResources().getString(R.string.url_srv_gets1);
        String in2 =  "?100:select kata,tana,hinc,kazu from zaiko where hinc = '15916-1' and tana <> 'REFER' order by tana DESC; ";
        SqlOpeAsyncTask retAsyn = new SqlOpeAsyncTask();
        retAsyn.setListener(createListener());
        String inStr = in1 + in2;
        Log.d(TAG, "before Async execute");
        retAsyn.execute(inStr);
 ***********************************************************************/

package com.futureagri.app.thn.hd.java;

import android.nfc.Tag;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.TextView;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

/******************************************************************
 * 非同期でデータベース処理をするクラス。
 ******************************************************************/
public  class SqlOpeAsyncTask  extends AsyncTask<String, Void, String> {
    private Listener listener;

    private static final String TAG = "SqlOpeAsyncTask";
    private String retString = "";
    private TextView textView;

    /********************************************************************
     * メインスレッドとは別のスレッドで実行されます。
     * 非同期で処理したい内容を記述します。
     * このメソッドだけは必ず実装する必要があります
     * @param data
     * @return
     */
    @Override
    //protected String doInBackground(Object[] data) {
    public String doInBackground(String... data) {
        // target URL
        String urlStr = (String)data[0];
        Log.d(TAG, "doInBackground: " + "input sql=" + urlStr);
        // Variable of set result texts
        //StringBuilder result = new StringBuilder();
        String result = "";
        //http接続を行うHttpURLConnectionオブジェクトを宣言。finallyで確実に解放するためにtry外で宣言。
        HttpURLConnection con = null;
        //http接続のレスポンスデータとして取得するInputStreamオブジェクトを宣言。同じくtry外で宣言。
        InputStream is = null;
        int i = 10;
        try {
            //URLオブジェクトを生成。
            URL url = new URL(urlStr);
            //URLオブジェクトからHttpURLConnectionオブジェクトを取得。
            con = (HttpURLConnection) url.openConnection();
            //http接続メソッドを設定。
            con.setRequestMethod("GET");
            //接続。
            con.connect();
            final int   status = con.getResponseCode();
            if (status == HttpURLConnection.HTTP_OK) {
                // Success HTTP GET method
                is = con.getInputStream();
                result = is2String(is);
                is.close();
            }else {
                Log.d(TAG, "doInBackground: HTTP response error");
            }
        } catch (MalformedURLException ex) {
            ex.printStackTrace();
            //} catch (ProtocolException ex) {
            //ex.printStackTrace();
            Log.d(TAG, "doInBackground: HTTP MalformedURLException");
        } catch (IOException ex) {
            ex.printStackTrace();
            Log.d(TAG, "doInBackground: HTTP IOException");
        } finally {
            // HttpURLConnectionオブジェクトがnullでないなら解放。
            if (con != null) {
                con.disconnect();
            }
            // InputStreamオブジェクトがnullでないなら解放。
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ex) {
                    Log.d(TAG,"doInBackground: HTTP close is IOException");
                }
            }
        }
        // 非同期で取得したデータをセットする
        if ( i == 0) {
            Log.d(TAG, "doInBackground: HTTP return : error end");
            return "-99999";
        }else {
            //return result.toString();
            retString = result;
            return result;
        }
    }
    /*****************************************************************************
     * doInBackgroundメソッドの実行後にメインスレッドで実行されます。
     * doInBackgroundメソッドの戻り値をこのメソッドの引数として受け取り、
     * その結果を画面に反映させることができます
     * @param result
     */
    @Override
    public void onPostExecute(String result) {
        //toastMake(result, 0, -200);
        Log.d(TAG, "onPostExecute: result :" + result);
        String[] ret = result.split("\\|");  // Mac \ -> (option + ¥)
        int iMode = Integer.parseInt(ret[0]);   // 呼び出した処理番号
        int cnt  = Integer.parseInt(ret[1]);    // 検索数
        int retu = Integer.parseInt(ret[2]);    // 列数
        Log.d(TAG, "onPostExecute: mode:cnt:retu=" + iMode + ":" + cnt + ":" + retu);
        //-------------------------------------------------------------
        //  mode=100 kata,tana,hinc,kazu
        //-------------------------------------------------------------
        if (iMode == 100) {
            retString = iMode + "|" + cnt + "|" + retu + "|";
            for (int i = 0; i < cnt * retu; i++) {
                retString = retString + ret[3 + i] + "|";
            }
        }
        if (listener != null) {
            listener.onSuccess(retString);
        }
    }
    /*****************************************************************************
     * InputStreamオブジェクトを文字列に変換するメソッド。変換文字コードはUTF-8。
     *
     * @param is 変換対象のInputStreamオブジェクト。
     * @return 変換された文字列。
     * @throws IOException 変換に失敗した時に発生。
     */
    private String is2String(InputStream is) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
        StringBuffer sb = new StringBuffer();
        char[] b = new char[2048];
        int line;
        while(0 <= (line = reader.read(b))) {
            sb.append(b, 0, line);
        }
        return sb.toString();
    }
    /************************************************************
     * Listener 処理を定義する
     * 
     * @param listener
     */
    void setListener(Listener listener) {
        this.listener = listener;
    }
    interface Listener {
        void onSuccess(String  count);
    }
}

2019年8月2日金曜日

Android 非同期処理でサーバサービスを利用する

スマフォからデータベース処理する方法のメモ
至る所に掲載されているので、あくまでも自分のメモ

基本的にデータ量が少ないのでGETメソッドで説明
ちなみに、com.squareup.okhttp3:okhttp:3.12.1 を使っています。

1.基本的な流れ

・スマフォ側で、起動するサーバサイドのサービスの定義と渡す情報の設定
・非同期処理部分(AsyncTask)の記述。
   基本部分は一度書けば使いまわし。
   大事なのは、onPostExecuteの部分。
   この部分にサーバから戻ってきたデータに対する処理を記述する。
   この投稿では、データをチェックしたあと、Intentで別のActivity起動している

2. スマフォ側でサーバプログラム起動の準備

オブジェクト作成と、起動サービス、引数の設定
非同期処理のHTTPサービスのオブジェクトを作成して実行

Object[] data = new Object[3];
data[0] = getResources().getString(R.string.url_srv_reg_payout);
// 引数を設定する
data[1] =

hdPartsActivity.SqlSelectReciever receiver =
               new hdPartsActivity.SqlSelectReciever();
receiver.execute(qq);

3.非同期処理(AsyncTask)を担当する本体部分

   private class SqlSelectReciever extends AsyncTask<String, String, String> {
        public SqlSelectReciever() {
        }
        @Override
        public String doInBackground(String... params) {
            // target URL
            String urlStr = (String)params[0];
            Log.d(TAG, " SqlSelectReciever: " + "sql=" + urlStr);
            // Variable of set result texts
            //StringBuilder result = new StringBuilder();
            String result = "";
            //http接続を行うHttpURLConnectionオブジェクトを宣言。finallyで確実に解放するためにtry外で宣言。
            HttpURLConnection con = null;
            //http接続のレスポンスデータとして取得するInputStreamオブジェクトを宣言。同じくtry外で宣言。
            InputStream is = null;
            int i = 10;
            try {
                //URLオブジェクトを生成。
                URL url = new URL(urlStr);
                //URLオブジェクトからHttpURLConnectionオブジェクトを取得。
                con = (HttpURLConnection) url.openConnection();

                //http接続メソッドを設定。
                con.setRequestMethod("GET");
                //接続。
                con.connect();
                final int   status = con.getResponseCode();
                if (status == HttpURLConnection.HTTP_OK) {
                    // Success HTTP GET method
                    is = con.getInputStream();
                    result = is2String(is);
                    Log.d(TAG, " HTTP connect result=" + result);
                    is.close();
                }else {
                    Log.d(TAG, "HTTP response error");
                }
            } catch (MalformedURLException ex) {
                ex.printStackTrace();
                //} catch (ProtocolException ex) {
                //ex.printStackTrace();
                Log.d(TAG, "HTTP MalformedURLException");
            } catch (IOException ex) {
                ex.printStackTrace();
                Log.d(TAG, "HTTP IOException");
            } finally {
                // HttpURLConnectionオブジェクトがnullでないなら解放。
                if (con != null) {
                    Log.d(TAG, "HTTP not null");
                    con.disconnect();
                }
                // InputStreamオブジェクトがnullでないなら解放。
                if (is != null) {
                    Log.d(TAG,"HTTP close InputStream");
                    try {
                        is.close();
                    } catch (IOException ex) {
                        Log.d(TAG,"HTTP close is IOException");
                    }
                }
            }
            //JSON文字列を返す。
            if ( i == 0) {
                Log.d(TAG, "HTTP return : error");
                return "error";
            }else {
                //Log.d("doInBackground:return:", result.toString());
                //return result.toString();
                //Log.d(TAG, "HTTP return : normal");
                return result;
            }
        }
        //=================================================
// サーバから戻ってきた戻り値に対する処理
// ここの部分が大事。
// Activityの中で複数の非同期処理をする時もここにを書く。
// 因みに僕は戻り値の最初の情報cntとretuを使って複数対応している。
//=================================================
        @Override
        public void onPostExecute(String result) {
            String[] ret = result.split("\\|");  // Mac \ -> (option + ¥)
            int cnt  = Integer.parseInt(ret[0]);  // 1行目:検索数
            int retu = Integer.parseInt(ret[1]);  // 1行目:列数
            Log.d(TAG, "cnt:retu=" + cnt + ":" + retu);
                 ・
                 ・ ここにいろいろ処理を書く、
                 ・
            Intent intent = new Intent(hdPartsActivity.this, SqlGetList2.class);
            // intentへ添え字付で値を保持させる
            intent.putExtra( "hdkey", hks);
            // 返却したい結果ステータスをセットする
            setResult( SqlGetList2.RESULT_OK, intent );
            //startActivity(new Intent(hdSeibanListActivity.this, SqlGetList2.class));
            startActivity(intent);
            //Log.d(TAG, " Intent after : startActivity(intent)");
            return;
        }
        /**
         * InputStream objectを文字列に変換するmethod。変換文字codeはUTF-8。
         *
         * @param is 変換対象のInputStreamオブジェクト。
         * @return 変換された文字列。
         * @throws IOException 変換に失敗した時に発生。
         */
        private String is2String(InputStream is) throws IOException {
            BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            StringBuffer sb = new StringBuffer();
            char[] b = new char[1024];
            int line;
            while(0 <= (line = reader.read(b))) {
                sb.append(b, 0, line);
            }
            return sb.toString();
        }
    }

Peace!!