かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot でメール送信する Web アプリケーションを作る ( その9 )( メール送信画面の作成4 )

概要

Spring Boot でメール送信する Web アプリケーションを作る ( その8 )( メール送信画面の作成3 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • メール送信画面の作成
    • 以下の点を調査・修正します。
      • create table 文のテーブル名/カラム名は英大文字で記述したのですが、Doma-Gen で生成された Entity クラスの中のテーブル名/カラム名や、pgAdmin Ⅲ 上に表示されるテーブル名/カラム名は英小文字で表示されていることに気づきました。PostgreSQL でのテーブル名/カラム名の命名ルールを確認してみます。
      • 現在 Service クラスのメソッドに @Transactional アノテーションを付加してトランザクションを開始していますが、XMLファイルから AOPトランザクションが設定されるようにします。
      • dataSource と transactionManager を利用したいだけならば spring-boot-starter-data-jpa ではなく spring-boot-starter-jdbc でもよいような気がするので試してみます。
      • 性別(sex)と項目(type)が保存されない問題を解消します。
      • email_item テーブルにデータを保存する処理で毎回インスタンスを生成するように実装しましたが、emailItemId をクリアすればよいだけのような気もするので試してみます。
      • 画面の初期表示時に From の入力フィールドにカーソルが移動するようにします。

ソフトウェア一覧

参考にしたサイト

  1. PostgreSQLでは識別子に大文字を使ってはいけない
    http://doma.readthedocs.org/ja/stable/

  2. Postgresqlのテーブル名が大文字だとややこしい件
    http://suguru1989-programming.hatenablog.com/entry/2014/07/10/001357

  3. PostgreSQLで大文字のテーブル名やフィールド名を扱う
    http://cadweb.jp/blog/2006/09/postgresql.html

手順

PostgreSQL のテーブル名/カラム名は必ず小文字?

  1. 「参考にしたサイト」に上げたサイトの内容を読んだ結論は、PostgreSQL では SQL 文内のダブルクォーテーションで囲まれていないテーブル名/カラム名は英小文字に変換される、ということでした。まさかそんな仕様があるとは。。。 PostgreSQL を使用する時は英大文字は使わないという対応が無難ですね。

  2. SQL ファイル、Java のソース内のコメントのテーブル名/カラム名を英小文字に変更します。IntelliJ IDEA では変換したい文字にカーソルを移動して ( 変換したい文字列を選択状態にする必要はありません ) Ctrl+SHIFT+U を押せば英大文字←→英小文字に変換することが出来ます。

  3. IntelliJ IDEA で 1.0.x-lower-tablename ブランチを作成します。

  4. /sql の下の create_table.sqlリンク先のその1の内容 に変更します。

  5. src/main/java/ksbysample/webapp/email/web/mailsend の下の MailsendService.javaリンク先のその1の内容 に変更します。

  6. commit、GitHub へ Push、1.0.x-lower-tablename -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-lower-tablename ブランチを削除、をします。

@Transactional アノテーションではなく applicationContext.xmlトランザクションを設定する

  1. IntelliJ IDEA で 1.0.x-transactionwithconf ブランチを作成します。

  2. @Transactional アノテーションを削除します。src/main/java/ksbysample/webapp/email/web/mailsend の下の MailsendService.javaリンク先のその2の内容 に変更します。

  3. ksbysample.webapp.email の下 ( サブパッケージを含む ) にある末尾が "Service" で終わるクラスのメソッド全てに txAdvice の設定を反映します。src/main/resources の下の applicationContext.xmlリンク先の内容 に変更します。

  4. 動作確認します。Gradle projects View から bootRun タスクを実行して Tomcat を起動します。

  5. ブラウザを起動し http://localhost:8080/mailsend へアクセスします。以下の画像の値を入力後、「送信」ボタンをクリックします。

    f:id:ksby:20150505091551p:plain

  6. エラーは発生せず、メールも送信され email, email_item どちらのテーブルにもデータが保存されていました。

  7. Run View で Ctrl+F2 を押して Tomcat を停止します。

  8. commit、GitHub へ Push、1.0.x-transactionwithconf -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-transactionwithconf ブランチを削除、をします。

spring-boot-starter-data-jpa ではなく spring-boot-starter-jdbc でも dataSource, transactionManager は利用可能か?

dataSource の生成の仕組みと自動生成される transactionManager Bean を利用したいだけで、Doma 2 を使用する場合には Spring Data JPA を利用することはないので、spring-boot-starter-data-jpa ではなく spring-boot-starter-jdbc でも大丈夫なのか確認します。

  1. IntelliJ IDEA で 1.0.x-starter-jpa2jdbc ブランチを作成します。

  2. build.gradle を リンク先の内容 に変更します。

  3. Gradle projects View の左上にある「Refresh all Gradle projects」アイコンをクリックして、変更した build.gradle の内容を反映します。

  4. Gradle projects View から clean タスク、build タスクを実行します。

  5. Gradle projects View から bootRun タスクを実行して Tomcat を起動しようとしたところ java.lang.NoClassDefFoundError: org/aspectj/weaver/tools/PointcutExpression というエラーログが出力されて起動できませんでした。AspectJ のライブラリが足りないようです。

    単純に spring-boot-starter-data-jpa → spring-boot-starter-jdbc に変更しても動作するのかを確認したいだけでしたので、この件の調査はこれで中止とします。

    ※逆にこのエラーで気づきましたが、spring-boot-starter-data-jpa を入れると Spring AOP に必要なライブラリも読み込まれて AOP が使用できるようになるようです。

  6. 変更を全て元に戻します。

    C:\project-springboot\ksbysample-webapp-email>git reset --hard

  7. ブランチを 1.0.x に変更し、1.0.x-starter-jpa2jdbc ブランチを削除します。

  8. Gradle projects View の左上にある「Refresh all Gradle projects」アイコンをクリックして、変更した build.gradle の内容を反映します。

  9. Gradle projects View から clean タスク、build タスクを実行します。

  10. Gradle projects View から bootRun タスクを実行して Tomcat が起動することを確認した後、Run View で Ctrl+F2 を押して Tomcat を停止します。

性別(sex)と項目(type)を保存する

email テーブルの sex, type、email_item テーブルの item を Form クラスに合わせて smallint → varchar へ変更します。

  1. IntelliJ IDEA で 1.0.x-change-columntype ブランチを作成します。

  2. /sql の下の create_table.sqlリンク先のその2の内容 に変更します。

  3. テーブルを作成し直します。

C:\project-springboot\ksbysample-webapp-email>psql -U ksbyemail_user ksbyemail  
ユーザ ksbyemail_user のパスワード:                                             
psql (9.4.1)                                                                    
"help" でヘルプを表示します.                                                    
                                                                                
ksbyemail=> drop table email_item;                                              
DROP TABLE                                                                      
ksbyemail=> drop table email;                                                   
DROP TABLE                                                                      
ksbyemail=> CREATE TABLE email                                                  
ksbyemail-> (                                                                   
ksbyemail(>     email_id        bigserial primary key                           
ksbyemail(>     , from_addr     varchar(65) not null                            
ksbyemail(>     , to_addr       varchar(65) not null                            
ksbyemail(>     , subject       varchar(128) not null                           
ksbyemail(>     , name          varchar(32)                                     
ksbyemail(>     , sex           varchar(1)                                      
ksbyemail(>     , type          varchar(1)                                      
ksbyemail(>     , naiyo         text                                            
ksbyemail(> );                                                                  
CREATE TABLE                                                                    
ksbyemail=> CREATE TABLE email_item                                             
ksbyemail-> (                                                                   
ksbyemail(>     email_item_id   bigserial primary key                           
ksbyemail(>     , email_id      bigint not null references EMAIL(email_id) on delete cascade                                                                    
ksbyemail(>     , item          varchar(3)                                      
ksbyemail(> );                                                                  
CREATE TABLE                                                                    
ksbyemail=> \d                                                                  
                          リレーションの一覧                                    
 スキーマ |             名前             |     型     |     所有者              
----------+------------------------------+------------+----------------         
 public   | email                        | テーブル   | ksbyemail_user          
 public   | email_email_id_seq           | シーケンス | ksbyemail_user          
 public   | email_item                   | テーブル   | ksbyemail_user          
 public   | email_item_email_item_id_seq | シーケンス | ksbyemail_user          
(4 行)                                                                          
                                                                                
                                                                                
ksbyemail=> \q                                                                  
                                                                                
C:\project-springboot\ksbysample-webapp-email>                                  

※drop table すると id 自動発行用のシーケンスも作成し直されて、発行される値が 1 に戻ります。

  1. Gradle projects View から gen タスクを実行して Entity クラスを作成し直します。以下メモ書きです。

    • Entity クラスは gen タスクを実行すると再作成されます ( Doma-Gen の EntityConfig の overwrite パラメータのデフォルトが true のためです ) 。事前に削除しておく必要はありません。
    • ただしテーブルを削除した場合は Entity クラスは自動で削除されません。手動で削除して下さい。
    • Dao クラスは gen タスクを実行しても再作成されません ( Doma-Gen の DaoConfig の overwrite パラメータのデフォルトが false のためです ) 。
  2. src/main/java/ksbysample/webapp/email/web/mailsend の下の MailsendService.javaリンク先のその3の内容 に変更します。

  3. 動作確認します。Gradle projects View から bootRun タスクを実行して Tomcat を起動します。

  4. ブラウザを起動し http://localhost:8080/mailsend へアクセスします。以下の画像の値を入力後、「送信」ボタンをクリックします。

    f:id:ksby:20150505091551p:plain

  5. エラーは発生せず、メールも送信されました。email, email_item どちらのテーブルにもデータが保存されており、かつ性別(sex)と項目(type)にも値がセットされています。

    f:id:ksby:20150507023947p:plain f:id:ksby:20150507024140p:plain

  6. Run View で Ctrl+F2 を押して Tomcat を停止します。

  7. commit、GitHub へ Push、1.0.x-transactionwithconf -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-transactionwithconf ブランチを削除、をします。

email_item テーブルのデータ保存処理で毎回インスタンスを生成しなくてもよいか?

  1. IntelliJ IDEA で 1.0.x-necessary-new-emailitem ブランチを作成します。

  2. src/main/java/ksbysample/webapp/email/web/mailsend の下の MailsendService.javaリンク先のその4の内容 に変更します。

  3. 動作確認します。Gradle projects View から bootRun タスクを実行して Tomcat を起動します。email, email_item テーブルにデータが登録されている場合には全て削除しておきます。

  4. ブラウザを起動し http://localhost:8080/mailsend へアクセスします。以下の画像の値を入力後、「送信」ボタンをクリックします。

    f:id:ksby:20150505091551p:plain

  5. エラーは発生せず、メールも送信されました。email_item テーブルにもデータが保存されています。

    f:id:ksby:20150508223132p:plain

  6. 問題なさそうですので、このまま進めます。Run View で Ctrl+F2 を押して Tomcat を停止します。

  7. commit、GitHub へ Push、1.0.x-necessary-new-emailitem -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-necessary-new-emailitem ブランチを削除、をします。

画面の初期表示時に From の入力フィールドにカーソルを移動する

ID を from → fromAddr に変更した際に Javascript 内の ID を変更するのを忘れていました。修正します。

  1. IntelliJ IDEA で 1.0.x-setcursor-fromfield ブランチを作成します。

  2. src/main/resources/templates/mailsend の下の mailsend.html を リンク先の内容 に変更します。

  3. 動作確認します。Gradle projects View から bootRun タスクを実行して Tomcat を起動します。

  4. ブラウザを起動し http://localhost:8080/mailsend へアクセスし、From フィールドにカーソルが移動することを確認します。

  5. Run View で Ctrl+F2 を押して Tomcat を停止します。

  6. commit、GitHub へ Push、1.0.x-necessary-new-emailitem -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-necessary-new-emailitem ブランチを削除、をします。

ソースコード

create_table.sql

■その1

CREATE TABLE email
(
    email_id        bigserial primary key
    , from_addr     varchar(65) not null
    , to_addr       varchar(65) not null
    , subject       varchar(128) not null
    , name          varchar(32)
    , sex           smallint
    , type          smallint
    , naiyo         text
);

CREATE TABLE email_item
(
    email_item_id   bigserial primary key
    , email_id      bigint not null references EMAIL(email_id) on delete cascade
    , item          smallint
);
  • テーブル名/カラム名を英大文字→英小文字に変更します。

■その2

CREATE TABLE email
(
    email_id        bigserial primary key
    , from_addr     varchar(65) not null
    , to_addr       varchar(65) not null
    , subject       varchar(128) not null
    , name          varchar(32)
    , sex           varchar(1)
    , type          varchar(1)
    , naiyo         text
);

CREATE TABLE email_item
(
    email_item_id   bigserial primary key
    , email_id      bigint not null references EMAIL(email_id) on delete cascade
    , item          varchar(3)
);
  • email テーブルの sex, type の型を smallint → varchar(1) へ変更します。
  • email_item テーブルの item の型を smallint → varchar(3) へ変更します。

MailsendService.java

■その1

    @Transactional(rollbackFor = Exception.class)
    public void saveAndSendEmail(MailsendForm mailsendForm) {
        // 入力されたデータを email, email_item テーブルに保存する
        saveEmail(mailsendForm);

        // メールを送信する
        sendEmail(mailsendForm);
    }

    public void saveEmail(MailsendForm mailsendForm) {
        // email テーブルに保存する
        Email email = new Email();
        BeanUtils.copyProperties(mailsendForm, email);
        emailDao.insert(email);

        // email_item テーブルに保存する
        for (String item : mailsendForm.getItem()) {
            EmailItem emailItem = new EmailItem();
            emailItem.setEmailId(email.getEmailId());
            emailItem.setItem(Short.parseShort(item));
            emailItemDao.insert(emailItem);
        }
    }
  • saveAndSendEmail メソッド、saveEmail メソッド内のコメントに記述していたテーブル名を英大文字→英小文字に変更します。

■その2

    @Autowired
    private EmailService emailService;

    public void saveAndSendEmail(MailsendForm mailsendForm) {
        // 入力されたデータを email, email_item テーブルに保存する
        saveEmail(mailsendForm);

        // メールを送信する
        sendEmail(mailsendForm);
    }
  • saveAndSendEmail メソッドに付加していた @Transactional(rollbackFor = Exception.class) を削除します。

■その3

    public void saveEmail(MailsendForm mailsendForm) {
        // email テーブルに保存する
        Email email = new Email();
        BeanUtils.copyProperties(mailsendForm, email);
        emailDao.insert(email);

        // email_item テーブルに保存する
        for (String item : mailsendForm.getItem()) {
            EmailItem emailItem = new EmailItem();
            emailItem.setEmailId(email.getEmailId());
            emailItem.setItem(item);
            emailItemDao.insert(emailItem);
        }
    }
  • emailItem.setItem(Short.parseShort(item));emailItem.setItem(item); に変更します。

■その4

    public void saveEmail(MailsendForm mailsendForm) {
        // email テーブルに保存する
        Email email = new Email();
        BeanUtils.copyProperties(mailsendForm, email);
        emailDao.insert(email);

        // email_item テーブルに保存する
        EmailItem emailItem = new EmailItem();
        for (String item : mailsendForm.getItem()) {
            emailItem.setEmailItemId(null);
            emailItem.setEmailId(email.getEmailId());
            emailItem.setItem(item);
            emailItemDao.insert(emailItem);
        }
    }
  • for 文の前で EmailItem emailItem = new EmailItem(); を実行するよう変更します。
  • for 文の中では emailItem.setEmailItemId(null); を実行して emailItemId をクリアします。

applicationContext.xml

    <aop:config>
        <aop:pointcut id="pointcutService" expression="execution(* ksbysample.webapp.email..*Service.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcutService"/>
    </aop:config>
  • ksbysample.webapp.email.service..*Service.*(..))ksbysample.webapp.email..*Service.*(..)) へ変更します。

build.gradle

dependencies {
    def jdbcDriver = "org.postgresql:postgresql:9.4-1201-jdbc41"

    // spring-boot-gradle-plugin によりバージョン番号が自動で設定されるもの
    // Appendix E. Dependency versions ( http://docs.spring.io/spring-boot/docs/current/reference/html/appendix-dependency-versions.html ) 参照
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.springframework.boot:spring-boot-starter-thymeleaf")
    compile("org.springframework.boot:spring-boot-starter-jdbc")
    compile("org.springframework.boot:spring-boot-starter-velocity")
    compile("org.springframework.boot:spring-boot-starter-mail")
    compile("org.codehaus.janino:janino")
    testCompile("org.springframework.boot:spring-boot-starter-test")
    testCompile("org.yaml:snakeyaml")

    // spring-boot-gradle-plugin によりバージョン番号が自動で設定されないもの
    compile("${jdbcDriver}")
    compile("org.seasar.doma:doma:2.2.0")
    compile("org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16")
    compile("org.apache.commons:commons-lang3:3.4")
    compile("org.projectlombok:lombok:1.16.2")
    testCompile("org.dbunit:dbunit:2.5.0")

    // for Doma-Gen
    domaGenRuntime("org.seasar.doma:doma-gen:2.2.0")
    domaGenRuntime("${jdbcDriver}")
}
  • compile("org.springframework.boot:spring-boot-starter-data-jpa")compile("org.springframework.boot:spring-boot-starter-jdbc") へ変更します。

mailsend.html

<div th:replace="common/bottom-js"></div>
<script type="text/javascript">
<!--
$(document).ready(function() {
    $('#fromAddr').focus();

    $('#send').bind('click', function(){
        $('#mailsendForm').submit();
    });
});
-->
</script>
</body>
</html>
  • $('#from').focus();$('#fromAddr').focus(); へ変更します。

履歴

2015/05/08
初版発行。