親バカエンジニアのナレッジ帳

webのエンジニアをやっており、日頃の開発で詰まったことや書き残しておきたいことを載せています。育児のイロハという育児サイト(https://ikujip.jp)の開発も行っているため、その開発で使用されている技術についても掲載しています。

Spring Data JPAでパラメータ以外でコロンを使いたい

RepositoryにSQLを直接記入

Spring Data JPAでORマッパーでは書けないようなSQLを実行したい場合、Repositoryに直接構文を書きますよね。
そんな時、パラメータで変数の値を渡したい時はコロンで変数を指定しますよね。
例えば以下のidのように

@Query(value = "SELECT * FROM test_tables WHERE id = :id ", nativeQuery = true)
List<Test> findById(@Param("id") Integer id);

パラメータ以外でコロンを使いたい

パラメータで変数を渡したい場合は普通にコロンをつければいいですが、それ以外の用途でコロンをつけたい場合はそうはいきません。
例えば以下のようなSQLをそのまま書くと、以下のようなエラーが発生します。
プログラムの中でこのようなSQLを実行する人はいないと思いますが笑、1から50まで表示されるSQLです。

SELECT 0 generate_series FROM DUAL WHERE (@num:= 1 - 1) * 0 UNION ALL
SELECT @num:= @num + 1 FROM information_schema.COLUMNS LIMIT 50

Repositoryに入れるとこんな感じですね。

@Query(value = "SELECT 0 generate_series FROM DUAL WHERE (@num:= 1 - 1) * 0 UNION ALL " +
"SELECT @num:= @num + 1 FROM information_schema.COLUMNS LIMIT 50", nativeQuery = true)
List<Integer> getNumber50();


エラー内容は以下です。

Space is not allowed after parameter prefix ‘:'

解決方法はエスケープ

上記エラーの解決方法としては、コロンの前にエスケープのスラッシュを入れることです。
ただし1つ入れるだけではダメで、以下のように2つのスラッシュを入れましょう。

@Query(value = "SELECT 0 generate_series FROM DUAL WHERE (@num\\:= 1 - 1) * 0 UNION ALL " +
"SELECT @num\\:= @num + 1 FROM information_schema.COLUMNS LIMIT 50", nativeQuery = true)
List<Integer> getNumber50();

Fire TV Stick

Fire TV Stick

新登場  Fire TV Stick 4K - Alexa対応音声認識リモコン付属

新登場 Fire TV Stick 4K - Alexa対応音声認識リモコン付属

Fire HD 10 タブレット (10インチHDディスプレイ) 64GB

Fire HD 10 タブレット (10インチHDディスプレイ) 64GB

Spring Data JPA のfind+OrderByで、No property desc foundエラー時の対処法

Spring Data JPAでのOrderBy

Spring Data JPAでエンティティクラスにfindして複数のレコードを取得する場合、OrderByをつければ並び順を変更できることはご存知でしょうか?

findByNameOrderById(Spring name)

などとすれば、nameで絞り込んだ上で、idの降順で並び替えができます。

これを見る限り、後ろにOrderByを入れればいいんだな、と思ってしまいがちですが、以下のような書き方だとエラーが発生してしまいます。

findAllOrderById()

これだとコンパイルのタイミングで、

No property desc found for type Integer! Traversed path

のようなエラーメッセージが出力されるのです。

whereを指定しない場合、OrderByの前にByが必要

上記のfindAllの例のように、where句の絞り込みをしない場合、OrderByの前にもByが必要になります。

よって、findAllの場合は以下のようにしなければいけません。

findAllByOrderById()

SQLの書き方に慣れていると、order byの前にbyを付けているようで少し気持ち悪いですよね。

今回はfindAllの例でしたが、この書き方はfindFirstやfindTopの場合なども同様です。

findFirst1ByOrderById()
findTopByOrderById()

今まで条件を指定しないとOrderByが使えないのでは?と思われていた方がいたら、ぜひ参考にしてください。

Fire TV Stick

Fire TV Stick

新登場  Fire TV Stick 4K - Alexa対応音声認識リモコン付属

新登場 Fire TV Stick 4K - Alexa対応音声認識リモコン付属

Fire HD 10 タブレット (10インチHDディスプレイ) 64GB

Fire HD 10 タブレット (10インチHDディスプレイ) 64GB

Vue.jsのv-ifやv-forで無駄にタグを増やしたくない時はtemplateで代用するのが便利

Vue.jsでの分岐やループ処理

Vue.jsでは分岐やループ処理で、動的にタグを描画したい場面がありますよね。
例えばリストを動的に増やしたい時は、liタグにv-forを付与したり、ある文言が特定の条件を満たした時のみ表示させる場合はpタグにv-ifを付けたりなど…。

例)

<ul>
  <li v-for="test in testList"></li>
</ul>

<p v-if="showMessage === true"></p>

