Angular 12 Kotlin SpringBoot MySQL CRUD Example

In this tutorial, we show you Angular 12 Http Client & Spring Boot Server example that uses Spring JPA to do CRUD with MySQL and Angular 12 as a front-end technology to make request and receive response.

Related Posts:
How to use Spring JPA MySQL | Spring Boot
Spring JPA + MySQL + AngularJS example | Spring Boot
Spring JPA Hibernate Many to Many – SpringBoot + PostgreSQL
Spring JPA Hibernate One to Many Relationship – SpringBoot + MySQL
Spring Boot + React Redux + MySQL CRUD example
Kotlin + SpringBoot JPA + MySQL- Save/retrieve Files/Images with @Lob annotation
Spring Security – JDBC Authentication – SpringBoot + MySQL + Bootstrap

Related pages:

I. Technologies

– Java 1.8
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.4.RELEASE
– Spring Boot: 2.0.3.RELEASE
– Angular 12
– RxJS 6

II. Overview

kotlin-spring-boot-angular-6-crud-httpclient-spring-rest-api-data-mysql-crud-spring-jpa + angular-http-service-architecture

1. Spring Boot Server

kotlin-spring-boot-angular-6-crud-httpclient-spring-rest-api-data-mysql-crud-spring-jpa + spring-server-architecture

2. Angular 12 Client

kotlin-spring-boot-angular-6-crud-httpclient-spring-rest-api-data-mysql-crud-spring-jpa + angular-client-architecture

III. Practice

1. Project Structure

1.1 Spring Boot Server

Angular-6-Crud-HttpClient-Kotlin-SpringBoot-MySQL-CRUD-Spring-JPA +Kotlin-SpringBoot-project

Customer class corresponds to entity and table customer.
CustomerRepository is an interface extends CrudRepository, will be autowired in CustomerController for implementing repository methods and custom finder methods.
CustomerController is a REST Controller which has request mapping methods for RESTful requests such as: getAllCustomers, postCustomer, deleteCustomer, deleteAllCustomers, findByAge, updateCustomer.
– Configuration for Spring Datasource and Spring JPA properties in application.properties
Dependencies for Spring Boot and MySQL in pom.xml

1.2 Angular 12 Client

kotlin-spring-boot-angular-6-crud-httpclient-spring-rest-api-data-mysql-crud-spring-jpa + angular-client-structure

In this example, we focus on:
– 4 components: customers-list, customer-details, create-customer, search-customer.
– 3 modules: FormsModule, HttpClientModule, AppRoutingModule.
customer.ts: class Customer (id, firstName, lastName)
customer.service.ts: Service for Http Client methods

2. How to do

2.1 Spring Boot Server

2.1.1 Dependency
<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>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<scope>runtime</scope>
</dependency>
2.1.2 Customer – Data Model

model/Customer.kt


package com.ozenero.springrest.mysql.model

import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
 
@Entity
class Customer(
		var name: String,
		var age: Int,
		var active: Boolean = false,
		@Id @GeneratedValue(strategy = GenerationType.AUTO)
		val id: Long = -1) {
	
	private constructor() : this("", -1)
	
	override fun toString(): String{
		return "Customer [id= + ${this.id} + , name= + ${this.name} + , age= + ${this.age} + , active= + ${this.active} + ]";
	}
}
2.1.3 JPA Repository

repo/CustomerRepository.kt


package com.ozenero.springrest.mysql.repo

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.ozenero.springrest.mysql.model.Customer;

@Repository
interface CustomerRepository : CrudRepository {
	fun findByAge(age: Int): Iterable
}
2.1.4 REST Controller

controller/CustomerController.kt


package com.ozenero.springrest.mysql.controller

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
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.RestController;
 
import com.ozenero.springrest.mysql.repo.CustomerRepository
import com.ozenero.springrest.mysql.model.Customer

@CrossOrigin(origins = arrayOf("http://localhost:4200"))
@RestController
@RequestMapping("/api")
class CustomerController {
	@Autowired
	lateinit var repository: CustomerRepository
	
	@GetMapping("/customers")
	fun findAll() = repository.findAll()
	
