春のブーツ。 バックグラウンドタスクなど

はじめに



このチュートリアルでは、Scheduledアノテーションを使用して、誕生日(お祝いなど)に基づいてユーザーに電子メールを送信するアプリケーションの例を紹介します。 私の意見では、データベース(この場合はPostgreSQL)の操作、Spring Data JPA、新しいjava 8 time api、メールサービス、バックグラウンドタスクの作成など、かなり多くのことが含まれているため、この例を挙げることにしました。コンパクトなままの小規模ビジネスロジック。 今日、インターネットには、通常CrudRepositoryやJpaRepositoryなどから継承する方法に要約される膨大な種類のチュートリアルが豊富にあります。 このチュートリアルは、少なくともそれらのいくつかを既に見て、Spring Bootが何であるかを理解しているという事実のために設計されています。 その機能とその操作方法をより広く示すアプリケーションの例を示します。



プロジェクト作成



Spring Initializrに移動します。



依存関係を追加します。



1. PosgreSQL-データベースとして

2. JPA-データベースへのアクセス

3.ロンボク-便宜上、定型コードを削除する(ゲッター、セッターなどを自分で記述する必要はありません)、詳細はこちら

4.メール-実際に仕事やメールを送信するためのもの。 ドキュメント



グループとアーティファクト、たとえばcom.applicationとtaskを指定します。 プロジェクトをダウンロードして解凍し、開発環境で開きます。このIntellij IDEAがあります。



データベース



PostgreSQLをインストールします 。 次に、ユーザーとパスワードを使用してデータベースを作成します。 Linuxを使用している場合は、次のコマンドを使用してコマンドラインを使用し、データベースタブでIDEAから直接これを行うことができます。



sudo -u postgres createuser <username> sudo -u postgres createdb <dbname> $ sudo -u postgres psql psql=# alter user <username> with encrypted password '<password>'; psql=# grant all privileges on database <dbname> to <username> ;
      
      





また、Windowsでは、これはpgAdminまたはその代替を使用して実行できます。



開始する



プロジェクトを開き、コードの記述を開始できます。



これで、プロジェクトには1つのJavaファイルしかありません。 次のようになります。



 @SpringBootApplication public class TaskApplication { public static void main(String[] args) { SpringApplication.run(TaskApplication.class, args); } }
      
      





クラス名は、プロジェクトの作成時に指定したアーティファクトの名前によって異なる場合があります。



このクラスは、アプリケーションの起動ポイントです。 @SpringBootApplicationアノテーションは、Spring Bootアプリケーションであり、@ Configuration、@ EnableAutoConfiguration、および@ComponentScanを使用することと同等であることを意味します。



モデル作成



まず、クラスが置かれているディレクトリを分割してアプリケーション全体を実行し、モデル、リポジトリ、サービスの3つのディレクトリに分割します。



次に、モデルフォルダーでUserクラスを作成します。



 @Getter @Setter @ToString @NoArgsConstructor @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(name = "name", nullable = false) private String name; @Email private String email; private LocalDate birthday; }
      
      





そこで、必要な最小限のフィールド(ユーザーID、彼の名前、メールアドレス、生年月日)を持つクラスを作成しました。



アノテーションを見てみましょう。クラスの最初の4つは、ゲッター、セッター、toStringメソッド、および引数なしのコンストラクターを生成するロンボックアノテーションです。



エンティティ -このクラスがエンティティであることをHibernateに示します。

テーブル -データベース内のテーブルに対応する名前。

Id-このクラスの主キーを示します。

@GeneratedValue- Idと共に使用され、戦略とジェネレーターのパラメーターを定義します。

@Column-エンティティのプロパティにマップされる列の名前を示します。また、nullable = falseを使用すると、フィールドが必須であることを示します。

電子メール -文字列は有効な電子メールアドレスである必要があります(最後にこのアノテーションは古くなっているため、org.hibernate.validator.constraintsではなくjavax.validation.constraintsパッケージが使用されます)。



