在本教程中,我们将向您展示Vue.js Http Client和Spring Boot Server示例,该示例使用Spring JPA将MySQL,Vue.js作为前端技术进行CRUD发出请求和接收响应。
用到的技术:
– 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
1. Spring Boot Server
图片.png2. Vue.js Client
图片.png开始实战
1. Spring Boot Server
图片.png– Customer 实体类及customer表
– CustomerRepository 一个继承于CrudRepository的接口(相当于以前的DAO),他在CustomerController 会自动被注入进来。你可以在该接口中实现自己操作数据库的方法。
– CustomerController 是一个REST Controller,里面有各个请求端点的映射。例如getAllCustomers,
postCustomer,deleteCustomer,findByAge,updateCustomer方法。
– Configuration Spring Datasource 和 Spring JPA properties 的配置在application.properties中
– Dependencies Spring Boot 和MySQL的相关依赖包在 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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
1.2 数据模型
model/Customer.java
package com.grokonez.spring.restapi.mysql.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.grokonez.spring.restapi.mysql.repo;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
import com.grokonez.spring.restapi.mysql.model.Customer;
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByAge(int age);
}
1.4 REST Controller
controller/CustomerController.java
package com.grokonez.spring.restapi.mysql.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.grokonez.spring.restapi.mysql.model.Customer;
import com.grokonez.spring.restapi.mysql.repo.CustomerRepository;
@CrossOrigin(origins = "http://localhost:4200")
@RestController
@RequestMapping("/api")
public class CustomerController {
@Autowired
CustomerRepository repository;
@GetMapping("/customers")
public List<Customer> getAllCustomers() {
System.out.println("Get all Customers...");
List<Customer> 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<String> 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<Customer> findByAge(@PathVariable int age) {
List<Customer> customers = repository.findByAge(age);
return customers;
}
@PutMapping("/customer/{id}")
public ResponseEntity<Customer> updateCustomer(@PathVariable("id") long id, @RequestBody Customer customer) {
System.out.println("Update Customer with ID = " + id + "...");
Optional<Customer> 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 CSpring Datasource & JPA 相关配置信息
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. Vue.js Client
图片.png给项目添加Vue Router
– 执行命令: npm install vue-router.
– Import router 到 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");
定义路由
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 添加Navbar and router-view
src/App.vue:
<template>
<div id="app" class="container-fluid">
<div class="site-info">
<h1>grokonez</h1>
<h3>Vue SpringBoot example</h3>
</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 初始化HTTP Client
import axios from "axios";
export default axios.create({
baseURL: "http://localhost:8080/api",
headers: {
"Content-type": "application/json",
}
});
2.2 Components
列表组件
components/CustomersList.vue
<template>
<div class="list row">
<div class="col-md-6">
<h4>Customers List</h4>
<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>
单个详情查看
components/Customer.vue
<template>
<div v-if="this.customer">
<h4>Customer</h4>
<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>
添加
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>
<h4>You submitted successfully!</h4>
<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>
查询操作
components/SearchCustomers.vue
<template>
<div class="searchform">
<h4>Find by Age</h4>
<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">
<h6>{{customer.name}} ({{customer.age}})</h6>
</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>
2.3 为 Vue App配置端口
vue.config.js
module.exports = {
devServer: {
port: 4200
}
}