arcanum_jp’s blog

おっさんの日記

東北3姉妹、ずん子さん、きりたん、イタコ姉のコルクコースターを作りました。

 

f:id:arcanum_jp:20190308214711j:plain

 

先日参戦した、ずん子イベントで自分の島の横で売っていたのがコースターで、当日は何の話もしなかったのですが、とてもいい皮の財布やコースターなどを販売しており、自分も作ってみたいなぁと思い作成。今回は手軽にコルクでコースターをつくってみました。

 

arcanum.hatenablog.com

 

データは以前、光るずん子スタンドで作成したデータを流用。3姉妹で作成したかったため、イタコ姉のデータも追加で作った。

 

arcanum.hatenablog.com

 

光るずん子スタンドのデータは彫刻部分のデータで彫刻された場所が白く、他は透明(暗く感じる)なもので、こんな感じで写真のネガみたいなものです。これを直接皮やコルクに彫刻してしまうと残念な結果になるのでこのデータから反転したデータを作ります。

f:id:arcanum_jp:20190308213159p:plain

 

イラレで反転データの作り方知らないので上の絵に大きな矩形をかぶせて全体を切り抜いてチマチマ修正してました。それがこれ。慌てた部分があり、濃淡のディザや豆型弓の豆の影などがありませんね・・・あと装飾は市販のデータを使ってます。

 

f:id:arcanum_jp:20190308213428p:plain

同じようにきりたん。データは単純に色の反転だけではなく、細かい部分で調整しています。目とか。

f:id:arcanum_jp:20190308213627p:plain

今回、3姉妹で作成したかったので新たにイタコ姉のデータも作りました。これは公式の絵でイタコ姉の絵では自分が一番気に入っているものです。

 

f:id:arcanum_jp:20190308213849p:plain

特に目が苦労しました。まつ毛まではトレスですがお目目の瞳はもう自分で頑張って描きました。ちなみにトレスは簡単そうに思われますが、線の太さ、どこをどう線にすれば効果的に見えるかなど単純に色と色の分かれ目で出来ないので結構奥が深いです。

 

f:id:arcanum_jp:20190308214026p:plain

1回目、イタコ:パワー26%(施設のデフォルト)、できてみると、鼻に来るコルクの焼けた香り。レーザーカッターはこの焼けた匂いがなんか、作ってるなぁという感じがして好きですね。 

f:id:arcanum_jp:20190308214210j:plain

でも手で触ると彫刻がされ過ぎてますし、ちょっと焼けすぎですね。ここから綺麗に彫刻するための苦労をしていきます。具体的には彫刻のパワーを下げていき、最適な出力を見つけます。

 

f:id:arcanum_jp:20190308215101j:plain

 

 きりたん1回目。イタコ姉よりちょっと彫刻の出力を落としました。これも駄目です。まだまだ出力が大きすぎます。ちなみにこのきりたん、きりたん砲と左手がポイントだなぁと思いこんな構図にしてます。

f:id:arcanum_jp:20190308214220j:plain

 

ずん子1回目。きりたんより大幅に出力を落としてみました。あーーーーー微妙だ・・・色は良いけど線が出てない、、触るとまだ彫刻部分が段々になってしまってる・・・クッ!!

f:id:arcanum_jp:20190308214229j:plain

 

 

イタコ2回目:うん、、いいね。これ・・ぐら、、い、、もうちょっとかな?微妙だ、、これでもいいぐらいだ。次のきりたんが彫刻的にはグッドですが、彫刻の色の濃さではこちらの方がいいですね。。

f:id:arcanum_jp:20190308214241j:plain

 

きりたん2回目:うん。よい。これがグッド。触ってみて彫刻が分からないぐらいでなおかつ線も綺麗に出てる。

 

f:id:arcanum_jp:20190308214251j:plain

 

ずん子2回目。実は先のきりたん以上に出力を下げてやってみたのですが、もう絵がかすれたため途中でやめて、きりたんと同じ出力でやっています。

