Menu Bar

Kata Mutiara

"Keberhasilan merupakan tetesan dari jeri-payah perjuangan, luka, pengorbanan dan hal-hal yang mengejutkan. Kegagalan merupakan tetesan dari kemalasan, kebekuan, kelemahan, kehinaan dan kerendahan"

ANIMASI TULISAN BERJALAN

Showing posts with label springboot. Show all posts
Showing posts with label springboot. Show all posts

Tuesday, February 24, 2026

20 Fundamental Spring Boot yang wajib diketahui Pemula Programmer spesialis Java Developer

 

Apa itu Spring Boot

Spring Boot adalah framework berbasis Spring Framework yang mempermudah pembuatan aplikasi Java production-ready dengan:

✅ konfigurasi minimal
✅ embedded server
✅ auto-configuration
✅ siap untuk microservices & cloud


1. Konsep Inti: Dependency Injection (DI) & IoC

🎯 Tujuan

Mengurangi coupling antar class.

Tanpa DI

UserService service = new UserService();

Dengan DI

@Service
public class UserService {}


@Autowired
UserService service;

Spring container akan membuat & mengelola object.

✔ loose coupling
✔ mudah testing
✔ mudah maintain


2. Auto Configuration

Spring Boot otomatis mengkonfigurasi komponen berdasarkan dependency.

Contoh:

Jika ada dependency:

spring-boot-starter-data-jpa

Spring otomatis setup:

✔ DataSource
✔ JPA
✔ Transaction manager

➡ developer tidak perlu konfigurasi manual.


3. Starter Dependencies

Starter = paket dependency siap pakai.

Contoh:

  • spring-boot-starter-web → REST API

  • spring-boot-starter-data-jpa → database

  • spring-boot-starter-security → authentication

  • spring-boot-starter-validation → validation

👉 mempercepat setup project.


4. Embedded Server

Spring Boot menjalankan server internal:

✔ Tomcat (default)
✔ Jetty
✔ Undertow

Menjalankan aplikasi cukup:

java -jar app.jar


5. Struktur Layered Architecture (Best Practice)

Struktur umum:

controller
service
repository
model/entity
dto
config
exception

📌 Controller (API layer)

@RestController
@RequestMapping("/users")
public class UserController {}


📌 Service (business logic)

@Service
public class UserService {}


📌 Repository (data access)

@Repository
public interface UserRepository extends JpaRepository<User, Long> {}

✔ separation of concerns
✔ scalable architecture


 6. REST API Development (Spring MVC)

Digunakan untuk membangun API.

@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
    return userService.getUser(id);
}

Annotation penting:

  • @RestController
  • @GetMapping
  • @PostMapping
  • @RequestBody
  • @PathVariable

7. Configuration Management

Konfigurasi di:

application.yml

Contoh: YAML

server:
    port: 8081

spring:
    datasource:
    url: jdbc:postgresql://localhost:5432/app


Profile Environment

application-dev.yml
application-prod.yml

Aktifkan:

spring.profiles.active=dev

✔ mendukung multi environment.


8. Spring Data JPA (ORM)

Mempermudah akses database.

public interface UserRepository extends JpaRepository<User, Long> {

    List<User> findByName(String name);

}

Tanpa SQL manual.


9. Entity Mapping

@Entity
public class User {
   @Id
   @GeneratedValue
   private Long id;
}

Mapping object → tabel database.


 10.Transaction Management

Digunakan untuk menjaga konsistensi data.

@Transactional
public void transfer() {
// debit
// credit
}

✔ rollback otomatis jika error
✔ penting untuk transaksi finansial

11. Validation

public class UserRequest {

@NotNull
private String name;
}

@PostMapping
public void create(@Valid @RequestBody UserRequest req){}

✔ validasi otomatis request.

12. Exception Handling Global

@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(Exception.class)
public ResponseEntity<?> handle(Exception e){
return ResponseEntity.badRequest().body(e.getMessage());
}
}

✔ response error konsisten
✔ mudah debugging

13. Logging

Menggunakan SLF4J + Logback.

private static final Logger log LoggerFactory.getLogger(UserService.class);
log.info("User created");

✔ monitoring
✔ debugging production

14. Security (Spring Security + JWT)

Digunakan untuk:

✔ authentication
✔ authorization
✔ proteksi API

Biasanya digunakan bersama JWT token.


15. Caching

Menggunakan Redis / in-memory cache.

@Cacheable("users")
public User find(Long id){}

✔ meningkatkan performa

16. Actuator (Monitoring & Health Check)

Dependency:

