Java LTS 17, 21 or later. Pada 2024 versi LTS terakhir versi 21.
You can also import the code straight into your IDE:
Buka laman web https://start.spring.io. Lalu inisialisasi projek yang ingin dibuat.
Kita akan membuat Project Maven, dengan bahasa pemrograman Java, lalu versi Spring Boot yang dipilih adalah versi 3.3.2.
Download lalu extract hasil generate project di atas.
Buka Editor anda di bawah ini merupakan contoh jika menggunakan IntelliJ IDEA
Selanjutnya kita perlu menambahkan beberapa dependency atau library yang akan digunakan ke dalam file pom.xml.
Dependency ini bertujuan untuk bisa mengakses ke dalam database yang sudah dikonfigurasi.
Penambahan juga dapat dilakukan saat sebelum mengenerate project pada Spring Initializr.
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-validation
org.springframework.boot
spring-boot-devtools
runtime
com.mysql
mysql-connector-j
org.projectlombok
lombok
provided
Selanjutnya tambahkan properties, setup project pada application.properties seperti berikut
spring.application.name=hello
# changing port
server.port=7000
# changing port
server.servlet.context-path=/v1/api
# setup database connection
# driver database
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# database url connection
spring.datasource.url=jdbc:mysql://localhost:3306/demo
# database username
spring.datasource.username=root
# database password
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format-sql=true
/v1/api
maka, endpoint API kita dapat diakses pada localhost:8000/v1/api/{endpoint}.
jdbc:db_type://host_name:port/db_name.
src
+- main
| +- java
| | +- com
| | +- ombagoes
| | +- blog
| | +- dtos
| | +- controllers
| | | +- PostController.java
| | +- models
| | | +- Post.java
| | +- repositories
| | | +- PostRepository.java
| | +- BlogApplication.java
| +- resources
| +- application.properties
+- test
Di dalam root projectnya akan dibuat beberapa packages atau folder, seperti
Buat package baru bernama models
sesuaikan dengan Struktur Direktori, lalu buat file class bernama Post
.
models/Post.java
package com.ombagoes.hello.models;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Getter
@Entity
@Table(name = "posts")
@NoArgsConstructor
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Setter
private String title;
@Setter
private String description;
@Setter
private boolean published;
@Column(name = "created_at")
@Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
@Column(name = "updated_at")
@Temporal(TemporalType.TIMESTAMP)
private Date updatedAt;
@PrePersist
protected void onCreate() {
createdAt = new Date();
}
@PreUpdate
protected void onUpdate() {
updatedAt = new Date();
}
//DTO
public Post(String title, String description, boolean published) {
this.title = title;
this.description = description;
this.published = published;
}
@Override
public String toString() {
return "Tutorial [id=" + id + ", title=" + title + ", desc=" + description + ", published=" + published + "]";
}
}
Pada pom.xml sebelumnya kita sudah menambahkan dependency lombok. Fungsinya adalah untuk menggenerate fungsi sbb :
@Getter
& @Setter
, Menggenerate fungsi Get
dan Set
untuk field secara otomatis. Untuk case di atas Get
digenerate di semua field, sementara Setter hanya pad a field yang ditambahkan Annotation @Setter
di atas field.
@NoArgsConstructor
Menggenerate constructor default kosong pada Class
. jika anda menggunakan semua file tambahkan @AllArgsConstructor
untuk menggantikan constructor public Post(String title, ...){...}
di atas, tidak digunakan karena tidak sesuai. Akan berguna jika anda membuat Dto
.
Simplenya tanpa lombok kita harus membuat beberapa fungsi seperti berikut :
public class Post{
...
//NoArgsConstructor
public Post() {
}
//Getters and Setters
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
//dst. untuk semua field
...
}
@Data
merupakan generator untuk memanggil generator lain @Getter
, @Setter
, @NoArgsConstructor
, dan @AllArgsConstructor
, Jadi jika anda memerlukan semuanya cukup memangil Anotasi @Data
import lombok.Data;
@Data
public class ContohKelas {
private String title;
private String description;
private boolean published;
}
Biasa digunakan saat membuat DTO(Data Transfer Object)
.
Pada pom.xml sebelumnya kita sudah menambahkan dependency spring-boot-starter-validation
...
org.springframework.boot
spring-boot-starter-validation
Tambahkan anotasi berikut ke dalam models/Post.java
...
public class Post {
...
@Setter
@NotBlank(message = "title cannot empty")
@Pattern(regexp = "/^[\\w\\s]+$/", message = "title only allow AlphaNum Char.")
private String title;
...
}
Buat package baru bernama repositories
sesuaikan dengan Struktur Direktori, lalu buat file interface bernama PostRepository
dengan extends JpaRepository
models/PostRepository.java
package com.ombagoes.hello.repositories;
import com.ombagoes.hello.models.Post;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface PostRepository extends JpaRepository {
List findByPublished(boolean published);
List findByTitleContaining(String title);
}
Kita memiliki method asli :
Ditambahkan dengan method bawaan JpaRepository:
Tanpa menuliskannya di dalam interface.
Buat package baru bernama controllers
sesuaikan dengan Struktur Direktori, lalu buat file class bernama PostController
.
controllers/PostController.java
package com.ombagoes.hello.controllers;
import com.ombagoes.hello.models.Post;
import com.ombagoes.hello.repositories.PostRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@RestController
public class PostController {
@Autowired
PostRepository postRepository;
@GetMapping("/posts")
public ResponseEntity> getAllPosts(@RequestParam(required = false) String title) {
try {
List Posts = new ArrayList();
if (title == null)
Posts.addAll(postRepository.findAll());
else
Posts.addAll(postRepository.findByTitleContaining(title));
if (Posts.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(Posts, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@GetMapping("/posts/{id}")
public ResponseEntity getPostById(@PathVariable("id") long id) {
Optional postData = postRepository.findById(id);
return postData.map(post -> new ResponseEntity<>(post, HttpStatus.OK)).orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
@PostMapping("/posts")
public ResponseEntity> createPost(@RequestBody @Valid Post post, , BindingResult bindingResult) {
try {
if (bindingResult.hasErrors()) {
List exceptionalErrors= bindingResult
.getFieldErrors()
.stream()
.map(x -> x.getDefaultMessage())
.collect(Collectors.toList());
map.put("errors", exceptionalErrors);
map.put("success", false);
return new ResponseEntity<>(map, HttpStatus.BAD_REQUEST);
}
Post _post = postRepository
.save(new Post(post.getTitle(), post.getDescription(), post.isPublished()));
return new ResponseEntity<>(_post, HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PutMapping("/posts/{id}")
public ResponseEntity updatePost(@PathVariable("id") long id, @RequestBody Post post) {
Optional postData = postRepository.findById(id);
if (postData.isPresent()) {
Post _post = postData.get();
_post.setTitle(post.getTitle());
_post.setDescription(post.getDescription());
_post.setPublished(post.isPublished());
return new ResponseEntity<>(postRepository.save(_post), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
@DeleteMapping("/posts/{id}")
public ResponseEntity deletePost(@PathVariable("id") long id) {
try {
postRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@DeleteMapping("/posts")
public ResponseEntity deleteAllPosts() {
try {
postRepository.deleteAll();
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@GetMapping("/posts/published")
public ResponseEntity> findByPublished() {
try {
List posts = postRepository.findByPublished(true);
if (posts.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(posts, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
Methods | Urls | Actions |
---|---|---|
POST | /api/posts | Membuat Post baru |
GET | /api/posts | Mendapatkan semua data Post |
GET | /api/posts/:id | Mendapatkan 1 data Post berdasarkan :id |
PUT | /api/posts/:id | Mengubah1 data Post berdasarkan :id |
DELETE | /api/posts/:id | Menghapus 1 data Post berdasarkan :id |
DELETE | /api/posts | Menghapus semua Post |
GET | /api/posts/published | Mendapatkan semua data Post yang sudah di publish |
GET | /api/posts?title=[keyword] | Mendapatkan Semua data Post berdasarkan keyword |