f:id:arcanum_jp:20190308214258j:plain

 

各キャラクターごとの1回目と2回目を並べてみます。こうやってみると2回目の方が線がよく出ています。

f:id:arcanum_jp:20190308220459j:plain

f:id:arcanum_jp:20190308220515j:plain

f:id:arcanum_jp:20190308220442j:plain

 

 今回カットしたものは以下で販売しております。ご興味があればどうぞ。

 

東北3姉妹コースターセット(テストカット成功版) - arcanum_jp - BOOTH

 

東北3姉妹コースターセット(テストカット失敗版) - arcanum_jp - BOOTH

  ↑

嫁ぎ先が見つかりました。ありがとうございます。

めかぶの刻みもの作ったよ

この季節に美味しい楽しみの一つがめかぶです。自分はワカメや昆布、めかぶなどの海藻類が好きなのでこの季節にメカブが売ってると買ってきてしまいます。

 

こんな茶色いのメカブなわけないだろ!メカブってあれだろ?緑でネバネバした、、、どう見たってエイリアンのなんかビュビューって飛び出すアレだろ?そう言うのが聞こえてきそうです。

 

f:id:arcanum_jp:20190216193942j:plain

 

調理方法は色々方法があるとおもいますが、自分の場合はある程度刻んでしまいます。湯がいた後に切ろうとしても切るの大変なので。

 

f:id:arcanum_jp:20190216194741j:plain

 

ちなみにこのめかぶ、店の方で茎をあらかじめ切っていたようです。ナイス!イトーチェーン!まぁ茎は茎でうまいのだが。

 

f:id:arcanum_jp:20190216194035j:plain

これを沸騰したお湯に入れて、ほんの数十秒待つと色が茶色から鮮やかな緑色になります。

 


メカブ

 

あとは水で洗ってもう一回気のすむまで刻んで出来上がりです。僕はどちらかと言うと、細かく刻むよりある程度大きなカケラがある方が好きなのでこの程度にしておきます。これに醤油なりめんつゆなり、かけて食べます。僕はこれに生姜をおろしたものをかけるのが好きです。

 

f:id:arcanum_jp:20190216195423j:plain

 

では明日の朝の楽しみができました。

 

管理ゼロで成果はあがる、を読んだ


話題になっていたので。私はもう会社員じゃないため、こんなの読んでも部下もいなければ上司もいないしな、実践しようにないという気持ちも無きにしも非ずでしたが、いや読みたいものを読むのが読書というもんだ!といてもたってもいられなく購入。決済も自分で行うなど、管理されない働き方、自由すぎんぞと思ったけど。

はじめ、いろいろなサイトであった感想から、管理しなくて良い人を育てるのか、結局は組織行動学あたりの評価と従業員の行動の強化(意識づけ)をどうするか?って話だと仮定して読みました。

多分著者の言いたいことの一つは、はじめに、でこのように書いている。多分ここだろう。

p6

信頼関係さえしっかりと築くことができれば、管理などしなくてもだれもが管理したとき以上に責任感を持ってきちんと仕事に取り組むし、個々の主体性も増しました。指示されて働くよりも、自分ごとにして圧倒的に楽しく働いてくれるようになりました。そして、社員が自律的に楽しく働くだけで、お客様からの評判は高くなり、新規事業が勝手に生み出され、創業した私が想像した以上の成果を出すことになったのです

経営者、従業員ともお互いに信頼していれば管理なんかする必要なんてないのである。至極あたりまえというか、そのために両者は何をすれば良いのか?というのがこの本で語られている。両者のすべきことは、従業員は自律的に働くこと、それが管理が不要な人材の最低限な条件であり、経営者から見れば従業員が働くための場を用意する。ソニックガーデンの場合は一貫性、公平性、透明性を確保する。また、従業員が働く場としてリモートワークを提供したりであったりと。お互い信頼しあって同じベクトルを共有する。


