Tutorial: Vue.js SpringBoot CRUD MariaDB Example | Spring Data JPA + REST + MariaDB CRUD
In this Vue.js SpringBoot tutorial, we show you Vue.js Http Client & Spring Boot Server example that uses Spring JPA to do CRUD with MariaDB and Vue.js as a front-end technology to make request and receive response.
Related Posts:
– MariaDB – How to use Spring JPA MariaDB | Spring Boot
– Vue Router example – with Nav Bar, Dynamic Route & Nested Routes
– Reactjs JWT Authentication Example
Technologies – Vuejs SpringBoot MariaDB
– Java 1.8
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.4.RELEASE
– Spring Boot: 2.0.5.RELEASE
– Vue 2.5.17
– Vue Router 3
– Axios 0.18.0
Overview – Vue.js Spring Boot CRUD MariaDB Example
This is full-stack Architecture:

Demo
1. Implement Spring Boot CRUD MariaDB Server

2. Vue.js RestAPI CRUD Client

Practice – Vue.js Spring Boot CRUD MariaDB Example
1. Spring Boot Server
– 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
, findByAge
, updateCustomer
.
– Configuration for Spring Datasource and Spring JPA properties in application.properties
– Dependencies for Spring Boot and MariaDB in pom.xml
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>org.mariadb.jdbc</groupId> <artifactId>mariadb-java-client</artifactId> </dependency>
1.2 Data Model
model/Customer.java
package com.ozenero.spring.restapi.mariadb.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "customer") public class Customer { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @Column(name = "name") private String name; @Column(name = "age") private int age; @Column(name = "active") private boolean active; public Customer() { } public Customer(String name, int age) { this.name = name; this.age = age; this.active = false; } public long getId() { return id; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } public void setAge(int age) { this.age = age; } public int getAge() { return this.age; } public boolean isActive() { return active; } public void setActive(boolean active) { this.active = active; } @Override public String toString() { return "Customer [id=" + id + ", name=" + name + ", age=" + age + ", active=" + active + "]"; } }
1.3 JPA Repository
repo/CustomerRepository.java
package com.ozenero.spring.restapi.mariadb.repo; import java.util.List; import org.springframework.data.repository.CrudRepository; import com.ozenero.spring.restapi.mariadb.model.Customer; public interface CustomerRepository extends CrudRepository{ List findByAge(int age); }
1.4 REST Controller
controller/CustomerController.java
package com.ozenero.spring.restapi.mariadb.controller; import java.util.ArrayList; import java.util.List; import java.util.Optional; 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.spring.restapi.mariadb.model.Customer; import com.ozenero.spring.restapi.mariadb.repo.CustomerRepository; @CrossOrigin(origins = "http://localhost:4200") @RestController @RequestMapping("/api") public class CustomerController { @Autowired CustomerRepository repository; @GetMapping("/customers") public ListgetAllCustomers() { System.out.println("Get all Customers..."); List customers = new ArrayList<>(); repository.findAll().forEach(customers::add); return customers; } @PostMapping("/customer") public Customer postCustomer(@RequestBody Customer customer) { Customer _customer = repository.save(new Customer(customer.getName(), customer.getAge())); return _customer; } @DeleteMapping("/customer/{id}") public ResponseEntity deleteCustomer(@PathVariable("id") long id) { System.out.println("Delete Customer with ID = " + id + "..."); repository.deleteById(id); return new ResponseEntity<>("Customer has been deleted!", HttpStatus.OK); } @GetMapping("customers/age/{age}") public List findByAge(@PathVariable int age) { List customers = repository.findByAge(age); return customers; } @PutMapping("/customer/{id}") public ResponseEntity updateCustomer(@PathVariable("id") long id, @RequestBody Customer customer) { System.out.println("Update Customer with ID = " + id + "..."); Optional customerData = repository.findById(id); if (customerData.isPresent()) { Customer _customer = customerData.get(); _customer.setName(customer.getName()); _customer.setAge(customer.getAge()); _customer.setActive(customer.isActive()); return new ResponseEntity<>(repository.save(_customer), HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } } }
1.5 Configuration for Spring Datasource & JPA properties
application.properties
spring.datasource.url=jdbc:mariadb://localhost:3306/testdb spring.datasource.username=root spring.datasource.password=123456 spring.jpa.generate-ddl=true
2. Vue.js Client
– package.json with 3 main modules: vue
, vue-router
, axios
.
– 4 components: CustomersList, Customer, AddCustomer, SearchCustomer.
– router.js defines routes
, each route has a path and maps to a component.
– http-common.js initializes HTTP Client with baseUrl
and headers
for axios HTTP methods.
– vue.config.js configures port
for Vue App.
For more details about how to use Vue Router in this example, please visit:
Vue Router example – with Nav Bar, Dynamic Route & Nested Routes
2.0 Setup Vue Project & Router
Init Project
Point cmd to the folder you want to save Project folder, run command:
vue create vue-springboot
You will see 2 options, choose default:
Add Vue Router to Project
– Run command: npm install vue-router
.
– Import router
to src/main.js:
import Vue from "vue"; import App from "./App.vue"; import router from './router' Vue.config.productionTip = false; new Vue({ router, // inject the router to make whole app router-aware render: h => h(App) }).$mount("#app");
Define Routes
src/router.js:
import Vue from "vue"; import Router from "vue-router"; import CustomersList from "./components/CustomersList.vue"; import AddCustomer from "./components/AddCustomer.vue"; import SearchCustomers from "./components/SearchCustomers.vue"; import Customer from "./components/Customer.vue"; Vue.use(Router); export default new Router({ mode: "history", routes: [ { path: "/", name: "customers", alias: "/customer", component: CustomersList, children: [ { path: "/customer/:id", name: "customer-details", component: Customer, props: true } ] }, { path: "/add", name: "add", component: AddCustomer }, { path: "/search", name: "search", component: SearchCustomers } ] });
App template with Navbar and router-view
src/App.vue:
<template> <div id="app" class="container-fluid"> <div class="site-info"> <h1>ozenero</h1> <h2>Vue SpringBoot example</h2> </div> <nav> <router-link class="btn btn-primary" to="/">Customers</router-link> <router-link class="btn btn-primary" to="/add">Add</router-link> <router-link class="btn btn-primary" to="/search">Search</router-link> </nav> <br/> <router-view/> </div> </template> <script> export default { name: "app" }; </script> <style> .site-info { color: blue; margin-bottom: 20px; } .btn-primary { margin-right: 5px; } .container-fluid { text-align: center; } </style>
2.1 Initialize HTTP Client
Install axios with command: npm install axios
.
Then create http-common.js file:
import axios from "axios"; export default axios.create({ baseURL: "http://localhost:8080/api", headers: { "Content-type": "application/json", } });
2.2 Components
List of Items
components/CustomersList.vue
<template> <div class="list row"> <div class="col-md-6"> <h3>Customers List</h3> <ul> <li v-for="(customer, index) in customers" :key="index"> <router-link :to="{ name: 'customer-details', params: { customer: customer, id: customer.id } }"> {{customer.name}} </router-link> </li> </ul> </div> <div class="col-md-6"> <router-view @refreshData="refreshList"></router-view> </div> </div> </template> <script> import http from "../http-common"; export default { name: "customers-list", data() { return { customers: [] }; }, methods: { /* eslint-disable no-console */ retrieveCustomers() { http .get("/customers") .then(response => { this.customers = response.data; // JSON are parsed automatically. console.log(response.data); }) .catch(e => { console.log(e); }); }, refreshList() { this.retrieveCustomers(); } /* eslint-enable no-console */ }, mounted() { this.retrieveCustomers(); } }; </script> <style> .list { text-align: left; max-width: 450px; margin: auto; } </style>
Item Details
components/Customer.vue
<template> <div v-if="this.customer"> <h3>Customer</h3> <div> <label>Name: </label> {{this.customer.name}} </div> <div> <label>Age: </label> {{this.customer.age}} </div> <div> <label>Active: </label> {{this.customer.active}} </div> <span v-if="this.customer.active" v-on:click="updateActive(false)" class="button is-small btn-primary">Inactive</span> <span v-else v-on:click="updateActive(true)" class="button is-small btn-primary">Active</span> <span class="button is-small btn-danger" v-on:click="deleteCustomer()">Delete</span> </div> <div v-else> <br/> <p>Please click on a Customer...</p> </div> </template> <script> import http from "../http-common"; export default { name: "customer", props: ["customer"], methods: { /* eslint-disable no-console */ updateActive(status) { var data = { id: this.customer.id, name: this.customer.name, age: this.customer.age, active: status }; http .put("/customer/" + this.customer.id, data) .then(response => { this.customer.active = response.data.active; console.log(response.data); }) .catch(e => { console.log(e); }); }, deleteCustomer() { http .delete("/customer/" + this.customer.id) .then(response => { console.log(response.data); this.$emit("refreshData"); this.$router.push('/'); }) .catch(e => { console.log(e); }); } /* eslint-enable no-console */ } }; </script>
Add Item
components/AddCustomer.vue
<template> <div class="submitform"> <div v-if="!submitted"> <div class="form-group"> <label for="name">Name</label> <input type="text" class="form-control" id="name" required v-model="customer.name" name="name"> </div> <div class="form-group"> <label for="age">Age</label> <input type="number" class="form-control" id="age" required v-model="customer.age" name="age"> </div> <button v-on:click="saveCustomer" class="btn btn-success">Submit</button> </div> <div v-else> <h3>You submitted successfully!</h3> <button class="btn btn-success" v-on:click="newCustomer">Add</button> </div> </div> </template> <script> import http from "../http-common"; export default { name: "add-customer", data() { return { customer: { id: 0, name: "", age: 0, active: false }, submitted: false }; }, methods: { /* eslint-disable no-console */ saveCustomer() { var data = { name: this.customer.name, age: this.customer.age }; http .post("/customer", data) .then(response => { this.customer.id = response.data.id; console.log(response.data); }) .catch(e => { console.log(e); }); this.submitted = true; }, newCustomer() { this.submitted = false; this.customer = {}; } /* eslint-enable no-console */ } }; </script> <style> .submitform { max-width: 300px; margin: auto; } </style>
Search Items
components/SearchCustomers.vue
<template> <div class="searchform"> <h3>Find by Age</h3> <div class="form-group"> <input type="number" class="form-control" id="age" required v-model="age" name="age"> </div> <div class="btn-group"> <button v-on:click="searchCustomers" class="btn btn-success">Search</button> </div> <ul class="search-result"> <li v-for="(customer, index) in customers" :key="index"> <h5>{{customer.name}} ({{customer.age}})</h5> </li> </ul> </div> </template> <script> import http from "../http-common"; export default { name: "search-customer", data() { return { age: 0, customers: [] }; }, methods: { /* eslint-disable no-console */ searchCustomers() { http .get("/customers/age/" + this.age) .then(response => { this.customers = response.data; // JSON are parsed automatically. console.log(response.data); }) .catch(e => { console.log(e); }); } /* eslint-enable no-console */ } }; </script> <style> .searchform { max-width: 300px; margin: auto; } .search-result { margin-top: 20px; text-align: left; } </style> </code></pre> <h4>2.3 Configure Port for Vue App</h4> <em>vue.config.js</em> <pre><code class="language-java"> module.exports = { devServer: { port: 4200 } }
Run
– Spring Boot Server: mvn clean install
and mvn spring-boot:run
.
– Vue.js Client: npm run serve
.
Open Browser with Url: http://localhost:4200/
.
Source Code for Vue.js and SpringBoot
– SpringBootRestMariaDB
– vue-springboot
Further Reading
– Vuejs Guide
– Reactjs JWT Authentication Example
Just Testing