リポジトリ



次に、リポジトリフォルダーで、UserRepositoryインターフェイスを作成します。



 public interface UserRepository extends JpaRepository<User, Integer> {}
      
      





JpaRepositoryからの継承により、delete、save、findAllなどのデータベースを操作するためのメソッドを使用する機会が与えられます。 さらに、必要に応じて、「必要なものを書く」という原則に基づいて独自のメソッドを作成できます。 つまり、同じ名前のすべてのユーザーを見つける必要がある場合、メソッドは次のようになります。



 List<User> findAllByName(String name);
      
      





このメソッドは、最終的に次のようなSQLクエリを作成します。



 SELECT * FROM users WHERE name = ?;
      
      





または例えば:



 List<User> findByBirthdayAfter(LocalDate date);
      
      





特定の日付以降に生まれたすべてのユーザーを選択できます。



一般に、これはかなり広範なトピックであり、かなりの数の記事とビデオがあります。 たとえばこれが好きです。

メソッドの本体のすぐ上にあるクエリ JPAアノテーションを使用して、SQLクエリを記述することもできます。

2種類の構文を使用できます: JPQL (テーブルや列の代わりにSQLを使用するJPAクエリ言語-エンティティ、属性など)または実際に使用するSQL(その後、nativeQuery = trueプロパティが追加されます)。 JPQLの例:



 @Query(value = "SELECT u from User u where u.name = ?1") List<User> findAllByName(String name);
      
      





JPA @Paramアノテーションを使用して、リクエストパラメータの名前を示すことができます。



 @Query(value = "SELECT u from User u where u.name = :name") List<User> findAllByName(@Param("name") String name);
      
      





純粋なSQLを使用する場合:



 @Query(value = "SELECT * FROM users WHERE name = :name", nativeQuery = true) List<User> findAllByName(@Param("name") String name);
      
      





メールがnullではないすべてのユーザーをデータベースから取得し、月と誕生日がそこに転送するユーザーに対応するメソッドを作成します。 これで、リポジトリは次のようになります。



 @Repository public interface UserRepository extends JpaRepository<User, Integer> { @Query(value = "SELECT * FROM users " + "WHERE email IS NOT NULL " + "AND extract(MONTH FROM birthday) = :m " + "AND extract(DAY FROM birthday) = :d", nativeQuery = true) List<User> findByMatchMonthAndMatchDay(@Param("m") int month, @Param("d") int day); }
      
      





いくつかのリポジトリ機能



ジェネリックの最初のパラメーターは、使用するエンティティであり、2番目はその主キーのタイプに対応する必要があります。

また、メソッドのタイプは最初のパラメーターに対応する必要があります(独自のマッピングを使用しない場合)。

このディレクトリがなぜdaoではなくリポジトリと呼ばれるのかという質問が突然あった場合、これはSpring Bootの調子の良いルールです。同じことをする義務はなく、そのまま受け入れられます。



サービス



まず、サービスディレクトリにUserRepositoryServiceインターフェイスを作成します。



 public interface UserRepositoryService { List<User> getAll(int month, int day); }
      
      





次に、ここで別のディレクトリimplを作成し、その中にサービスの実装クラスを作成します。



 @Service public class UserRepositoryServiceImpl implements UserRepositoryService { private final UserRepository repository; @Autowired public UserRepositoryServiceImpl(UserRepository repository) { this.repository = repository; } @Override public List<User> getAll(int month, int day) { return repository.findByMatchMonthAndMatchDay(int month, int day); } }
      
      





それでは、クラスを分析しましょう。

サービスの注釈は、Springがそれがサービスであることを示しています。

次に、UserRepository型の変数を宣言し、@ Autowiredアノテーションでマークした後、コンストラクターで初期化します。

(リポジトリフィールドのすぐ上に注釈を配置できますが、コンストラクタまたはセッターを作成することをお勧めします)

@Autowired-springは、目的のBeanを検出し、その値をアノテーションでマークされたプロパティに置き換えます。

