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 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