spring-boot-starter-actuator

Endpoint:

/actuator/health
/actuator/metrics

✔ monitoring aplikasi production.

17. Spring Boot Profiles & Environment

Memisahkan konfigurasi:

✔ dev
✔ staging
✔ production

Sangat penting untuk deployment.


 18. Build & Packaging

Gunakan:

✔ Maven
✔ Gradle

Build:

mvn clean package

Hasil:

app.jar

19. Testing

Unit Test

@SpringBootTest
class UserServiceTest {}

Tools umum:

✔ JUnit
✔ Mockito
✔ Testcontainers

20. Microservices Ready Features

Spring Boot mendukung:

✔ REST communication
✔ service discovery
✔ distributed config
✔ circuit breaker
✔ container deployment

Flow Request di Spring Boot

1️⃣ Client request API
2️⃣ Controller menerima request
3️⃣ Service menjalankan business logic
4️⃣ Repository akses database
5️⃣ Response dikirim kembali


 Kenapa Spring Boot Populer di Enterprise?

✅ cepat dikembangkan
✅ standar industri
✅ scalable & cloud ready
✅ ecosystem besar
✅ production ready

Kesimpulan

Fundamental Spring Boot yang wajib dikuasai:

✔ Dependency Injection & IoC
✔ Auto Configuration & Starter
✔ REST API & MVC
✔ JPA & Transaction
✔ Exception handling & validation
✔ Security & caching
✔ Monitoring & deployment

Tuesday, February 11, 2025

Proyek Sederhana Spring Web JPA+MYSQL+Angular 16

/**/

 1. Backend (Spring Boot)

1.1. Struktur Proyek

danamon-backend
│── pom.xml
│── src/main/java/com/joko
│   ├── controller
│   │   ├── DanamonEmployeeController.java
│   ├── model
│   │   ├── DanamonEmployee.java
│   ├── repository
│   │   ├── DanamonEmployeeRepository.java
│   ├── SpringbootCrudAngular16MysqlApplication.java
│── src/main/resources
│   ├── application.properties

1.2. pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>3.1.1</version>

<relativePath/> <!-- lookup parent from repository -->

</parent>

<groupId>com.joko</groupId>

<artifactId>Springboot-crud-angular16-mysql</artifactId>

<version>0.0.1-SNAPSHOT</version>

<name>Springboot-crud-angular16-mysql</name>

<description>Springboot-crud-angular16-mysql</description>

<properties>

<java.version>17</java.version>

</properties>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-jpa</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-validation</artifactId>

</dependency>


<dependency>

<groupId>com.mysql</groupId>

<artifactId>mysql-connector-j</artifactId>

<scope>runtime</scope>

</dependency>

<dependency>

<groupId>com.fasterxml.jackson.datatype</groupId>

<artifactId>jackson-datatype-jsr310</artifactId>

</dependency>

<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-classic</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

</dependencies>


<build>

<plugins>

<plugin>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-maven-plugin</artifactId>

</plugin>

</plugins>

</build>


</project>

1.3. Model (DanamonEmployee.java)

package com.joko.model;


import jakarta.persistence.Column;

import jakarta.persistence.Entity;

import jakarta.persistence.GeneratedValue;

import jakarta.persistence.GenerationType;

import jakarta.persistence.Id;

import jakarta.persistence.Table;

import jakarta.validation.constraints.NotBlank;

import jakarta.validation.constraints.Size;


@Entity

@Table(name="employees")

public class DanamonEmployee {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

@NotBlank

@Size(max = 120)

@Column(name = "name")

private String name;

@NotBlank

@Size(max = 200)

@Column(name = "address")

private String address;

@NotBlank

@Size(max = 200)

@Column(name = "salary")

private String salary;

@NotBlank

@Size(max = 120)

@Column(name = "job")

private String job;

public DanamonEmployee () {}


public DanamonEmployee(@NotBlank @Size(max = 120) String name, @NotBlank @Size(max = 200) String address,

@NotBlank @Size(max = 200) String salary,@NotBlank @Size(max = 120) String job) {

super();

this.name = name;

this.address = address;

this.salary = salary;

this.job = job;

}



@Override

public String toString() {

return "DanamonEmployee [id=" + id + ", name=" + name + ", address=" + address + ", salary=" + salary

+ ", job="+job+", getId()=" + getId() + ", getName()=" + getName() + ", getAddress()=" + getAddress()

+ ", getSalary()=" + getSalary() +", getJob()="+getJob()+ ", toString()=" + super.toString() + "]";

}




public Long getId() {

return id;

}


public void setId(Long id) {

this.id = id;

}


public String getName() {

return name;

}


public void setName(String name) {

this.name = name;

}


public String getAddress() {

return address;

}


public void setAddress(String address) {

this.address = address;

}


public String getSalary() {

return salary;

}


public void setSalary(String salary) {

this.salary = salary;

}




public String getJob() {

return job;

}




public void setJob(String job) {

this.job = job;

}

}