それほど複雑でなければ困らないのですが、条件が複雑になったり、ループの階層が増える場合、v-forやv-if用に無駄にタグを増やしてしまうことはありませんか?

そんな時、無駄に階層を増やさなくてもできる方法があるのです。

templateタグを使う

タイトルにもある通りですが、templateタグを使えば万事解決です。

例えば以下のような使い方をした場合、実際に表示されるタグを見てみましょう。
(下記のような場合はspanタグにv-ifを入れてしまえばいいと思いますが、例としてtemplateタグを使っています。)

入力内容

<template v-if="showMessage === true">
  <span>メッセージ</span>
</template>

実際の表示

<span>メッセージ</span>

わかりますか?
templateタグは表示されませんよね。
MVCモデルで、サーバサイドからレンダリングした値をビューに表示させる時のような使い方ができるのです。

よって以下のように複数の階層のループ処理があっても、無駄にタグを増やす必要がないのです。

入力内容

<ul>
  <template v-for="test in testList">
    <li v-for="sub in test.subList">
      <span>{{ sub.message }}</span>
    </li>
  </template>
</ul>

実際の表示

<ul>
  <li>
    <span>メッセージが表示されます</span>
  </li>
</ul>

とても便利ですよね。
無駄にタグを増やさないようにぜひ使ってみてください。

Fire TV Stick

Fire TV Stick

新登場  Fire TV Stick 4K - Alexa対応音声認識リモコン付属

新登場 Fire TV Stick 4K - Alexa対応音声認識リモコン付属

Fire HD 10 タブレット (10インチHDディスプレイ) 64GB

Fire HD 10 タブレット (10インチHDディスプレイ) 64GB

Spring Bootで実行SQLのログを取得する方法

アプリケーション開発でのSQLログ

アプリケーションの開発をしている時、実行されたSQLのログを参照したい場面がありますよね。
想定外のSQLが実行されていないか確認したり、ボトルネックとなっているSQLを確認したりなど。
Spring BootではそのようなSQLの実行ログを簡単に見ることができます。

今回の方法は、Spring Data JPAでデータベースへのアクセスをするものとします。

application.ymlでの設定

手順としては、application.ymlに以下を書き込むだけです。

logging:
  level:
    org:
      hibernate:
        SQL: DEBUG
        type:
          descriptor:
            sql:
              BasicBinder: TRACE

Spring Data JPAは内部的にhibernateで実装されているために、hibernateのログ設定になるんですね。
あとは通常通りサーバを起動させればOKです。
実際に処理を動かしてみると、以下のようなログが出力されることでしょう。

2018-10-16 17:59:53.321 DEBUG 14383 --- [nio-8080-exec-2] org.hibernate.SQL                        : SELECT ...
2018-10-16 17:59:53.321 TRACE 14383 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder      : binding parameter [3] as [INTEGER] - [1]

とても簡単ですよね。
O/Rマッパーでのオブジェクトへのアクセスで、実際にどのようなクエリが実行されているのかを確認したい場合などに設定してみてください。

Fire TV Stick

Fire TV Stick

新登場  Fire TV Stick 4K - Alexa対応音声認識リモコン付属

新登場 Fire TV Stick 4K - Alexa対応音声認識リモコン付属

Fire HD 10 タブレット (10インチHDディスプレイ) 64GB

Fire HD 10 タブレット (10インチHDディスプレイ) 64GB

Vue.jsでURLの#(シャープ)を取り除く方法

URLの#(シャープ)

Vue-cliでvue.jsのセットアップを行った時、開発環境のURLを叩くと後ろに#(シャープ)が付いてしまいます。

http://localhost:8080と入力しても、http://localhost:8080/#/
となってしまいます。

これはhashモードという状態で付くものであり、これはこれでメリットがあるのですが(ここでは詳細は省きます)、このような状態で公開するのは格好悪いですよね。

historyモードにすることで解決

上記の#(シャープ)は、routerの設定でhistoryモードにすることで解決します。

例として、変更前のrouterの設定が以下であるとした場合に、

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Top',
      component: Top
    },
    {
      path: '/test',
      name: 'Test',
      component: Test
    }
  ]
})

以下のように mode: 'historyを入れれば解決です。

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'Top',
      component: Top
    },
    {
      path: '/test',
      name: 'Test',
      component: Test
    }
  ]
})

historyモードの注意点が1つあり、サーバ側のルーティング指定を適切に行わないと、http://localhost:8080/testとした場合に404エラーが発生してしまいます。

.htaccessの設定を変えるなど解決方法は調べれば出てくるのでここでは省きますが、後々別なところで適切なルーティングを行うための設定が必要になるということは留意してください。

Fire TV Stick

Fire TV Stick

新登場  Fire TV Stick 4K - Alexa対応音声認識リモコン付属

新登場 Fire TV Stick 4K - Alexa対応音声認識リモコン付属