	@PostMapping("/customers/create")
	fun postCustomer(@RequestBody customer: Customer): Customer{
		return repository.save( Customer(customer.name, customer.age));
	}
	
	@DeleteMapping("/customers/{id}")
	fun deleteCustomer(@PathVariable("id") id: Long): ResponseEntity{
		println("Delete Customer with ID = " + id + "...");
		repository.deleteById(id);
		return ResponseEntity("Customer has been deleted!", HttpStatus.OK);
	}
	
	@DeleteMapping("/customers/delete")
	fun deleteAllCustomers(): ResponseEntity{
		println("Delete All Customers...");
		repository.deleteAll();
		return ResponseEntity("All customers have been deleted!", HttpStatus.OK);
	}
	
	@GetMapping("customers/age/{age}")
	fun findByAge(@PathVariable age: Int) = repository.findByAge(age);
 
	@PutMapping("/customers/{id}")
	fun updateCustomer(@PathVariable("id") id: Long, @RequestBody customer: Customer):  ResponseEntity {
		println("Update Customer with ID = " + id + "...");
 
		var customerData = repository.findById(id);
 
		if (customerData.isPresent()) {
			var _customer = customerData.get();
			_customer.name = customer.name;
			_customer.age = customer.age;
			_customer.active = customer.active;
			return ResponseEntity(repository.save(_customer), HttpStatus.OK);
		} else {
			return ResponseEntity(HttpStatus.NOT_FOUND);
		}
	}
}
2.1.5 Configuration for Spring Datasource & JPA properties

application.properties


spring.datasource.url=jdbc:mysql://localhost:3306/testdb?useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.jpa.generate-ddl=true

2.2 Angular 12 Client

2.2.0 Create Service & Components

Run commands below:
ng g s Customer
ng g c CreateCustomer
ng g c CustomerDetails
ng g c CustomersList
ng g c SearchCustomers
On each Component selector, delete app- prefix, then change tslint.json rules"component-selector" to false.

2.2.1 Model

customer.ts


export class Customer {
    id: number;
    name: string;
    age: number;
    active: boolean;
}
2.2.2 CustomerService

customer.service.ts


import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class CustomerService {

  private baseUrl = 'http://localhost:8080/api/customers';

  constructor(private http: HttpClient) { }

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

  createCustomer(customer: Object): Observable<Object> {
    return this.http.post(`${this.baseUrl}` + `/create`, customer);
  }

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

  deleteCustomer(id: number): Observable<any> {
    return this.http.delete(`${this.baseUrl}/${id}`, { responseType: 'text' });
  }

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

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

  deleteAll(): Observable<any> {
    return this.http.delete(`${this.baseUrl}` + `/delete`, { responseType: 'text' });
  }
}
2.2.3 Components

– CustomerDetailsComponent:
customer-details/customer-details.component.ts


import { Component, OnInit, Input } from '@angular/core';
import { CustomerService } from '../customer.service';
import { Customer } from '../customer';

import { CustomersListComponent } from '../customers-list/customers-list.component';

@Component({
  selector: 'customer-details',
  templateUrl: './customer-details.component.html',
  styleUrls: ['./customer-details.component.css']
})
export class CustomerDetailsComponent implements OnInit {

  @Input() customer: Customer;

  constructor(private customerService: CustomerService, private listComponent: CustomersListComponent) { }

  ngOnInit() {
  }

  updateActive(isActive: boolean) {
    this.customerService.updateCustomer(this.customer.id,
      { name: this.customer.name, age: this.customer.age, active: isActive })
      .subscribe(
        data => {
          console.log(data);
          this.customer = data as Customer;
        },
        error => console.log(error));
  }

  deleteCustomer() {
    this.customerService.deleteCustomer(this.customer.id)
      .subscribe(
        data => {
          console.log(data);
          this.listComponent.reloadData();
        },
        error => console.log(error));
  }
}

customer-details/customer-details.component.html

<div *ngIf="customer">
  <div>
    <label>Name: </label> {{customer.name}}
  </div>
  <div>
    <label>Age: </label> {{customer.age}}
  </div>
  <div>
    <label>Active: </label> {{customer.active}}
  </div>

  <span class="button is-small btn-primary" *ngIf='customer.active' (click)='updateActive(false)'>Inactive</span>

  <span class="button is-small btn-primary" *ngIf='!customer.active' (click)='updateActive(true)'>Active</span>

  <span class="button is-small btn-danger" (click)='deleteCustomer()'>Delete</span>

  <hr/>