よく経営者は365日働いても疲れない、それは管理されず自己決定権があるあらだと言われます。その通りで管理されずに自分の決定で仕事をできれば仕事は楽しいだろう。もう趣味に近い。だれも管理なんかされたくないのである。でも自己決定権を持って管理されずに働くってどんなことだろう。


それが著書では「セルフマネジメント」ができるということで表現しています。本書ではセルフマネジメントのレベルを3段階に分けていますが、最終的には

  • 仕事では価値を生み出す
  • 組織としては周囲を生かす
  • 自分自身の将来を考える

という状態に各個人がなれば管理などする必要はないと言います。まるで社内の人材全員が個人事業主みたいなもんですね。そのセルフマネジメントを行えるように個人ができることは1章に具体的にソニックガーデンで行われていることが書いてあります。KPTで振り返りであったり、仕事の小口化であったりと。それらは一つの目的「生産性」のためにあります。この生産性がまず基本なわけですね。生産性なくして管理なしな自由などあり得ないわけですね。


しかしそれらも仕事の進め方などの本を読めば書いてあるだれにでも出来る一般的なことです。別にソニックガーデンが秘伝のタレ的に秘密なものでもありません。でもその当たり前のことを当たり前に続けるってのが難しいのだろうね。そして個人が当たり前になるようにそれらができる環境を会社として用意するのも世の中で色々と答えはありながら実践するのが難しいってことだろう。


あたりまえだが管理しないが目的ではなくて、この会社が試行錯誤の中でやってきた成果が、管理しないだったってことだろう。会社は規模や中の人など多種多様なものがあり当たり前だが管理をしないだけが正解ではない。その会社でみな幸せに仕事をできるよう、どんな方法がよいか絶えず改善して実行していくってことだろうなと。

SpringBoot 例外処理についてのメモ

SpringBootでの例外処理について学んだのでメモ

基本

コントローラー内にて、メソッドに@ExceptionHandlerを指定すると例外が発生するとそのメソッドが呼び出される。下記で実行すると、http://localhost/expで例外が発生しますが、例外が発生するとexception( )が呼び出される。

ハンドラ内でリクエストやレスポンス、送出された例外にアクセスしたい場合はメソッド引数に定義すれば使える。便利すぎんぞ!!

コントローラーには下のケースでは@RestControllerを指定しているため、ハンドラではAPIの戻りとしてJSONなりを返せばいい。サンプルでは単純に文字列を返している。

コントローラーに@Controllerを指定している場合は、エラーページへのパスを返せばいいそうだ

@RestController
public class HelloController {

	@RequestMapping(value="exp", method=RequestMethod.GET)
	public String doException(HttpServletResponse resp) throws Exception {
		throw new Exception("HELLO Exception!!");
	}

        ... 他の処理

	@ExceptionHandler(Exception.class)
	public String exception(Exception e) {
		return "Exception!! " + e.toString();
	}

}

例外ごとのハンドラ

上記ではExceptionで補足してしまったので、非検査例外やら他のException配下を個別にハンドルしたい時はどうするか?と思い、各例外ごとにハンドラを作ってみた。

@RestController
public class HelloController {

        ...さっきのコード

	@RequestMapping(value="fio", method=RequestMethod.GET)
	public String doIOException(HttpServletResponse resp) throws Exception {
		throw new FileNotFoundException("HELLO FileNotFoundException!!");
	}

	@RequestMapping(value="nullpo", method=RequestMethod.GET)
	public String doNullPointerException(HttpServletResponse resp) {
		throw new NullPointerException("HELLO NullPointerException!!");
	}
	
	@RequestMapping(value="illegalArg", method=RequestMethod.GET)
	public String doIllegalArgumentException(HttpServletResponse resp) {
		throw new IllegalArgumentException("args error!!");
	}

	
	@ExceptionHandler(IOException.class)
	public String fioException(Exception e) {
		return "FileNotFoundException!! " + e.toString();
	}
	
