[Springboot3-1] MySQLを使ってCRUD RESTAPIを構築
1. はじめに
こんにちは、私は長い間システムメンテナンスをしてきたのですが、それで今日からしばらく底から開発する練習をもう一度してみようと思います。MySQLを使ってCRUD RESTAPIを構築してみます。
2.データベース接続してエンティティを作成
2.1 スプリングイニシャルライザーから依存性を追加(Spring Web、MySqlドライバー、Spring Data JPA、Lombok)
2.2 IntelliJ でプロジェクトを読み込んだ後、設定値を変更
2.3 application.propertiesでコードを追加
spring.datasource.url=jdbc:mysql://localhost:3306/user_management
spring.datasource.username=root
spring.datasource.password=0000
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update
*spring.jpa.hibernate.ddl-auto=updateはエンティティの値が変更された時に自動的にDBテーブルカラムを変更
2.4 MySQLのWorkbenchでuser_managementスキーマを追加
2.5 Userエンティティ作成後、カラム設定
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name= "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String firstName;
@Column(nullable = false)
private String lastName;
@Column(nullable = false, unique = true)
private String email;
}
*ここで指摘すべき点。
@NoArgsConstructor、@AllArgsConstructorは、ユーザクラスの生成者を自動的に生成します。
@NoArgsConstructorは、パラメータのない基本的な生成子を生成します。
主にJPA(Java Persistence API)のようなフレームワークでエンティティクラスの基本生成者を要求する時に使用されます
@AllArgsConstructor'は、すべてのフィールドをパラメータとして受け取る生成子を生成します。
このジェネレータを使用すると、オブジェクトを初期化するときにすべてのフィールドの値を一度に設定できます。
オブジェクトの作成と初期化に便利です。
2.6 Spring実行後、テーブルが生成されたことを確認できます。
3. ユーザエンティティのCRUD API構築
ここで疑問に思うのは、Serviceの中になぜあえてSerivceImplまで作らなければならないのかです。その理由は簡単です。 コードの品質性向上のためです。 Service InterfaceとServiceImplクラスを使用すると、コードのモジュール化と分離、関心事の分離、拡張性、メンテナンス性が容易になります。 これらの設計パターンは通常、ソフトウェアアーキテクチャを改善し、コードの可読性とメンテナンス性を向上させます。
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
Repositoryの場合、JpaRepositoryをインターフェースしていることを忘れないでください。
3.1ユーザ情報生成API
User createUser(User user);
UserService
@Override
public User createUser(User user) {
return userRepository.save(user);
}
UserServiceImpl
@RestController // @Controller + @ResponseBody
@AllArgsConstructor
@RequestMapping("api/users")
public class UserController {
private UserService userService;
// build create user REST API
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User savedUser = userService.createUser(user);
return new ResponseEntity<>(savedUser, HttpStatus.CREATED);
}
}
UserController
@RequestBodyアノテーションは、HTTP要求本文のデータをユーザオブジェクトに変換することを示します。 クライアントが要求本文にJSONまたは他の形式のデータでユーザー情報を送信すると、このメソッドはこれをUserJavaオブジェクトに自動的に変換します。
Response Entity オブジェクトを生成して返します。 Response EntityはHTTP応答を表し、この場合、UserオブジェクトとHTTP状態コードHttpStatus。CREATEDを含んでいます。 クライアントには、生成されたユーザー情報とともにHTTPステータスコード201 Createdが返されます
3.2 ユーザー情報照会API
UserService
User getUserById(Long userId);
List<User> getAllUsers();
UserServiceImpl
@Override
public User getUserById(Long userId) {
Optional<User> optionalUser = userRepository.findById(userId);
return optionalUser.get();
}
@Override
public List<User> getAllUsers() {
return userRepository.findAll();
}
ここで注目すべき部分は「Optional」というタイプです。 OptionalはJava 8から導入されたクラスで、値が存在しないときにNull Pointer Exceptionを防止し、コードをより安定的にするために使用されます。
userRepository.findById(userId)メソッドがデータベースからユーザを見つけられない場合、返される値はnullではなくOptional。empty()です。 このようにOptionalを使用すると、NullPointerException防止、値が見つからない場合は特定のデフォルト値を返却、コード可読性向上といった利点が得られます。
しかし、一つ疑問点はなぜ単一ユーザーを探す時はOptionalを使うが、すべてのユーザーを探す時はOptionalを使わないのかということです。 一般的に、すべてのユーザーを探すときは、Optionalよりは「空のリスト」を返却するのが合理的であるため、Optionalを使用しないのが一般的です。
UserController
// build get user by id REST API
// http://localhost:8080/api/users/1
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable("id") Long userId) {
User user = userService.getUserById(userId);
return new ResponseEntity<>(user, HttpStatus.OK);
}
// Build Get All Users REST API
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.getAllUsers();
return new ResponseEntity<>(users, HttpStatus.OK);
}
3.3 ユーザー情報修正API
UserService
User updateUser(User user);
UserServiceImpl
@Override
public User updateUser(User user) {
User existingUser = userRepository.findById(user.getId()).get();
existingUser.setFirstName(user.getFirstName());
existingUser.setLastName((user.getLastName()));
existingUser.setEmail(user.getEmail());
User updatedUser = userRepository.save(existingUser);
return updatedUser;
}
UserController
// Build Update User Rest API
@PutMapping("{id}")
// http://localhost:8008/api/users/1
public ResponseEntity<User> updateUser(@PathVariable("id") Long userId,
@RequestBody User user) {
user.setId(userId);
User updatedUser = userService.updateUser(user);
return new ResponseEntity<>(updatedUser, HttpStatus.OK);
}
3.4 ユーザー情報削除API
UserService
void deleteUser(Long userId);
UserServiceImpl
@Override
public void deleteUser(Long userId) {
userRepository.deleteById(userId);
}
生成、照会、修正と違って削除の場合はvoidタイプを使いますが、その理由が気になる方もいると思います。 その理由は削除作業の結果をあえて返す必要がなく、問題が発生したら例外を投げることで処理するのが一般的です。
UserController
@DeleteMapping("{id}")
public ResponseEntity<String> deleteUser(@PathVariable("id") Long userId) {
userService.deleteUser(userId);
return new ResponseEntity<>("User successfully deleted!", HttpStatus.OK);
}
4. まとめ
今回は、新たにウェブアプリケーションを作成し、Event-Drivenマイクロサービスを想定してSpringColudGateway、Kafka、RebbitMQ、Dockerも導入して作ってみようと思います。 最近、大型サービスは相当数がマイクロサービス方式でアーキテクチャを構成しています。 言葉だけたくさん聞いてみて、実際に具現は一度もしたことがないのですが、また底から作ってみるついでに一段階開発力を一段階ジャンプさせてみたくて挑戦してみようと思います。 どうか三日坊主で終わらないでほしいですね。
エンジニアファーストの会社 株式会社CRE-CO
ソンさん
この記事が気に入ったらサポートをしてみませんか?