</div>

– CustomersListComponent:
customers-list/customers-list.component.ts


import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';

import { CustomerService } from '../customer.service';
import { Customer } from '../customer';

@Component({
  selector: 'customers-list',
  templateUrl: './customers-list.component.html',
  styleUrls: ['./customers-list.component.css']
})
export class CustomersListComponent implements OnInit {

  customers: Observable;

  constructor(private customerService: CustomerService) { }

  ngOnInit() {
    this.reloadData();
  }

  deleteCustomers() {
    this.customerService.deleteAll()
      .subscribe(
        data => {
          console.log(data);
          this.reloadData();
        },
        error => console.log('ERROR: ' + error));
  }

  reloadData() {
    this.customers = this.customerService.getCustomersList();
  }
}

customers-list/customers-list.component.html

<h1>Customers</h1>

<div *ngFor="let customer of customers | async" style="width: 300px;">
  <customer-details [customer]='customer'></customer-details>
</div>

<div>
  <button type="button" class="button btn-danger" (click)='deleteCustomers()'>Delete All</button>
</div>

– CreateCustomerComponent:
create-customer/create-customer.component.ts


import { Component, OnInit } from '@angular/core';

import { Customer } from '../customer';
import { CustomerService } from '../customer.service';

@Component({
  selector: 'create-customer',
  templateUrl: './create-customer.component.html',
  styleUrls: ['./create-customer.component.css']
})
export class CreateCustomerComponent implements OnInit {

  customer: Customer = new Customer();
  submitted = false;

  constructor(private customerService: CustomerService) { }

  ngOnInit() {
  }

  newCustomer(): void {
    this.submitted = false;
    this.customer = new Customer();
  }

  save() {
    this.customerService.createCustomer(this.customer)
      .subscribe(data => console.log(data), error => console.log(error));
    this.customer = new Customer();
  }

  onSubmit() {
    this.submitted = true;
    this.save();
  }
}

create-customer/create-customer.component.html

<h3>Create Customer</h3>
<div [hidden]="submitted" style="width: 300px;">
  <form (ngSubmit)="onSubmit()">
    <div class="form-group">
      <label for="name">Name</label>
      <input type="text" class="form-control" id="name" required [(ngModel)]="customer.name" name="name">
    </div>

    <div class="form-group">
      <label for="age">Age</label>
      <input type="text" class="form-control" id="age" required [(ngModel)]="customer.age" name="age">
    </div>

    <button type="submit" class="btn btn-success">Submit</button>
  </form>
</div>

<div [hidden]="!submitted">
  <h4>You submitted successfully!</h4>
  <button class="btn btn-success" (click)="newCustomer()">Add</button>
</div>

– SearchCustomersComponent:
search-customers/search-customers.component.ts


import { Component, OnInit } from '@angular/core';
import { Customer } from '../customer';
import { CustomerService } from '../customer.service';

@Component({
  selector: 'search-customers',
  templateUrl: './search-customers.component.html',
  styleUrls: ['./search-customers.component.css']
})
export class SearchCustomersComponent implements OnInit {

  age: number;
  customers: Customer[];

  constructor(private dataService: CustomerService) { }

  ngOnInit() {
    this.age = 0;
  }

  private searchCustomers() {
    this.dataService.getCustomersByAge(this.age)
      .subscribe(customers => this.customers = customers);
  }

  onSubmit() {
    this.searchCustomers();
  }
}

search-customers/search-customers.component.html

<h3>Find By Age</h3>
<div style="width: 300px;">
  <form (ngSubmit)="onSubmit()">
    <div class="form-group">
      <label for="lastname">Age</label>
      <input type="text" class="form-control" id="age" required [(ngModel)]="age" name="age">
    </div>

    <div class="btn-group">
      <button type="submit" class="btn btn-success">Submit</button>
    </div>
  </form>