	@ExceptionHandler(NullPointerException.class)
	public String nullPointerException(Exception e) {
		return "NullPointerException!! " + e.toString();
	}
	
	

結果としては、個別に補足してくれる。SpringBootはリクエストでマッピングされたメソッド内で例外が発生した場合、同一コントローラー内にハンドラが定義されていたら定義に従い通知する。ということ。まぁ至極あたりまえの感覚か・・

コントローラ以外にハンドラ定義

先ほど、「コントローラー内にて、メソッドに@ExceptionHandlerを指定する」と書いたが、じゃぁコントローラー以外にハンドラを定義した時はどうなのだろうと、上記のクラスに次のように追加およびコントローラーじゃないクラスにハンドラを定義してみた

@RestController
public class HelloController {
       ... さっきのコード

	@RequestMapping(value="illegalArg", method=RequestMethod.GET)
	public String doIllegalArgumentException(HttpServletResponse resp) {
		throw new IllegalArgumentException("args error!!");
	}

}


public class NotController {	
	@ExceptionHandler(IllegalArgumentException.class)
	public String notController() {
		return "IllegalArgumentException";
	}
	
}

予想では/illegalArgでアクセスした時、IllegalArgumentExceptionが発生するがハンドラが見当たらないのでexception( )が呼び出される。結果としてはコントローラじゃないクラスは無視。まぁ、当たり前的なことを確認した。アノテーションされていないクラスは知らんと。

全体で使えるハンドラ定義

じゃぁプログラミングするときにコントローラーごとに例外のハンドラを定義していけばいいのか?していくのか?って疑問が、、そりゃ大変そうだ、、収拾がつかなくなりそう。そのときは全体で共通のハンドラを定義が可能

全体のハンドラ用クラスを定義して、そのクラスに@ControllerAdviceまたは@RestControllerAdviceをつけ、その中でハンドラメソッドを定義する。下のサンプルではJSONで返すようにしている。

@RestControllerAdvice
public class ExpHandler {

	@ExceptionHandler(Exception.class)
	public ExpBean exceptionHandle(Exception e, HttpServletResponse resp) {
		resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		return new ExpBean(e, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
	}
	
}

この場合の疑問として、全体のハンドラとコントローラ内で定義されたローカルなハンドラはどちらが優先されるだろうって。。以下のコントローラーを定義してみた。

@RestController
public class HelloController2 {
	
	@RequestMapping(value="/exp2", method=RequestMethod.GET)
	@Transactional
	public String index() {
		throw new RuntimeException("他のコントロラーで例外");
	}
	
}


先ほどまでのサンプルを合わせて実行してみると、/expではコントローラー内で定義されたローカルなハンドラ、/exp2では、コントローラー内ではハンドラの定義がないので、共通で定義されたハンドラが呼び出されたのを確認。

うん、便 利 す ぎ !


なんでこんなのもっと早く覚えなかったのだろうかと

杜のコミフェス2で販売をしてきました。

 

moricomi.info

 

 

ずん子さんの15パズルも完成間近で、こんなつぶやきを

 

 

ここから始まりました。いままで作ってきたアクリル。満足したりしなかったり、そのたびに家のお蔵入りになっていました。カミさんや子供らからは無駄遣い!って言われでも作りたい欲求は抑えられず、、、でも見てくれる人もいない・・・じゃぁ展示目的でいいから出てみるか、、、

 

ただ、参加費ぐらいは回収しないとなんだか悲しいなということで、きりたんクリップ3点セットをレーザーで新規に作って持って行きました。あとはそれまで作った、きりたんのアクリルキーホルダーなどです。まぁこれが売れれば参加費ぐらいになるかね?的な。

 

 

f:id:arcanum_jp:20190203211741j:plain

 

前日からこんな感じでゴソゴソっと作品たちをかき集め、あぁ、これはあの時苦労したなぁとか、あぁ、、ここにあったんだ、、とか、色々思いはあったけど、

 

 

f:id:arcanum_jp:20190203211804j:plain

 

よくデータで作る円状のものです。きりたんのこのポーズはこのように円で囲むととてもいいです。

f:id:arcanum_jp:20190203211905j:plain

 

f:id:arcanum_jp:20190203211958j:plain

 

ドアップきりたん。こういうのいいなぁって作ったの。写真右側にあるポッチは傷。UVプリンタのインクがちょっとはげ落ちてしまったもの。

f:id:arcanum_jp:20190203212045j:plain

 

f:id:arcanum_jp:20190203212222j:plain

 

f:id:arcanum_jp:20190203212409j:plain

 

f:id:arcanum_jp:20190203212424j:plain

 

f:id:arcanum_jp:20190203212442j:plain

 

正直、これを見て欲しくて今回参加したもの。

f:id:arcanum_jp:20190203212250j:plain

 

f:id:arcanum_jp:20190203212504j:plain

 

f:id:arcanum_jp:20190203212514j:plain

 

f:id:arcanum_jp:20190203214854j:plain

このほかにもいくつか持っていったりしてました。

 

参加して感じたことなど

 

当日撮った写真はこれしかありませんでしたが、もうこの時点でガクブルなヘタレでした。本当は会場を撮ってプライバシー処理した上でこのブログに載せたかったのですが。ちなみにこの時点、始まる直前でEビーンズ2階から9階までお客さんの列ができていたそうです、高鳴る心臓!

 