1.4. Repository (DanamonEmployeeRepository.java)

package com.joko.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import com.joko.model.DanamonEmployee;

public interface DanamonEmployeeRepository extends JpaRepository<DanamonEmployee, Long>{

    List<DanamonEmployee> findByNameContaining(String name);

    @SuppressWarnings("unchecked")
    DanamonEmployee save(DanamonEmployee danamonEmployee);

}

1.5. Controller (DanamonEmployeeController.java)

package com.joko.controller;


import java.util.ArrayList;

import java.util.List;

import java.util.Optional;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.DeleteMapping;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.PutMapping;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

import org.springframework.web.bind.annotation.RestControllerAdvice;


import com.joko.model.DanamonEmployee;

import com.joko.repository.DanamonEmployeeRepository;


@RestController

@RequestMapping("/bdi/api")

public class DanamonEmployeeController {

@Autowired

DanamonEmployeeRepository danamonEmployeeRepository;

private static final Logger logger =LoggerFactory.getLogger(DanamonEmployeeController.class);

@GetMapping("/employee")

public ResponseEntity<List<DanamonEmployee>> getAllDanamonEmployee(@RequestParam(required = false) String name) {

try {

List<DanamonEmployee> danamonEmployees = new ArrayList<DanamonEmployee>();

if (name == null)

danamonEmployeeRepository.findAll().forEach(danamonEmployees::add);

else

danamonEmployeeRepository.findByNameContaining(name).forEach(danamonEmployees::add);


if (danamonEmployees.isEmpty()) {

logger.info(" Get All employee No Content");

return new ResponseEntity<>(HttpStatus.NO_CONTENT);

}

logger.info(" Get All employee succcess");

return new ResponseEntity<>(danamonEmployees, HttpStatus.OK);

} catch (Exception e) {

// TODO: handle exception

logger.error("INTERNAL_SERVER_ERROR Failure Get All employee ",e);

return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);

}

}

@GetMapping("/employee/{id}")

public ResponseEntity<DanamonEmployee> getDanamonEmployeeById(@PathVariable("id") long id) {

Optional<DanamonEmployee> danamonEmployees = danamonEmployeeRepository.findById(id);


if (danamonEmployees.isPresent()) {

logger.info(" Get employee by Id success");

return new ResponseEntity<>(danamonEmployees.get(), HttpStatus.OK);

} else {

logger.error(" Not Found Fail to Get employee by id");

return new ResponseEntity<>(HttpStatus.NOT_FOUND);

}

}

@PostMapping("/employee")

public ResponseEntity<DanamonEmployee> createDanamonEmployee(@RequestBody DanamonEmployee danamonEmployee) {

try {

DanamonEmployee danamonemployee = danamonEmployeeRepository.save(new DanamonEmployee(danamonEmployee.getName(), danamonEmployee.getAddress(),danamonEmployee.getSalary(),danamonEmployee.getJob()));

logger.info(" Successfully added new employee: ID={}, Name={}, Address={}, Salary={}, Job={}",

danamonemployee.getId(),

danamonemployee.getName(),

danamonemployee.getAddress(),

danamonemployee.getSalary(),

danamonemployee.getJob()

);

return new ResponseEntity<>(danamonemployee, HttpStatus.CREATED);

} catch (Exception e) {

logger.error(" INTERNAL_SERVER_ERROR Fail adding new employee",e);

return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);

}

}

@PutMapping("/employee/{id}")

public ResponseEntity<DanamonEmployee> updateDanamonEmployee(@PathVariable("id") long id, @RequestBody DanamonEmployee danamonEmployee) {

Optional<DanamonEmployee> employeedata = danamonEmployeeRepository.findById(id);


if (employeedata.isPresent()) {

DanamonEmployee emp = employeedata.get();

emp.setName(danamonEmployee.getName());

emp.setAddress(danamonEmployee.getAddress());

emp.setSalary(danamonEmployee.getSalary());

emp.setJob(danamonEmployee.getJob());

logger.info(" You have been success updating employee by id");

return new ResponseEntity<>(danamonEmployeeRepository.save(emp), HttpStatus.OK);

} else {

logger.error(" NOT_FOUND Fail updating employee by id");

return new ResponseEntity<>(HttpStatus.NOT_FOUND);

}

}