</div>
<ul>
  <li *ngFor="let customer of customers">
    <h4>{{customer.id}} - {{customer.name}} {{customer.age}}</h4>
  </li>
</ul>
2.2.4 AppRoutingModule

app-routing.module.ts


import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CustomersListComponent } from './customers-list/customers-list.component';
import { CreateCustomerComponent } from './create-customer/create-customer.component';
import { SearchCustomersComponent } from './search-customers/search-customers.component';

const routes: Routes = [
    { path: '', redirectTo: 'customer', pathMatch: 'full' },
    { path: 'customer', component: CustomersListComponent },
    { path: 'add', component: CreateCustomerComponent },
    { path: 'findbyage', component: SearchCustomersComponent },
];

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

export class AppRoutingModule { }

And AppComponent HTML for routing:
app.component.html

<div style="padding: 20px;">
  <h1 style="color: blue">{{title}}</h1>
  <h3>{{description}}</h3>
  <nav>
    <a routerLink="customer" class="btn btn-primary active" role="button" routerLinkActive="active">Customers</a>
    <a routerLink="add" class="btn btn-primary active" role="button" routerLinkActive="active">Add</a>
    <a routerLink="findbyage" class="btn btn-primary active" role="button" routerLinkActive="active">Search</a>
  </nav>
  <router-outlet></router-outlet>
</div>
2.2.5 AppModule

app.module.ts


import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { CreateCustomerComponent } from './create-customer/create-customer.component';
import { CustomerDetailsComponent } from './customer-details/customer-details.component';
import { CustomersListComponent } from './customers-list/customers-list.component';
import { SearchCustomersComponent } from './search-customers/search-customers.component';
import { AppRoutingModule } from './app-routing.module';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent,
    CreateCustomerComponent,
    CustomerDetailsComponent,
    CustomersListComponent,
    SearchCustomersComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

3. Run & Check Result

Build and Run Spring Boot project with commandlines: mvn clean install and mvn spring-boot:run.
– Run the Angular App with command: ng serve.

– Open browser for url http://localhost:4200/:
Add Customer:

kotlin-spring-boot-angular-6-crud-httpclient-spring-rest-api-data-mysql-crud-spring-jpa-add-customer

Show Customers:

kotlin-spring-boot-angular-6-crud-httpclient-spring-rest-api-data-mysql-crud-spring-jpa + show-customers

Click on Active button to update Customer status:

kotlin-spring-boot-angular-6-crud-httpclient-spring-rest-api-data-mysql-crud-spring-jpa +update-customers

Search Customers by Age:

kotlin-spring-boot-angular-6-crud-httpclient-spring-rest-api-data-mysql-crud-spring-jpa + search-customers

Delete a Customer:

kotlin-spring-boot-angular-6-crud-httpclient-spring-rest-api-data-mysql-crud-spring-jpa + delete-customer

Delete All Customers:

kotlin-spring-boot-angular-6-crud-httpclient-spring-rest-api-data-mysql-crud-spring-jpa + delete-all-customers

IV. Source Code

SpringKotlinRestAPIsMySQL
Angular6SpringBoot-Client