 横のサークルさんは左が皮製品で右がステッカーのサークルさんで、始まってすぐ、売れていくのをみるに、だんだん、「お、、俺はこのスタンド見て欲しくて参加したんだからね、そうだよね?ぼく負けないもん」的な寂しい気持ちになっていきました。あぁ、コミケとかに参戦した人たちが売れなくて気持ちが塞がっていくって聞いていたけど、こんな感じなんだろうなぁって。そのうちだんだん嫁ぎ先が見つかったりしたのですが。

 

買ってくださったお客さんで印象に残ったのは、UVプリンタの話から始まり、3Dプリンタなどで作ってますといったり、実際に仕事でレーザー使ってますといったお客様や、レジンについてちょっとお客様に教えてもらったりと、ものづくりの楽しさの話が机を隔ててやりとりしたりと面白かったです。

 

横にいたサークルさんと話していたのですが、会社の同僚が実はずん子さんファンだったり、親戚がコスプレしてたりというのが発覚したりと聞いてていいなぁと思ったのですが、それよりいいなぁと思ったのは、その同僚や親戚の方の子供がずん子さんのコスプレとかしていて、楽しそうなのですよ。あれウチの娘に着てと言っても絶対やんない。これさ、親が好きなことを子供の前で楽しんでるってのは本当に良い影響を与えるんだろうなぁって。

 

あと、原価の話になって、今回自分が持っていったものは原価でいうと、展示を目的としているため、殆どギリギリ的な感じ。一部に関してはもう赤字です。この「原価」も難しいですよねぇと話してました。作る際の試作品や失敗品なんかどう考えたらいいの?とか自分の場合は施設利用料もあるし、

 

んでそのサークルさん、はじめは作品に対し、xx円を予定してたけど、知り合いから「次の作品作るお金どうすんのよ?私だったらそれxx(2倍以上)の金額なら買うわよ?」とか言われてといったような話をされていました。いやぁ、そういう話ができなくて、わかってくれる人がいなくてほんといいサークルさんと当たったなぁと過ごしてました。

 

あと、当日こんな感じで自分の紹介カード?チラシを持って行きました。A5程度の紙を25枚程度です。失敗したなぁと感じるのは、これは100枚も持っていけ!ということでした。25枚じゃ到底足りない。あとA4で渾身の一枚を描けと。これ、流れて見てくださる方がいた時に、どうぞと渡して話ができるのですよ。

f:id:arcanum_jp:20190203213733p:plain

 

色々な話を聞けたり、買ってくださる方もいたりと自分にはほんと大変な1日でしたが、ほんと有意義な1日でもありました。

 

SpringBoot勉強、JdbcTemplateについて

Springでデータベースを使う場合はJdbcTemplateというものが用意されている。当然SpringBoot上からもJdbcTemplateは使える。アプリを作る際に気になるのは、コネクションはどうやってとるのとか、コネクションのコミット、ロールバック、クローズ方法やトランザクションなどだけど以下を確認した。以下自分の備忘録的なログなので別に実行ログとかは添付しない。ご参考程度に

以下まとめ