@DeleteMapping("/employee/{id}")

public ResponseEntity<HttpStatus> deleteDanamonEmployee(@PathVariable("id") long id) {

try {

danamonEmployeeRepository.deleteById(id);

logger.info(" You have been success deleting employee by id");

return new ResponseEntity<>(HttpStatus.NO_CONTENT);

} catch (Exception e) {

logger.error(" INTERNAL_SERVER_ERROR, error delete employee by id",e);

return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);

}

}

@DeleteMapping("/employee")

public ResponseEntity<HttpStatus> deleteAllDanamonEmployee() {

try {

danamonEmployeeRepository.deleteAll();

logger.info(" You have been success deleting all employee");

return new ResponseEntity<>(HttpStatus.NO_CONTENT);

} catch (Exception e) {

logger.error(" INTERNAL_SERVER_ERROR, error delete All employee ",e);

return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);

}

}

}



@RestControllerAdvice

class GlobalExceptionHandler{

private static final Logger logger =LoggerFactory.getLogger(GlobalExceptionHandler.class);

public ResponseEntity<String> handleRunTimeException(RuntimeException e){

logger.error("Exception Occur:{}",e.getMessage());

return ResponseEntity.badRequest().body(e.getMessage());

}

}

1.6. SpringbootCrudAngular16MysqlApplication.java

package com.joko;


import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication

public class SpringbootCrudAngular16MysqlApplication {


public static void main(String[] args) {

SpringApplication.run(SpringbootCrudAngular16MysqlApplication.class, args);

}


}

1.6. Konfigurasi Database (application.properties)

server.port=8087


spring.datasource.url= jdbc:mysql://localhost:3306/testdb?useSSL=false

spring.datasource.username= root

spring.datasource.password= root


spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL8Dialect

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.hibernate.ddl-auto= update

spring.jpa.properties.hibernate.globally_quoted_identifiers=true


2. Frontend (Angular 16)

2.1. Install Angular CLI dan Buat Proyek

npm install -g @angular/cli
ng new danamon-frontend
cd danamon-frontend
ng add @angular/material
2.2. Buat Service untuk API
ng generate service services/employee
typescript
// src/app/services/employee.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class EmployeeService {
  private baseUrl = 'http://localhost:8087/bdi/api/employee';

  constructor(private http: HttpClient) {}

  getEmployees(): Observable<any> {
    return this.http.get(this.baseUrl);
  }

  getEmployeeById(id: number): Observable<any> {
    return this.http.get(`${this.baseUrl}/${id}`);
  }

  createEmployee(employee: any): Observable<any> {
    return this.http.post(this.baseUrl, employee);
  }

  updateEmployee(id: number, employee: any): Observable<any> {
    return this.http.put(`${this.baseUrl}/${id}`, employee);
  }

  deleteEmployee(id: number): Observable<any> {
    return this.http.delete(`${this.baseUrl}/${id}`);
  }

  deleteAllEmployees(): Observable<any> {
    return this.http.delete(this.baseUrl);
  }
}


2.3. Buat Model untuk Employee
ng generate interface models/employee
typescript
// src/app/models/employee.ts
export interface Employee {
    id?: number;
    name: string;
    address: string;
    salary: string;
    job: string;
  }
 

2.4. Buat Component untuk Employee CRUD
ng generate component components/employee-list
ng generate component components/employee-form

2.5. Implementasi Employee List Component
typescript
// src/app/components/employee-list/employee-list.component.ts
import { Component, OnInit } from '@angular/core';
import { EmployeeService } from '../../services/employee.service';
import { Employee } from '../../models/employee';

@Component({
  selector: 'app-employee-list',
  templateUrl: './employee-list.component.html',
  styleUrls: ['./employee-list.component.css']
})
export class EmployeeListComponent implements OnInit {
  employees: Employee[] = [];

  constructor(private employeeService: EmployeeService) {}

  ngOnInit(): void {
    this.loadEmployees();
  }

  loadEmployees(): void {
    this.employeeService.getEmployees().subscribe(data => {
      this.employees = data;
    });
  }

  deleteEmployee(id: number): void {
    this.employeeService.deleteEmployee(id).subscribe(() => {
      this.loadEmployees();
    });
  }

  deleteAllEmployees(): void {
    this.employeeService.deleteAllEmployees().subscribe(() => {
      this.loadEmployees();
    });
  }
}