Fire HD 10 タブレット (10インチHDディスプレイ) 64GB

Fire HD 10 タブレット (10インチHDディスプレイ) 64GB

Spring BootでEntityオブジェクトのクローンをしてみる

Entityオブジェクトのクローン

SpringBootで機能の実装をしている中で、Entityオブジェクトのクローンを作りたくなることはありませんか?

例えばコピー機能などを想像してみてください。
いちいちオブジェクトをnewして、同じ値を全てsetして保存する...そんな方法でもできますが、綺麗なやり方ではないですよね。
属性が多ければsetの漏れも出るかもしれません。
そんな時、Entityオブジェクトのcloneを使えば、楽にクローンを作ることができます。

クローンをしたいEntityクラスにCloneableインターフェースを実装

例えばtestEntityをクローンする場合、まずは以下のようにCloneableインターフェースを継承します。

@Entity
public class testEntity implements Cloneable {

これを忘れると、後々以下のようなエラーが発生するので気をつけてください。

CloneNotSupportedException

そして以下のようにEntityにcloneメソッドを追加します。
※引数の「User user」や各種setはもちろんなくても大丈夫です。あくまで以下のようなことができるという参考にしてください。

public testEntity clone(User user) throws CloneNotSupportedException {
	TestEntity cloneTestEntity = (TestEntity) super.clone();
	cloneTestEntity.setId(null); // 新規のエンティティの場合はidをnullにしておきましょう。そうしないとクローン元のオブジェクトの上書きになってしまいます。
	cloneTestEntity.setCreatedUser(user); // 例えばこのuserのように引数をセットすることが可能です。
	cloneTestEntity.setCreatedAt(new Date());
	return cloneTestEntity;
}

クローンメソッドの呼び出し

あとは以下のようにcloneメソッドを呼び出せばクローンを取得することが可能になります。
originalTestEntityはクローン元のオブジェクトになります。

TestEntity testEntity = originalTestEntity.clone(user);

Fire TV Stick

Fire TV Stick

新登場  Fire TV Stick 4K - Alexa対応音声認識リモコン付属

新登場 Fire TV Stick 4K - Alexa対応音声認識リモコン付属

Fire HD 10 タブレット (10インチHDディスプレイ) 64GB

Fire HD 10 タブレット (10インチHDディスプレイ) 64GB

SpringBootでローカルjarファイルをMavenで管理

ローカルjarファイルはどんな時に使う?

普段SpringBootで外部ライブラリを使う時、Mavenの設定ファイルであるpom.xmlを使う人が多いのではないでしょうか。

Mavenリポジトリにあり、誰でもインストールして使えるライブラリだとそのやり方で問題ないのですが、例えば以下の場合は少し工夫が必要になります。

  • 自作のライブラリを使う場合
  • 有料のライブラリなど、ダウンロードして使う場合

ちなみにローカル環境での開発では、プロジェクト内にライブラリを配置しておけば使えてしまいます。
例えばプロジェクト直下にlibディレクトリを作成して、その直下にjarファイルを配置してみてください。

これだけで正しくimportができれば使えてしまいます。

ただしこのやり方では、ローカル環境で動かせても、ビルド時にwarファイルを作成するタイミングでエラーが出てしまいます。
ライブラリが見つからないという状態になるのです。

そこで、ローカルjarファイルの場合は設定方法に工夫が必要です。

設定方法

今回はself_create.jarという自作ライブラリを読み込むことにします。
場所はプロジェクト直下にlibディレクトリを作成し、その直下に配置しているものとします。

pom.xmlに以下の記述をしてください。

まずはmaven-install-pluginの設定です。
pluginsの中に以下を記述してください。

<build>
  <plugins>
    <plugin>
        <!-- maven-compiler-pluginなどの設定があると思います -->
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-install-plugin</artifactId>
      <executions>
        <execution>
          <id>install-external</id>
          <phase>clean</phase>
          <configuration>
            <file>${basedir}/lib/self_create.jar</file>
            <repositoryLayout>default</repositoryLayout>
            <groupId>com.self</groupId><!-- ライブラリに合わせて設定 -->
            <artifactId>self_create</artifactId>
            <version>1.0</version><!-- 適当な値でも大丈夫です -->
            <packaging>jar</packaging>
            <generatePom>true</generatePom>
          </configuration>
          <goals>
              <goal>install-file</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

次にdependencyです。
上記の入力値に合わせて設定してください。

<dependency>
  <groupId>com.self</groupId>
  <artifactId>self_create</artifactId>
  <version>1.0</version>
</dependency>

他のやり方を見ると、scopeやsystemPathが入力されているものがありますが、ここは未入力にしてください。

これでmavenの再読み込みをすればライブラリが追加されます。

新登場  Fire TV Stick 4K - Alexa対応音声認識リモコン付属

新登場 Fire TV Stick 4K - Alexa対応音声認識リモコン付属