  • JdbcTemplateを使う限り、JDBCのコネクションを意識する必要はない
  • autoCommitはデフォルトでtrueがセットされている
  • コネクションプーリングはHIKARI-CPが推奨
  • autoCommitをfalseにするにはHIKARI-CPの設定
  • トランザクションはデフォルトでOFFでJdbcTemplateを使用するメソッドまたはクラスに@Transactional宣言をする
  • @Transactionalをつけたメソッドの開始から終了までがトランザクションとなる
  • 非検査例外が発生すると自動ロールバック
  • 無事メソッドが終了するとコミットされる(余計なことを)
  • @Transactionalを付加するとautoCommitはfalse扱い

以下に確認して行った事をソースベースで確認。

pom.xmlJDBCの利用とJdbcTemplateの利用を宣言(こういう表現でいいのか?)以下ではPostgreSQLを利用。上の依存がJDBC、下の依存はポスグレの使用。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
</dependency>

JdbcTemplateの基本的な使い方

まず、JdbcTemplateの基本的な利用方法?JdbcTemplateインスタンス自身は@AutoWiredでインジェクション。各メソッドでJdbcTemplateインスタンス経由でDBにあアクセス。こんな感じのクラスを作成。コードが古いJavaとか言わない!そこの君!!

@Configuration
public class DBAccess {

	@Autowired
	private JdbcTemplate _jdbcTemplate;
	
	public int insert(String table, String[] values) {

		Log log = LogFactory.getLog(getClass());
		String sql = "insert into " + table + " values(";
		for (int i = 0 ; i < values.length; i++) {
			sql = sql + " ?,";
		}
		sql = sql.substring(0, sql.length() - 1);
		sql = sql + ")";
		log.debug("SQL: " + sql);
		return _jdbcTemplate.update(sql, values);
	}
	
	public List<Map<String, Object>> selectAll(String table) {
		String sql = "select * from " + table;
		return _jdbcTemplate.queryForList(sql);
	}
	
}

利用するクラスはこんな感じに

@RestController
public class HelloController {

	@Autowired
	DBAccess _dbaccess;
	
	@RequestMapping(value="/", method=RequestMethod.GET)
	@Transactional
	public ModelAndView index(ModelAndView mav, HttpServletRequest req, HttpServletResponse resp) {

		Log log = LogFactory.getLog(getClass());

		List<Map<String, Object>> citylist = _dbaccess.selectAll("city_tbl");
		for (Map<String, Object> record : citylist) {
			log.info("sort_key: " + record.get("sort_key") + "  --  name: " + record.get("name"));
		}
		_dbaccess.insert("city_tbl", new String[] {"004", "test1"});
		
	}

一応これでSELECTとINSERTができたのは確認できた。JdbcTemplateが使えたことを確認。INSERTなどの更新系のSQLを実行すると自動コミットされる。自動コミットされたのは、Connection#setAutoCommit()がtrueなため。

コネクションプーリングおよび、オートコミットの無効化

データベースを利用する場合、気になるのはコネクションのプーリング。SpringBootではHIKARI-CPというプーリング機構が推奨されているらしい。


docs.spring.io