クラスの上にLombokアノテーションを使用して自動配線コンストラクターを作成することができます。



 @RequiredArgsConstructor(onConstructor = @__(@Autowired))
      
      





コンストラクターの後、インターフェイスのメソッドを実装し、その中のリポジトリからメソッドを返します。

先に進みます:サービスディレクトリにEmailServiceを作成します。



 public interface EmailService { void send(String to, String title, String body); }
      
      





そして、その実装のEmailServiceImplの実装:



 @Service @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class EmailServiceImpl implements EmailService { private final JavaMailSender emailSender; @Override public void send(String to, String subject, String text) { MimeMessage message = this.emailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message); try { helper.setTo(to); helper.setSubject(subject); helper.setText(text); this.emailSender.send(message); } catch (MessagingException messageException) { throw new RuntimeException(messageException); } } }
      
      





説明には入りませんが、ここにODがあります。



次に、サービスの中で、シェダーとビジネスロジックを持つ最後のメインクラスを作成します(SchedulerServiceなど)。



次のフィールドをすぐに定義します。



 @Slf4j @Service @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class SchedulerService { private final UserRepositoryService userService; private final EmailService emailService; }
      
      





そこで、ロガー(Lombok @ Slf4jアノテーション)、コンストラクターのユーザーおよびメールサービス(@RequiredArgsConstructor(onConstructor = @__(@ Autowired)))を初期化しました。



次に、void sendMailToUsersメソッドを作成し、その上の注釈を示します。



 @Scheduled(cron = "*/10 * * * * *")
      
      





この注釈により、メソッドがいつ動作するかを指定できます。 特定の時間と日付のスケジュールを指定できるcronパラメーターを使用します。 fixedRate(メソッド呼び出しの間隔を定義する)、fixedDelay(メソッドの最後の呼び出しが終了してから次の呼び出しが開始するまでの間隔を決定する)、initialDelay(fixedRateまたはfixedDelayの最初の実行前に遅延するミリ秒数)などのパラメーターもあります。



cron行の各アスタリスクは、秒、分、時間、日、月、曜日を示します。 詳細はこちらです。 現在、この値はチェックが10秒ごとに行われることを意味します。これは例として行われますが、将来これを変更します。



便宜上、cron値を定数に移動できます。



 private static final String CRON = "*/10 * * * * *";
      
      





メソッドで、現在の日付(java日付と時刻api)の変数、日付から取得した月と日の変数、サービスのメソッドによって初期化されたユーザーのリストを作成し、空になるかどうかを確認します。



 @Scheduled(cron = CRON) public void sendMailToUsers() { LocalDate date = LocalDate.now(); int month = date.getMonthValue(); int day = date.getDayOfMonth(); List<User> list = userService.getUsersByBirthday(month, day); if (!list.isEmpty()) { } }
      
      





ここで、ユーザーごとにメッセージの変数を作成し、EmailServiceからsendメソッドを呼び出して、ユーザーのメール、ヘッダー、メッセージを渡します。 最後に、例外を回避するためにすべてをtry / catchでラップします。 すべて、メソッドの準備ができました。



クラス全体を見てみましょう:



 @Service @Slf4j @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class SchedulerService { private static final String CRON = "*/10 * * * * *"; private final UserRepositoryService userService; private final EmailService emailService; @Scheduled(cron = CRON) public void sendMailToUsers() { LocalDate date = LocalDate.now(); int month = date.getMonthValue(); int day = date.getDayOfMonth(); List<User> list = userService.getUsersByBirthday(month, day); if (!list.isEmpty()) { list.forEach(user -> { try { String message = "Happy Birthday dear " + user.getName() + "!"; emailService.send(user.getEmail(), "Happy Birthday!", message); log.info("Email have been sent. User id: {}, Date: {}", user.getId(), date); } catch (Exception e) { log.error("Email can't be sent.User's id: {}, Error: {}", user.getId(), e.getMessage()); log.error("Email can't be sent", e); } }); } } }
      
      