2.6. Template HTML untuk Employee List
<!-- src/app/components/employee-list/employee-list.component.html -->
<div class="container">
  <h2>Employee List</h2>
  <button (click)="deleteAllEmployees()" class="btn btn-danger">Delete All</button>
  <table class="table table-bordered mt-3">
    <thead>
      <tr>
        <th>ID</th>
        <th>Name</th>
        <th>Address</th>
        <th>Salary</th>
        <th>Job</th>
        <th>Actions</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let employee of employees">
        <td>{{ employee.id }}</td>
        <td>{{ employee.name }}</td>
        <td>{{ employee.address }}</td>
        <td>{{ employee.salary }}</td>
        <td>{{ employee.job }}</td>
        <td>
          <button (click)="deleteEmployee(employee.id!)" class="btn btn-danger">Delete</button>
        </td>
      </tr>
    </tbody>
  </table>
</div>


2.7. Implementasi Employee Form Component
typescript
// src/app/components/employee-form/employee-form.component.ts
import { Component } from '@angular/core';
import { EmployeeService } from '../../services/employee.service';
import { Employee } from '../../models/employee';

@Component({
  selector: 'app-employee-form',
  templateUrl: './employee-form.component.html',
  styleUrls: ['./employee-form.component.css']
})
export class EmployeeFormComponent {
  employee: Employee = { name: '', address: '', salary: '', job: '' };

  constructor(private employeeService: EmployeeService) {}

  saveEmployee(): void {
    this.employeeService.createEmployee(this.employee).subscribe(() => {
      alert('Employee created successfully!');
      this.employee = { name: '', address: '', salary: '', job: '' };
    });
  }
}


2.8. Template HTML untuk Employee Form
<!-- src/app/components/employee-form/employee-form.component.html -->
<div class="container">
  <h2>Create Employee</h2>
  <form (ngSubmit)="saveEmployee()">
    <div class="form-group">
      <label>Name</label>
      <input type="text" class="form-control" [(ngModel)]="employee.name" name="name" required>
    </div>
    <div class="form-group">
      <label>Address</label>
      <input type="text" class="form-control" [(ngModel)]="employee.address" name="address" required>
    </div>
    <div class="form-group">
      <label>Salary</label>
      <input type="text" class="form-control" [(ngModel)]="employee.salary" name="salary" required>
    </div>
    <div class="form-group">
      <label>Job</label>
      <input type="text" class="form-control" [(ngModel)]="employee.job" name="job" required>
    </div>
    <button type="submit" class="btn btn-primary mt-3">Save</button>
  </form>
</div>


2.9. Routing di Angular

Tambahkan route untuk komponen Employee List dan Employee Form di app-routing.module.ts

// src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { EmployeeListComponent } from './components/employee-list/employee-list.component';
import { EmployeeFormComponent } from './components/employee-form/employee-form.component';

const routes: Routes = [
  { path: 'employees', component: EmployeeListComponent },
  { path: 'add-employee', component: EmployeeFormComponent },
  { path: '', redirectTo: '/employees', pathMatch: 'full' }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

2.10. Tambahkan Navbar di app.component.html

<!-- src/app/app.component.html -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
  <div class="container">
    <a class="navbar-brand" href="/">Danamon Employee Management</a>
    <div class="collapse navbar-collapse">
      <ul class="navbar-nav ml-auto">
        <li class="nav-item">
          <a class="nav-link" routerLink="/employees">Employees</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" routerLink="/add-employee">Add Employee</a>
        </li>
      </ul>
    </div>
  </div>
</nav>
<div class="container mt-4">
  <router-outlet></router-outlet>
</div>


3. Jalankan Aplikasi

3.1. Jalankan Backend (Spring Boot)

Pastikan MySQL berjalan, lalu jalankan backend dengan:

mvn spring-boot:run

Atau menggunakan IntelliJ IDEA atau Eclipse, jalankan DanamonBackendApplication.

3.2. Jalankan Frontend (Angular)

ng serve --open

Ini akan membuka browser di http://localhost:4200/.

4. Hasil Akhir

  • Halaman utama akan menampilkan daftar karyawan.
  • Halaman "Add Employee" akan memungkinkan pengguna untuk menambahkan karyawan baru.
  • Tombol "Delete" akan menghapus karyawan dari database.

5. Kesimpulan

✅ Spring Boot + JPA + MySQL menangani backend API untuk CRUD karyawan.
✅ Angular 16 digunakan untuk frontend, memungkinkan user melihat dan mengelola data karyawan.
✅ Bootstrap dan Angular Material membantu dalam styling UI agar lebih rapi.


iklan

iklan