  1. We prefer HikariCP for its performance and concurrency. If HikariCP is available, we always choose it.
  2. Otherwise, if the Tomcat pooling DataSource is available, we use it.
  3. If neither HikariCP nor the Tomcat pooling datasource are available and if Commons DBCP2 is available, we use it.

また上のリンクに書いている通り、pom.xmlでspring-boot-starter-jdbcを指定していればこのHIKARI-CPのライブラリ一式の依存も解決してくれるみたい。あとはapplication.propertiesにその設定を書いていく。あと、設定最後にあるauto-commitの指定でオートコミットの無効化ができる。しかしオートコミットってプーリングに関するところで設定すんのが普通なんかな?という疑問は浮かぶ。

spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.pool-name=ConnectionPool
spring.datasource.hikari.leakDetectionThreshold=5000
spring.datasource.hikari.connection-test-query=SELECT 1
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.auto-commit=false

application.propertiesの設定については以下のリンクが本家

https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html


トランザクション

JDBCを使っている場合だと、トランザクションを意識する方法としてリクエスト中Connectionを引き回したりスレッドローカルでリクエスト中、同一のコネクションを使うようにするであったり、そういった仕組みを考える必要がありますが、SpringBoot自身ですでにトランザクションを用意しているみたい。

トランザクションについてはクラスメソッドの都元ダイスケさんという方が書いたこちらの記事が詳しいですが、メソッドに@Transactionalをつけるとそのメソッドの開始から終了までが1トランザクションとなり、終了時に非検査例外が発生していなければ自動的にコミットされます。不思議ですね〜。正直余計なことすんな!って思うのは古い人間なんでしょうか。

dev.classmethod.jp


先ほど作ったクラスに@Transactionalを入れます。

@RestController
public class HelloController {

	@Autowired
	DBAccess _dbaccess;
	