バックグラウンドタスクを実行できるようにするために、次のようにTaskApplicationの@SpringBootApplicationに@EnableSchedulingアノテーションを直接追加します。



 @EnableScheduling @SpringBootApplication public class TaskApplication { public static void main(String[] args) { SpringApplication.run(TaskApplication.class, args); } }
      
      





これでJavaコードの作業が完了しました。resourcesディレクトリのapplication.propertiesファイルで設定を指定するだけです。



構成



 #   .   ,      server.port=7373 #   spring.jpa.database=POSTGRESQL spring.jpa.show-sql=true spring.datasource.platform=postgres spring.jpa.generate-ddl=true spring.jpa.hibernate.ddl-auto=update spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:5432/your_database?stringtype=unspecified spring.datasource.username=your_database_username spring.datasource.password=your_database_password #  logging.level.org.hibernate=info logging.level.org.springframework.security=debug #      jpa  postgreSQL spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults = false spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect # email-a spring.mail.host=smtp.gmail.com spring.mail.port=587 spring.mail.username=your_email@gmail.com spring.mail.password=your_password spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true spring.mail.properties.mail.smtp.starttls.required=true
      
      





いくつかの説明:

 spring.jpa.generate-ddl=true spring.jpa.hibernate.ddl-auto=update
      
      





これらは、エンティティを使用してデータベース内のテーブルを自動的に作成/更新するために使用されます(運用環境では、値をfalseおよびnoneに変更することをお勧めします)



 spring.datasource.url=jdbc:postgresql://localhost:5432/your_database?stringtype=unspecified spring.datasource.username=your_database_username spring.datasource.password=your_database_password
      
      





データベースの名前、ログイン、パスワードがここに表示されます



 spring.mail.username=your_email@gmail.com spring.mail.password=your_password
      
      





あなたまたはテスト用の電子メールとパスワード。 Gmailへのアクセスでエラーが発生する可能性があります。そのためには、セキュリティとログインタブの設定で信頼性の低いアプリケーションを有効にするだけです。



打ち上げ



TaskApplicationにアクセスして、アプリケーションを実行します。 すべてが正しく行われている場合、10秒ごとに同様のログが必要です。



 Hibernate: select users0_.id as id1_0_, users0_.birthday as birthday2_0_, users0_.email as email3_0_, users0_.name as name4_0_ from users users0_ where (users0_.birthday is not null) and (users0_.email is not null)
      
      





つまり、このメソッドは少なくともデータベースからユーザーのリストを取得します。 データベースを開くと(IDEAで直接行います。通常は右上隅のデータベースタブに、必要なデータベースに接続する機会があります)、対応するフィールドを持つユーザーテーブルが表示されていることがわかります。 新しいレコードを作成し、誕生日と現在の日付を自分のメールとして入力しましょう。 変更をコミットした後、10秒ごとにログが表示され、電子メールが正常に送信されたことが通知されます。 電子メールを確認し、すべてが正しく行われた場合、誕生日のお祝いが1回以上行われます(メソッドの実行回数によって異なります)。 アプリケーションを停止し、CRON値を「0 0 10 * * *」に変更します。これは、チェックが10秒ごとではなく、毎日午前10時に行われることを意味します。



おわりに



この例に基づいて、特にバックグラウンドプロセスに関連するさまざまなタスクを作成して解決することができます。主なことは実験を恐れないことです。 本日は、Spring Boot、データベース、Javaの操作方法をよりよく理解できるようになりました。 誰かが興味を持っている場合は、コントローラーを追加して(たとえば、必要に応じて電子メール送信をオフにできるように)テストとセキュリティを追加して、記事の2番目の部分を作成できます。



このトピックに関する建設的な批判とコメントを歓迎します。

コメントに感謝します: StanislavLelegorodAPXEOLOGシンガポール



参照資料



Githubソースコード

公式のSpring Boot ドキュメント



All Articles