39 thoughts on “Angular 12 Kotlin SpringBoot MySQL CRUD Example”

  1. I adore your blog post.. pleasant colours & style. Did an individual design this web site your self or maybe did you actually hire someone to make it work available for you? Plz respond as I!|m planning to pattern my own website along with would wish to learn in which oughout became that out of. thanks a lot

  2. This is the correct weblog for everyone who has to search for away relating to this topic. You observe a great deal its practically time consuming to be able to claim with you (not that I actually would want?HaHa). An individual favorably put a whole new spin on a subject thats been discussed for a long time. Great things, merely fantastic!

  3. Whats rough now is how the systematic approach to life is not fixed. You know what I mean? It is nearly as if we crawl through the life experience with blinders on, not appreciating the true meaning of our own lives.

  4. 679558 936777Speedily and easily build your web traffic and PR, which provides Internet internet site visitors to add your page to any social bookmarking site. 935782

  5. Great blog here! Also your site quite a bit up fast! What
    web host are you the use of? Can I get your affiliate link to your host?
    I want my website loaded up as quickly as yours lol

  6. I do not know if it’s just me or if everyone else experiencing issues with your blog.
    It seems like some of the text on your content are running off the
    screen. Can someone else please comment and let me know if this is happening to them as well?
    This may be a issue with my web browser because I’ve had this happen previously.
    Kudos

  7. My spouse and I stumbled over here from a different
    web address and thought I should check things out.
    I like what I see so now i am following you. Look forward to looking into
    your web page for a second time.

  8. You actually make it seem really easy with your presentation however I find this matter to be actually
    something which I believe I’d never understand. It kind of feels
    too complex and very broad for me. I am having a look forward for your next put up, I will attempt to get the hang of it!

  9. I know this if off topic but I’m looking into starting my own weblog and was curious what all is needed to get set up?
    I’m assuming having a blog like yours would cost a pretty penny?
    I’m not very internet smart so I’m not 100% certain. Any recommendations or advice
    would be greatly appreciated. Kudos

  10. It’s really a nice and useful piece of information. I am satisfied that you just shared this helpful info with
    us. Please stay us informed like this. Thanks for sharing.

  11. Hi would you mind stating which blog platform you’re using?
    I’m going to start my own blog soon but I’m having a difficult time choosing between BlogEngine/Wordpress/B2evolution and Drupal.
    The reason I ask is because your design and style seems different then most blogs and I’m looking for something unique.
    P.S My apologies for being off-topic but I had to ask!

  12. Write more, thats all I have to say. Literally, it seems as though
    you relied on the video to make your point.
    You clearly know what youre talking about, why waste your intelligence on just
    posting videos to your weblog when you could be giving us something informative to
    read?

  13. Hey there, You’ve done an excellent job.
    I will definitely digg it and personally suggest to my friends.
    I am confident they will be benefited from this website.

  14. My brother recommended I may like this website. He used to be entirely right.
    This publish actually made my day. You can not believe just how much time I
    had spent for this info! Thank you!

  15. Woah! I’m really digging the template/theme of this blog.
    It’s simple, yet effective. A lot of times
    it’s hard to get that “perfect balance” between superb usability
    and appearance. I must say that you’ve done a amazing job with this.
    Also, the blog loads extremely quick for me on Firefox.
    Exceptional Blog!

  16. Hi there would you mind letting me know which hosting company you’re working with?
    I’ve loaded your blog in 3 different web browsers and I must say this
    blog loads a lot quicker then most. Can you suggest a good web hosting provider at a fair
    price? Cheers, I appreciate it!

  17. I enjoy what you guys tend to be up too. This kind of clever work
    and exposure! Keep up the amazing works guys I’ve incorporated you guys to my personal blogroll.

  18. Hello i am kavin, its my first occasion to commenting
    anywhere, when i read this post i thought i could also make comment due to
    this good piece of writing.

  19. Hi! I know this is kinda off topic but I’d figured I’d ask.
    Would you be interested in exchanging links or maybe guest writing
    a blog post or vice-versa? My website goes over a lot of the same subjects as yours and I feel we could
    greatly benefit from each other. If you happen to be interested feel free
    to send me an e-mail. I look forward to hearing from
    you! Terrific blog by the way!

  20. If you are going for finest contents like myself, just pay
    a quick visit this site every day since it provides quality contents,
    thanks

  21. Hello are using WordPress for your site platform? I’m new to the blog world but I’m trying to get started and create my own. Do you need any coding knowledge to make your
    own blog? Any help would be really appreciated!

  22. What’s Taking place i’m new to this, I stumbled upon this I’ve discovered
    It positively helpful and it has aided me out loads.

    I’m hoping to contribute & help other users
    like its aided me. Great job.

  23. When I initially commented I clicked the “Notify me when new comments are added” checkbox and now each time
    a comment is added I get several emails with the same comment.
    Is there any way you can remove me from that service?
    Thanks!

  24. I know this if off topic but I’m looking into starting my own weblog and was
    wondering what all is required to get set up? I’m assuming
    having a blog like yours would cost a pretty penny?

    I’m not very web savvy so I’m not 100% sure. Any tips or
    advice would be greatly appreciated. Appreciate it

Leave a Reply

Your email address will not be published. Required fields are marked *