	@RequestMapping(value="/", method=RequestMethod.GET)
	@Transactional                           <-----------------------------------------これを入れる
	public ModelAndView index(ModelAndView mav, HttpServletRequest req, HttpServletResponse resp) {

		Log log = LogFactory.getLog(getClass());
  。。。省略


このトランザクションの種類も色々とあるみたいで、こちらのエントリが非常にわかりやすかったです。
pppurple.hatenablog.com



ここで気になるのは異なるJdbcTemplateオブジェクトを介してSQLを発行した際にどうなるのか?トランザクションは一緒になるのか?ということで、こんな実験を。まず、異なるJdbcTemplateインスタンスになるであろうDBAccess2クラスを作る

@Configuration
public class DBAccess2 {
	
	@Autowired
	private JdbcTemplate _jdbcTemplate;
	
	public int insert(String table, String[] values) {
		Log log = LogFactory.getLog(getClass());
		String sql = "insert into " + table + " values(";
		for (int i = 0 ; i < values.length; i++) {
			sql = sql + " ?,";
		}
		sql = sql.substring(0, sql.length() - 1);
		sql = sql + ")";
		log.debug("SQL: " + sql);
		return _jdbcTemplate.update(sql, values);
	}

}

んで、コントローラでトランザクションを開始し、内部で異なるJdbcTemplateインスタンス経由でSQLを発行します。

	@RequestMapping(value="/", method=RequestMethod.GET)
	@Transactional
	public ModelAndView index(ModelAndView mav, HttpServletRequest req, HttpServletResponse resp) {

		Log log = LogFactory.getLog(getClass());

		// トランザクションのテスト
		log.info("dbaccess  : " + _dbaccess);
		log.info("dbaccess2 : " + _dbaccess2);
		_dbaccess.insert("city_tbl", new String[] {"004", "test1"});
		_dbaccess2.insert("city_tbl", new String[] {"005", "test2"});
		
		mav.setViewName("index");
		mav.addObject("msg", "input your name :");
		return mav;
	}

実は_dbaccessと_dbaccess2が持つJdbcTemplateインスタンスは同一オブジェクトでした。ログでオブジェクトの持つハッシュコードで確認。不思議なのはコントローラから@Transactionalを取っても同一オブジェクトみたいで、不思議〜。


次に、この状態で一つ目のINSERTの後、非検査例外が起きた場合です。

		_dbaccess.insert("city_tbl", new String[] {"004", "test1"});
		if(true)throw new RuntimeException("test!!!!");
		_dbaccess2.insert("city_tbl", new String[] {"005", "test2"});

こちらの場合は一つ目のSQLは成功しているのにも関わらず、コミットはされていません。

次にこの状態で、spring.datasource.hikari.auto-commit=trueにしてみます。オートコミットがONなら予想では一つ目のSQLはコミットされるはずです。しかし実行してみるとわかりますがいずれのSQLもコミットされません。もしかすると@Transactionalを付加した時点でspring.datasource.hikari.auto-commit=false扱いになるのかもしれません。そこで、メソッドの@Transactionalを外して実行してみます。

実行すると1つ目のSQLは無事?コミットされました、二つ目は実行前に例外が発生しているのでコミットされないのは当たり前ですね。ということでオートコミットとか考える必要もなく、トランザクションを使いたい場合は@Transactionalを使えば良いということがわかりました。便利すぎますね

SprinBootのapplication.propertiesメモ

SpringBootアプリケーションの設定ファイルで、/resources直下に置いておく。起動時にこれが読み込まれる。設定できる一覧はこちらで見れるらしい。こちらは従来からあるプロパティだけど、ymlでも書ける模様だが自分はymlが慣れていないのでここではプロパティファイルについてメモ

docs.spring.io

環境の切り替え

よくWebアプリケーションを作成している時に必ず作る仕組みが、開発環境、ステージング、本番環境とで環境依存の情報をプロパティファイルに外だしにして、各環境で読み込むファイルを切り替える、war作成時に読み込むファイルを変えるなど。それについてもapplication.propertiesでは可能。

プロファイルと言う仕組みらしい。例えば各環境ごとのキーを以下のように決めておく。これはプロジェクトによって自由に決められるみたい。

  • 開発環境:dev
  • ステージング:staging
  • 本番環境:release

それで、読み込むapplication.propertiesを次のように作り、resourcesの直下に配置する

/resources
    + application-dev.properties
    + application-staging.properties
    + application-release.properties


起動するときに環境を指定することにより起動した時に各環境ごとのapplication.propertiesが読み込まれる。起動方法は次の通り。

コマンドライン引数

java -jar spring-boot-application-demo.jar --spring.profiles.active=release

VMの起動引数

java -jar -Dspring.profiles.active=dev spring-boot-application-demo.jar

環境変数にセット

SPRING_PROFILES_ACTIVEというキーで環境変数を設定しておく

プログラム中からのapplication.properties参照方法

application.properties参照用のクラスを定義します。@PropertySourceのclasspathで読み込むプロパティファイルを指定するようです。resourcesからの相対パスで指定します。

@Configuration
@PropertySource("classpath:application.properties")
public class MyProperties {
	@Autowired
	private Environment _env;
	
	public String getValue(String key) {
		return _env.getProperty(key);
	}
	
}


これを使用するクラス側で使います

@RestController
public class HelloController {

	@Autowired
	MyProperties _prop;
	
	@RequestMapping(value="/", method=RequestMethod.GET)
	public ModelAndView index(ModelAndView mav, HttpServletRequest req, HttpServletResponse resp) {

		Log log = LogFactory.getLog(getClass());
		
		log.debug("hoge.fuga               : " + _prop.getValue("hoge.fuga"));
		log.debug("spring.application.name : " + _prop.getValue("spring.application.name"));
               ...
		
	}


まだあんまりDIの事がわかってないのですが、インスタンスインスタンス変数でインジェクトしてもらう必要があるって事なんでしょうかね。個人的にはシングルトンにしてこんな風に使いたいんですが・・・

MyProperties p = MyProperties.getInstance();
String hoge = p.getValue("hoge.fuga");

似たような方法は以下で見つけましたが、、なんだか面倒なうえあんまりかっこよくは無いですね・・・という事で諦めます。

teratail.com


先の一覧以外の、独自のキーも取得できるようです。上記だとapplications.propertiesにhoge.fuga=piyopiypと書いておくと”piyopiyo”が取得できます。この辺は普通のプロパティファイルと考えればいいみたいです。個人的には取得するキーに対するシンタックスシュガーメソッドが定義したいですね。プログラマに"spring.application.name"とかリテラルでも定数でも指定させんのは嫌なので。