- 投稿日:2019-04-13T21:06:32+09:00
プログラミング学習記録56〜動画とエディタを行き来するのが疲れるからデュアルディスプレイにしたい〜
今日やったこと
- Javaに触れる
- 動画教材とエディタを行き来するのが精神的に疲れるので、デュアルディスプレイの導入を検討する
今日は講義でJavaに触れました。
実行環境は用意されているものを使いました。
Cloud9のような感じです。Javaに触れた感想としては、今の所はまだ入門段階なので、他の言語と大差ないなという印象ですね。
ただ、Rubyと比べると記述量が多い気がします。あと、以前から思っていたことなのですが、動画を見ながらエディタにコードを打ち込んでいく作業をするときは2画面でやったほうがいいですね。
1画面でやろうとすると、画面を半分半分にして小さい画面で頑張るか、別のウィンドウで開いて切り替えながらやることになります。
これが意外と面倒になってきたので、デュアルディスプレイ(2画面)で作業していきたいと思います。
最初はお金がかかっちゃいますけど、その後の作業効率考えたら導入した方がいいです。調べてみたらMacBookの場合はiPadもサブディスプレイに使えるそうなので、まずはiPadで試していきます。
iPadで画面の大きさが足りないようであれば、別にディスプレイを用意したいと思います。
ということで、明日からもプログラミング学習頑張ります。
おわり
- 投稿日:2019-04-13T20:19:57+09:00
Mockitoの記述方法
JUnit + Mockitoでテストする際のMockitoの記述方法が紹介するページによりマチマチなので自分なりに整理した。
環境
- Mockito v2.23.9
- JUnit v4.12(JUnit5にしたい)
Mockitoの機能
Mock
対象インスタンスのメソッドをすべてモック化して置き換えたい場合に使用する。
デフォルト状態では、オブジェクトの場合、戻り値があるメソッドはnullを返すようになる。(Collectionでは空のCollectionだったり、プリミティブな値では0とかfalseとか...)
@Mockのアノテーションを付与することでモック化できる。
また、テスト対象となるクラスのインスタンスに@InjectMocksを付与することで、
インスタンス内の@Injectされたメンバーのインスタンスに@Mockのモックインスタンスを差し込むことができる。Spy
対象のインスタンスの特定のメソッドだけを置き換えたい場合に使用する。
私はあまり使いこなせていない。
getterやらのメソッドはそのまま使いつつ1つのメソッドだけ置き換えたい場合とかがある?
@Spyのアノテーションを付与することで利用できる。
インスタンスは自分で生成する必要がある。
@InjectMocksで注入することはできない。Captor
主に引数の値をキャプチャして検証するのに使用する。
引数がオブジェクトの場合、eqのような標準のマッチャでは検証できない。
このとき、Captorが有効である。引数をキャプチャする場合、ArgumentCaptorクラスのインスタンスを定義し
@Captorを付与する。基本のテストの書き方
DIを利用した設計をしていることを前提に、Mockitoを使った基本的な記述をまとめる。
基本方針
- Mockitoのモードは
StrictStubsにする。
- Mockitoには
Silent,Strict(v2のデフォルト),StrictStubsの3つのモードがある。StrictStubsにすると、スタブとなるモックの引数ミスマッチも検出してくれるので、一番厳しく設定。- 初期化は原則アノテーションで行う。
mock(XX.class)やspy(new XX()),ArgumentCaptor.forClass(XX.class)を使った初期化方法もあるが、アノテーション使ったほうがシンプルに書ける。@InjectMocksを付与する対象はインターフェースにしないよう注意。(newできない)verifyは書かなくてよい。
- v2からは用意したモックスタブが呼ばれなかった場合、非チェック例外が発生するので、呼ばれたかいちいち
verifyで検証する必要はないと思う。- あるとすれば、複数回呼ばれた回数をカウントしたい場合くらい?
- モックの記述は
doXX().when()の形式で書く。
- ここは賛否両論あり、公式も態度明らかにしていない感じである。
you may prefer to use these methods in place of the alternative with when(), for all of your stubbing calls.とのこと。- 個人的には、戻り値の有無やMockとSpyの違いで記述方法をいちいち覚えるのは面倒。見た目も煩雑になると思う。
- よって、すべての記述で使える
doXX().when()の形式に統一する。- ただし、後述する呼び出しによって戻り値を変えるケースでは
when().thenReturn()形式での記述が必要になる。前準備
- テストでMockitoを使うためにおまじない
@RunWith(MockitoJUnitRunner.StrictStubs.class)テストクラスに付与する。
@Ruleで同様の設定をしても良い。(Strictness.STRICT_STUBS)- テスト対象となるクラスの変数に
@InjectMocksを付与する。- モック化したいクラスの変数に
@Mockを付与する。
- @InjectMocksおよび@Mockのアノテーションを付与するだけでインスタンスが生成される。
import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; ... @RunWith(MockitoJUnitRunner.StrictStubs.class) public class XXTest{ @InjectMocks XXInteractor target; @Mock IXXRepository repoMock; ... }テストケース
@Testを付与したvoid publicメソッドを定義する。- モックインスタンスのモック化したいメソッドの定義を記述する。
- テスト対象のメソッドを呼ぶ。
- 結果を検証する。
テスト対象が以下のとき、
public class XXInteractor{ @Inject IXXRepository repository; XXEntity fetch(String id) throws NotFoundException{ try{ return repository.findBy(id); } catch(UseCaseNotFoundException e){ throw new NotFoundException(); } } }以下のようなテストが書ける。
private static final String ID = "ID"; @Test public void testFetchSuccess() throws Exception{ // 引数にIDが与えられたときだけ正常なXXEntityを返す。(その他の場合、null) doReturn(new XXEntity(ID)) .when(repoMock).findBy(eq(ID)); XXEntity result = target.fetch(ID); assertThat(result.getId(), is(ID)); } @Test(expected = NotFoundException.class) public void testFetchFailureWithNotFound() throws Exception{ doThrow(new RepositoryNotFoundException()) .when(repoMock).findBy(eq(ID)); target.fetch(ID); }モック化ユースケース
基本編
基本的なモック化パターンを以下に示す。
モック化したメソッドで値を返す
// 戻り値がある場合 doReturn(new XXEntity(ID)) .when(targetMock).fetch(eq(ID)); // 戻り値がない場合 doNothing().when(targetMock).delete(eq(ID))モック化したメソッドで例外を発生させる
doThrow(new XXException()).when(targetMock).fetch(eq(ID)) doThrow(new XXException()).when(targetMock).delete(eq(ID))Spyしたインスタンスのメソッドを置き換える
// 戻り値を返す doReturn(new XXEntity()).when(targetSpy).fetch() // 例外を発生させる doThrow(new XXException()).when(targetSpy).fetch()応用編
どんな引数でも呼ばれたらレスポンスしたい
doReturn(new XXEntity(ID)) .when(targetMock).fetch(any())同じメソッドで呼び出しごとに違う値を返したい
// 引数がないケース、あるいは、同じ引数で呼び出しごとに別の値を返したいケース // このケースはdoReturnでは書けないのでthenReturn形式で記述する。 when(targetMock.getNextKey()) .thenReturn("ID-1") // 一度目の呼び出し .thenReturn("ID-2"); // 二度目の呼び出し // 引数ごとに違う値を返すケース(普通にそれぞれ定義) doReturn(new XXEntity("ID-1")).when(targetMock).fetch(eq("ID-1"); doReturn(new XXEntity("ID-2")).when(targetMock).fetch(eq("ID-2");プリミティブでない引数を検証したい
引数がプリミティブな場合は
eqのみで検証できるが、引数がオブジェクトやリスト・マップ系の場合は検証できないため、Captorと組み合わせる必要がある。テスト対象が以下のとき、
public class XXInteractor{ @Inject IXXRepository repository; void update(XXEntity entity){ repository.update(entity); } }以下のように検証する。
private static final String ID = "ID"; @InjectMocks XXUseCase target; @Mock IXXRepository repoMock; // Captorを定義 @Captor ArgumentCaptor<XXEntity> argCaptor; @Test public void testUpdateSuccess(){ // 引数をキャプチャするためにモックを定義 doNothing().when(repoMock).update(argCaptor.capture()); target.update(ID); // キャプチャした引数を検証 assertThat(argCaptor.getValue().getId(), is(ID)); }モックに渡ってきた引数を書き換えたい
引数の一部を戻り値に使うC#でいう
refのような実装をしているメソッドをモックにするケース。
このメソッド定義はTestableでないので、根本的には設計変更で解決すべきだが、
どうしても引数でもらったインスタンスの中身を書き換えたい場合は以下の方法で実現できる。doAnswer( invocation -> { Object[] args = invocation.getArguments(); // 引数をそのクラスにキャストすることで、そのインスタンスを操作できるようになる。 // このケースでは第二引数を書き換えるのでargs[1] ((XXOutputParam) args[1]).setId("ID"); return null; //戻り値がvoidならnull }).when(useCaseMock).get(any(), any())モックの動きをリセットしたい
@Beforeのメソッドあたりで毎回呼ぶと良い。reset(mock);参考
https://static.javadoc.io/org.mockito/mockito-core/2.23.4/org/mockito/Mockito.html
- 投稿日:2019-04-13T17:32:46+09:00
【Android】Google MapのinfoWindowにウェブ上の画像を表示する
1.プロジェクト作成
新規プロジェクトを作成します。
Google Maps Activityを選択し、プロジェクト名を入力して進みます。
自動的に生成されるres/values/google_maps_api.xmlのYOUR_KEY_HEREの部分を、取得してある自身のAPIキーで置き換えておきます。google_maps_api.xml<resources> <!-- 略 --> <string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">YOUR_KEY_HERE</string> </resources>この時点で一度プログラムを起動し、地図がちゃんと表示される事を確認します。
2.レイアウトファイルを記述する
infoWindow用のレイアウトを作成します。今回はLinearLayoutにImageViewが一つあるだけのシンプルなものにします。
info_window_layout.xml<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/img" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>3.InfoWindowAdapterの記述
MapsActivity.javaのonMapReadyメソッドにて、InfoWindowAdapterを実装します。
MapsActivity.java@Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; // 〜略〜 mMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() { Marker lastMarker; View infoWindow; @Override public View getInfoWindow(Marker marker) { return null; } @Override public View getInfoContents(Marker marker) { // 今回はこちらに処理を記述 return null; } });infoWindowAdapterはgetInfoWindow()メソッドとgetInfoContents()メソッドの両方をオーバーライドする必要があり、かつどちらかに処理を記述しもう片方はnullを返す必要があります。今回はgetInfoContents()に処理を記述します。また、処理の都合上必要な変数lastMarkerとinfoWindowをinfoWindowAdapterのクラス内で宣言しておきます。
4.getInfoContents()メソッドの記述
@Override public View getInfoContents(Marker marker) { if(lastMarker==null || !lastMarker.equals(marker)){ lastMarker = marker; infoWindow = getLayoutInflater().inflate(R.layout.info_window_layout,null); ImageView img = infoWindow.findViewById(R.id.img); new DownloadImageTask(img,marker).execute(imgUrl); // ↑このあと実装 } return infoWindow; }最後まで実装してから細かく処理を追えばわかることですが、マーカーをクリックしてinfoWindowを表示させようとするとgetInfoWindow()メソッドは合計2回呼ばれることになります。1回目で画像をダウンロードする処理が走り、2回目はreturn infoWindow;だけ実行され、無限ループにならないようにしています。
5.画像のダウンロード処理のクラスを記述
private class DownloadImageTask extends AsyncTask<String,Void, Bitmap>{ ImageView img = null; Marker marker = null; DownloadImageTask(ImageView img, Marker marker){ this.img = img; this.marker = marker; } @Override protected Bitmap doInBackground(String... strings) { Bitmap bmp = null; try{ String urlStr = strings[0]; URL url = new URL(urlStr); HttpURLConnection con = (HttpURLConnection)url.openConnection(); con.setRequestMethod("GET"); con.connect(); int resp = con.getResponseCode(); switch (resp){ case HttpURLConnection.HTTP_OK: InputStream is = con.getInputStream(); bmp = BitmapFactory.decodeStream(is); is.close(); break; default: break; } } catch (MalformedURLException e) { e.printStackTrace(); } catch (ProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return bmp; } @Override protected void onPostExecute(Bitmap bmp){ img.setImageBitmap(bmp); marker.showInfoWindow(); } }画像のダウンロード処理のクラスであるDownladImageTaskを記述します。画像のダウンロードは非同期で行う必要があるのでAsyncTaskを継承しています。画像のURLは、execute()で呼び出すときにStringで受け渡し、doInbackground()のstrings[0]で受け取ります。画像のダウンロードが完了したあとで呼ばれるonPostExecute()でImageViewに画像をセットしており、そのあとmarker.showInfoWindow()を呼ぶことでinfoWindowに画像が表示されます。
6.MapsActivity.javaの全体
マーカーはデフォルトの位置であるシドニー、画像は、ウェブ版のGoogle mapでシドニーで検索したときに出たもののurlを指定しています。
MapsActivity.javapackage com.example.test; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.support.v4.app.FragmentActivity; import android.os.Bundle; import android.view.View; import android.widget.ImageView; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.ProtocolException; import java.net.URL; public class MapsActivity extends FragmentActivity implements OnMapReadyCallback { private GoogleMap mMap; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_maps); // Obtain the SupportMapFragment and get notified when the map is ready to be used. SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map); mapFragment.getMapAsync(this); } @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; final String imgUrl = "https://lh5.googleusercontent.com/p/AF1QipNUrZvzjGohRsYfuwIpCS2MjYdAq_3xruYM5imS=w408-h271-k-no"; // Add a marker in Sydney and move the camera LatLng sydney = new LatLng(-34, 151); mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney")); mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney)); mMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() { Marker lastMarker; View infoWindow; @Override public View getInfoWindow(Marker marker) { return null; } @Override public View getInfoContents(Marker marker) { if(lastMarker==null || !lastMarker.equals(marker)){ lastMarker = marker; infoWindow = getLayoutInflater().inflate(R.layout.info_window_layout,null); ImageView img = infoWindow.findViewById(R.id.img); new DownloadImageTask(img,marker).execute(imgUrl); } return infoWindow; } }); } private class DownloadImageTask extends AsyncTask<String,Void, Bitmap>{ ImageView img = null; Marker marker = null; DownloadImageTask(ImageView img, Marker marker){ this.img = img; this.marker = marker; } @Override protected Bitmap doInBackground(String... strings) { Bitmap bmp = null; try{ String urlStr = strings[0]; URL url = new URL(urlStr); HttpURLConnection con = (HttpURLConnection)url.openConnection(); con.setRequestMethod("GET"); con.connect(); int resp = con.getResponseCode(); switch (resp){ case HttpURLConnection.HTTP_OK: InputStream is = con.getInputStream(); bmp = BitmapFactory.decodeStream(is); is.close(); break; default: break; } } catch (MalformedURLException e) { e.printStackTrace(); } catch (ProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return bmp; } @Override protected void onPostExecute(Bitmap bmp){ img.setImageBitmap(bmp); marker.showInfoWindow(); } } }参考
https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap.InfoWindowAdapter
https://stackoverflow.com/questions/36335004/how-to-get-image-in-infowindow-on-google-maps-with-picasso-android
- 投稿日:2019-04-13T14:28:55+09:00
ライブラリ共有、JavaからMySqlへアクセス
DBを利用するだけなのに、DAO、entity、XMLを3セット書かなくちゃ!
フレームワークは面倒くさいと思いつつ、今回のライブラリを作成した。1.メリット
・XML不要(sql文を記述する設定ファイル)
・DAO不要(sqlを実行するクラス)
・フレーム不要・・・・・・・
Q:なにも不要?ちゃんとしている機能はないじゃない??
A:いえいえ、sql及びentityによるDB操作はしっかりサポートしているし、かつ一つクラスで完結!2.ライブラリ紹介
sqlの作成を手助ける
早速サンプルコードを見ましょう!
SqlWritter writter = new SqlWritter(); writter.select("*") .from("speech_data") .where("name") .like("新垣結衣 ニンゲン観察バラエティモニタリング"); System.out.println(writter);?コンソール出力
select
*
from
speech_data
where
name LIKE '%新垣結衣 ニンゲン観察バラエティモニタリング%'Q: ('ω') うわあああ!sql文を生成してくれたねといいたかったところ、どういうメリットがあるか?
A:確かにこれだけだと、メリットはあんまり実感できなさそうだね。しかし、以上の機能をコアとして、さらに便利な機能を提供できるんだって!sqlによる検索
生成したsqlを用いて、DAO不要のメリットを見てみよう。
List<SpeechData> entities; try (Accessor accessor = new Accessor()) { entities= accessor.selectBySql(writter, SpeechData.class); }上記のコードを簡単に説明してみると、上記はsql文による検索操作だ。
SpeechDataは事前作成したspeech_dataというテーブルのエンティティクラス
entitiesは結果を格納するためのリスト
Accessorはデータベースへアクセスするためのクラス
writterは先ほど生成したSQLQ:うんーーー、よくわからない!
A:大丈夫、覚えておいてほしいのはAccessorクラスはすべてのエンティティクラスに対応できるため、DAOはもう不要だ!下記のコードを追加して、実行結果を見てみよう。
for (SpeechData entity : entities) { System.out.println(String.format("ファイル:%sの分析結果:", entity.getName())); System.out.print(String.format("喜び:%s,", entity.getJoy())); System.out.print(String.format("悲しみ:%s,", entity.getSorrow())); System.out.print(String.format("怒り:%s,", entity.getAnger())); System.out.print(String.format("エネルギー:%s,", entity.getEnergy())); System.out.println(String.format("穏やかさ:%s", entity.getCalm())); System.out.println(); }?コンソール出力
ファイル:新垣結衣 ニンゲン観察バラエティモニタリング_00.wavの分析結果:
喜び:0,悲しみ:9,怒り:0,エネルギー:0,穏やかさ:40ファイル:新垣結衣 ニンゲン観察バラエティモニタリング_01.wavの分析結果:
喜び:18,悲しみ:2,怒り:0,エネルギー:16,穏やかさ:29ファイル:新垣結衣 ニンゲン観察バラエティモニタリング_02.wavの分析結果:
喜び:11,悲しみ:0,怒り:0,エネルギー:11,穏やかさ:38・
・
・Q:怪しいデータが出てきた!
A:ごめんごめん、音声による感情識別データだ。データの中身はとりあえず無視して、実行したsqlは無事に結果を戻しれくれた。エンティティによる検索
フレームのように、エンティティによる検索も可能!
SpeechData speechData = new SpeechData(); speechData.setName("新垣結衣 ニンゲン観察バラエティモニタリング_02.wav"); List<SpeechData> entities; try (Accessor accessor = new Accessor()) { entities = accessor.selectByEntity(speechData); } for (SpeechData entity : entities) { System.out.println(String.format("ファイル:%sの分析結果:", entity.getName())); System.out.print(String.format("喜び:%s,", entity.getJoy())); System.out.print(String.format("悲しみ:%s,", entity.getSorrow())); System.out.print(String.format("怒り:%s,", entity.getAnger())); System.out.print(String.format("エネルギー:%s,", entity.getEnergy())); System.out.println(String.format("穏やかさ:%s", entity.getCalm())); System.out.println(); }?コンソール出力
ファイル:新垣結衣 ニンゲン観察バラエティモニタリング_02.wavの分析結果:
喜び:11,悲しみ:0,怒り:0,エネルギー:11,穏やかさ:38Q:エンティティによる検索だね
A:はい、ライブラリだけど、機能は半端ない!3.ライブラリ共有
ライブラリを使ってみたい方は、下記のリンクからソースをダウンロードすることができる。
https://github.com/chaofanzheng/leadingeわからない時があれば、下記の仕様を参考すれば助けになる!!
4.ライブラリを使用するための仕様
システムプロパティ
system.properties#mysqlのユーザID(各自の設定に従う) USER_ID = root #mysqlのパスワード(各自の設定に従う) USER_PASSWORD = MySql #mysqlへアクセスのURL(各自の設定に従う) DB_URL = jdbc:mysql://localhost:3306/speech_recognition?useSSL=false&&allowPublicKeyRetrieval=trueエンティティ
SpeechData.java//CommonEntityを継承する必要がある public class SpeechData extends CommonEntity { //コンストラクタ public SpeechData() { //データタイプの初期化(テーブル情報に合わせて設定する) columnsType = new HashMap<String,Class<?>>(); columnsType.put("id", int.class); columnsType.put("name", String.class); columnsType.put("wav", byte[].class); columnsType.put("error", int.class); columnsType.put("calm", int.class); columnsType.put("anger", int.class); columnsType.put("joy", int.class); columnsType.put("sorrow", int.class); columnsType.put("energy", int.class); //テーブル名を設定する(スネークケース) setTableName("speech_data"); } public int getId() { return (int)columns.get("id"); } public void setId(int value) { columns.put("id",value); } public String getName() { return (String)columns.get("name"); } public void setName(String value) { columns.put("name",value); } public byte[] getWav() { return (byte[])columns.get("wav"); } public void setWav(byte[] value) { columns.put("wav",value); } public int getError() { return (int)columns.get("error"); } public void setError(int value) { columns.put("error",value); } public int getCalm() { return (int)columns.get("calm"); } public void setCalm(int value) { columns.put("calm",value); } public int getAnger() { return (int)columns.get("anger"); } public void setAnger(int value) { columns.put("anger",value); } public int getJoy() { return (int)columns.get("joy"); } public void setJoy(int value) { columns.put("joy",value); } public int getSorrow() { return (int)columns.get("sorrow"); } public void setSorrow(int value) { columns.put("sorrow",value); } public int getEnergy() { return (int)columns.get("energy"); } public void setEnergy(int value) { columns.put("energy",value); } }DBを操作するためには
Select.java//sqlを作成した場合は...もちろん、外部ファイルでも手書きでもオケ SqlWritter writter = new SqlWritter(); writter.select("*") .from("speech_data") .where("name") .like("新垣結衣 ニンゲン観察バラエティモニタリング"); List<SpeechData> entities; //DBアクセサの初期化 try (Accessor accessor = new Accessor()) { //sqlによる選択 entities = accessor.selectBySql(writter, SpeechData.class); //エンティティの初期化 SpeechData speechData = new SpeechData(); speechData.setName("新垣結衣 ニンゲン観察バラエティモニタリング_02.wav"); //エンティティによる選択 entities = accessor.selectByEntity(speechData); }Insert.java//sqlを作成した場合は...もちろん、外部ファイルでも手書きでもオケ SqlWritter writter = new SqlWritter(); writter.insertInto("speech_data") .colums("id","name") .values(999,"新垣結衣 ニンゲン観察バラエティモニタリング_999.wav"); //DBアクセサの初期化 try (Accessor accessor = new Accessor()) { //sqlによる選択操作 accessor.insertBySql(writter); //エンティティの初期化 SpeechData speechData = new SpeechData(); speechData.setId(999); speechData.setName("新垣結衣 ニンゲン観察バラエティモニタリング_999.wav"); //エンティティによるインサート操作 accessor.insertByEntity(speechData); }5.ソースコードについて
現状ではinsertとselectしかサポートしていないので、
更新や削除でもできるようにしたい場合は...各自がコードを改修してください。実際にDBへアクセスするときに使っていただくか、またはソースが参考になれば、
うれしいと思います。
- 投稿日:2019-04-13T11:55:12+09:00
eclipse上でStruts2のデモプロジェクトを作ります
プロジェクトの作成
まず、eclipseを開いてプロジェクトを作成しましょう。
ファイル -> 新規 -> その他と進んで「動的Webプロジェクト」を選択して「次へ」を押しましょう。
「プロジェクト名」にプロジェクト名(struts2_demo)を入力し、適当なTomcatサーバを選択して完了を押しましょう。
プロジェクトにStruts2を組み込み
Struts2に必要なライブラリをダウンロードしましょう。
はじめての方は”struts-バージョン-all.zip”となっているファイルをダウンロードしましょう。
ダウンロードしたStruts-2.5.20-all.zipを展開しstruts-2.5.20->libから次の13個を
- commons-fileupload-1.4.jar
- commons-io-2.6.jar
- commons-lang-2.4.jar
- commons-lang3-3.8.jar
- commons-logging-1.2.jar
- freemarker-2.3.28.jar
- javassist-3.20.0-GA.jar
- log4j-api-2.11.1.jar
- ognl-3.1.21.jar
- struts2-core-2.5.20.jar
- xmlpull-1.1.3.1.jar
- xpp3_min-1.1.4c.jar
- xstream-1.4.11.jar
プロジェクトの配下のWebContent->WEB-INF->libにコピーします。
eclipseのプロジェクトエクスプローラー(左の小窓)上の同じ場所のlibをリフレッシュして環境に反映しましょう。
にドラッグ&ドロップしましょう。
プロジュエクト名を右クリックして、Javaのビルド・パスから、クラスの作成先のパスを変更します。
フォーム画面(index.jsp)
プロジェクトエクスプローラー(左の小窓)のWebContentの上で右クリック
新規->ファイルを押してJSPファイルを作成しましょう。
下記の画面が表示されるのでFile nameに”index.jsp”と入力して”Finish”を押しましょう。
以下のようにフォーム画面(index.jsp)を記述しましょう。<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Hello Struts2!</title> </head> <body> <form action = "HelloStruts2"> <label for = "name">Please enter your name</label><br/> <input type = "text" name = "name"/> <input type = "submit" value = "OK"/> </form> </body> </html>結果画面(HelloStruts.jsp)
以下のように結果表示画面(HelloStruts.jsp)を記述しましょう。
<%@ taglib prefix="s" uri="/struts-tags" %> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Hello Struts2!</title> </head> <body> Hello Struts2, <s:property value="name"/> </body> </html>?「<%@ taglib prefix="s" uri="/struts-tags" %>」は、Jspのタグライブラリにページロードされています。
アクションファイルを作成
一見完成したように思えますがまだ未完成です。
なぜなら、フォーム画面から結果画面へ変数(name)を受け渡す必要があるからです。
そこで、変数の受け渡しを行うアクションファイル(HelloStruts2Action.java)を作成します。
HelloStruts2Action.javaをsrcの下にパッケージ名をhelloで作成します。
プロジェクトエクスプローラー(左の小窓)のJava Resources->srcで右クリックしHelloStruts2Action.javaを作ります。
以下のようにHelloStruts2Action.javaを記述しましょう。
package com.stuts2.demo; public class HelloStruts2Action { private String name; public String execute() throws Exception { return "success"; } public String getName() { return name; } public void setName(String name) { this.name = name; } }Struts.xmlの作成(Strutsのルール)
「フォームの処理をHelloStruts2Action.excecute()で行い戻り値が”success”だったら”HelloWorld.jsp”(結果画面)を表示する」
ルールを明記する必要があります。
それはstruts2_demo->src->struts.xmlに記述します。
struts.xmlを下記のように記述しましょう。<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="Demo" extends="struts-default"> <action name="HelloStruts2" class="com.stuts2.demo.HelloStruts2Action" method="execute"> <result name="success">/HelloStruts.jsp</result> </action> </package> </struts>package要素の属性には、以下の値が設定できます
要素 解説 name 複数のアクションをグループ可する際の名前。別のpakage名と重複しないようにする(必須) extends ![]()
別のpackageを継承する。継承することによりそのパッケージの要素にアクセスできるようになる(任意) namespace urlの一部になる(任意) abstract trueを設定すると、子要素のactionが必須項目でなくなる(任意) web.xmlの設定(URLのルーティング)
Strutsは、バージョンによってMVCフレームワークを開発環境に導入実現も違います。今回Struts2.5をロードしますので、
WebContent->WEB-INF->web.xmlに、以下のように内容を編集します。<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0"> <display-name>struts2_demo</display-name> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>TomcatにContextを配置
Tomcatサーバのconf/Catalina/localhostディレクトリの下に、struts2_demo.xmlを作ります。
struts2_demo.xmlの中身は以下とないrます。<Context path="/struts2_demo" docBase="/work/ws_eclipse/struts2_demo/WebContent" reloadable="false" source="org.eclipse.jst.jee.server:struts2_demo" />eclipseでstruts2アプリを起動
EclipseからTomcatを起動します。
起動するログには、以下のメッセージが確認できれば、struts2_demoが立ち上がりました。
画面で動かしましよう。
ブラウザーに、「http://localhost:8080/struts2_demo/index.jsp」を入力すると、以下の画面が表示します。
任意文言を書いてOKを押すと、次のページに遷移します。
- 投稿日:2019-04-13T11:23:46+09:00
spring bootをマルチプロジェクトでgradleしてみたかった
はじめに
eclipseでspring-bootでgradleでマルチなプロジェクトでプロジェクトフォルダを同階層に配置した構成でやってみようと思いました。なかなかうまくいかなかったのでメモ代わりに!
構成
fooをルートプロジェクトにしてbarを子プロジェクトとして↓のようにしてみました。
foo ← プロジェクトフォルダ build.gradle settings.gradle bar ← プロジェクトフォルダ build.gradle内容
foo
fooフォルダのgradleはいつものようにgradleを作って、settings.gradleの1行目に
includeFlat 'bar'を書き足しました。foo/build.gradleplugins { id 'org.springframework.boot' version '2.1.4.RELEASE' id 'java' } apply plugin: 'io.spring.dependency-management' group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' compile project(':bar') compileOnly 'org.projectlombok:lombok' }foo/settings.gradleincludeFlat 'bar' /* pluginManagement { repositories { gradlePluginPortal() } } */ rootProject.name = 'foo'bar
こっちはいろいろ試して最後に以下のようになりました。
pluginsにid 'org.springframework.boot' version '2.1.4.RELEASE'があるとうまく認識してくれませんでした。
apply plugin: 'io.spring.dependency-management'やapply plugin: 'org.springframework.boot'はなくても動くかもしれません。(試してませんw)bar/build.gradleplugins { id 'java' } apply plugin: 'io.spring.dependency-management' apply plugin: 'org.springframework.boot' group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' repositories { mavenCentral() } dependencies { compileOnly 'org.springframework.boot:spring-boot-starter-thymeleaf' compileOnly 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-security' compileOnly 'org.projectlombok:lombok' }おわり
とにかくこんな構成で動くところまでは確認しました。
久しぶりにgradle触ったら記述方法が変わってて軽いギャップを感じてしまいました。
- 投稿日:2019-04-13T00:41:46+09:00
Downgrade Java MacOS
After install Java check what versions do you have:
$ java --versionSomething like this will appear:
Matching Java Virtual Machines (2): 12, x86_64: "Java SE 12" /Library/Java/JavaVirtualMachines/jdk-12.jdk/Contents/Home 11.0.2, x86_64: "Java SE 11.0.2" /Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/HomeSelect the version you want:
/Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Homeopen your bash to edit:
$ sudo vi ~/.bash_profileand add this line:
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Homeupdate your bash:
source ~/.bash_profileconfirm:
$ java --version









