Spring Boot + AngularJS + Spring Data + JPA CRUD App Example
This post hasn't been updated for 3 years
Trong bài trước tôi đã có giới thiệu về Spring Boot, 1 framework rất mạnh mẽ được xây dựng dựạ trên backbone của Spring Framework, giúp giảm tải cấu hình, tăng tốc quá trình phát triển.
Ở bài này, tôi sẽ demo 1 ứng dụng thêm, sửa, xóa sử dụng Spring Boot kết hợp với AngularJS, JPA và MySQL.
- Các công nghệ có sử trong project:
- Spring Boot 1.4.3.RELEASE
- Spring 4.3.5.RELEASE
- Spring data JPA 1.10.6.RELEASE
- Hibernate 5.0.11.Final
- MySQL 5.1.40
- H2 1.4.187
- AngularJS 1.5.8
- Maven 3.1
- JDK 1.8
- Intellij IDEA
1. Cấu trúc Project:
2. Dependency của project:
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.websystique.springboot</groupId>
<artifactId>SpringBootCRUDApplicationExample</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>SpringBootCRUDApplicationExample</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
<h2.version>1.4.187</h2.version>
</properties>
<dependencies>
<!-- Add typical dependencies for a web application -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Add freemarker template support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- Add JPA support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Add Hikari Connection Pooling support -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<!-- Add H2 database support [for running with local profile] -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2.version}</version>
</dependency>
<!-- Add MySQL database support [for running with PRODUCTION profile] -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin><!-- Include if you want to make an executable jar[FAT JAR which
includes all dependencies along with sprinboot loader] that you can run on
commandline using java -jar NAME -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring-boot-starter-parent: Cung cấp các default hữu ích của maven, cung cấp các thành phần quản lý dependency trong trường hợp bạn có thể quên khai báo version của các dependencies. spring-boot-starter-web: Cung cấp các container điển hình của WEB MVC + Embeded. spring-boot-starter-freemarker: Cung cấp các template của thư viện rất phổ biến trong java là freemarker. spring-boot-starter-data-jpa: Cung cấp spring-data để thiết lập các thành phần JPA abstraction, giúp thao tác dễ dàng với tầng DAO.
3. Spring Boot Application [Main class]
package com.websystique.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
import com.websystique.springboot.configuration.JpaConfiguration;
@Import(JpaConfiguration.class)
@SpringBootApplication(scanBasePackages={"com.websystique.springboot"})
public class SpringBootCRUDApp {
public static void main(String[] args) {
SpringApplication.run(SpringBootCRUDApp.class, args);
}
}
- @SpringBootApplication là sự kết hợp của các annotation @EnableAutoConfiguration, @Configuration & @ComponentScan.
- Spring Boot @SpringBootApplication cố gắng cấu hình tự động ứng dựng dựa trên các thành phần dependencies mà bạn đã added,. Ngay khi chúng ta added spring-boot-starter-web , Spring sẽ cấu hình cho ứng dụng web.
4. JPA configuration.
- Spring Data @EnableJpaRepositories : Annocation enable JPA repositories. Nó sẽ scan xác định packages cho Spring Data repositories.
- Spring Boot DataSourceProperties: là các class hữu dụng cho việc cấu hình data source.
- Spring Boot DataSourceBuilder: là các builder, giúp mapping các properties datasource.
package com.websystique.springboot.configuration;
import java.util.Properties;
import javax.naming.NamingException;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.zaxxer.hikari.HikariDataSource;
@Configuration
@EnableJpaRepositories(basePackages = "com.websystique.springboot.repositories",
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager")
@EnableTransactionManagement
public class JpaConfiguration {
@Autowired
private Environment environment;
@Value("${datasource.sampleapp.maxPoolSize:10}")
private int maxPoolSize;
/*
* Populate SpringBoot DataSourceProperties object directly from application.yml
* based on prefix.Thanks to .yml, Hierachical data is mapped out of the box with matching-name
* properties of DataSourceProperties object].
*/
@Bean
@Primary
@ConfigurationProperties(prefix = "datasource.sampleapp")
public DataSourceProperties dataSourceProperties(){
return new DataSourceProperties();
}
/*
* Configure HikariCP pooled DataSource.
*/
@Bean
public DataSource dataSource() {
DataSourceProperties dataSourceProperties = dataSourceProperties();
HikariDataSource dataSource = (HikariDataSource) DataSourceBuilder
.create(dataSourceProperties.getClassLoader())
.driverClassName(dataSourceProperties.getDriverClassName())
.url(dataSourceProperties.getUrl())
.username(dataSourceProperties.getUsername())
.password(dataSourceProperties.getPassword())
.type(HikariDataSource.class)
.build();
dataSource.setMaximumPoolSize(maxPoolSize);
return dataSource;
}
/*
* Entity Manager Factory setup.
*/
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan(new String[] { "com.websystique.springboot.model" });
factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
factoryBean.setJpaProperties(jpaProperties());
return factoryBean;
}
/*
* Provider specific adapter.
*/
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
return hibernateJpaVendorAdapter;
}
/*
* Here you can specify any provider specific properties.
*/
private Properties jpaProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("datasource.sampleapp.hibernate.dialect"));
properties.put("hibernate.hbm2ddl.auto", environment.getRequiredProperty("datasource.sampleapp.hibernate.hbm2ddl.method"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("datasource.sampleapp.hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("datasource.sampleapp.hibernate.format_sql"));
if(StringUtils.isNotEmpty(environment.getRequiredProperty("datasource.sampleapp.defaultSchema"))){
properties.put("hibernate.default_schema", environment.getRequiredProperty("datasource.sampleapp.defaultSchema"));
}
return properties;
}
@Bean
@Autowired
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(emf);
return txManager;
}
}
5. Property file [application.yml]
- Thông thường theo truyền thống chúng ta hay sử dụng file .properties, nhưng trong Spring Boot còn hộ trợ YAML cung cấp thư viện **SnakeYAML ** , nó thường xuyên được sử dụng ngay từ lúc start project. YAML các tập hợp cha của JSON, rất tiện lợi cho việc phân cấp dữ liệu. src/main/resources/application.yml:
---
server:
port: 8080
contextPath: /SpringBootCRUDApp
---
spring:
profiles: default
active: default
datasource:
sampleapp:
url: jdbc:h2:~/test
username: SA
password:
driverClassName: org.h2.Driver
defaultSchema:
maxPoolSize: 10
hibernate:
hbm2ddl.method: create-drop
show_sql: true
format_sql: true
dialect: org.hibernate.dialect.H2Dialect
---
spring:
profiles: prod
active: prod
datasource:
sampleapp:
url: jdbc:mysql://localhost:3306/websystique
username: root
password:
driverClassName: com.mysql.jdbc.Driver
defaultSchema:
maxPoolSize: 20
hibernate:
hbm2ddl.method: update
show_sql: true
format_sql: true
dialect: org.hibernate.dialect.MySQLDialect
6. Model
User.java
package com.websystique.springboot.model;
import org.hibernate.validator.constraints.NotEmpty;
import javax.persistence.*;
import java.io.Serializable;
@Entity
@Table(name="APP_USER")
public class User implements Serializable{
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
@NotEmpty
@Column(name="NAME", nullable=false)
private String name;
@Column(name="AGE", nullable=false)
private Integer age;
@Column(name="SALARY", nullable=false)
private double salary;
--- getter/setter omitted to save space
}
7. Spring-Data repositories
package com.websystique.springboot.repositories;
import com.websystique.springboot.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByName(String name);
}
8. Service
- Controller sẽ sử dụng các service này cho các hoạt động liên quan đến User. Service sử dụng spring-data repositories cho việc access và update user.
package com.websystique.springboot.service;
import com.websystique.springboot.model.User;
import java.util.List;
public interface UserService {
User findById(Long id);
User findByName(String name);
void saveUser(User user);
void updateUser(User user);
void deleteUserById(Long id);
void deleteAllUsers();
List<User> findAllUsers();
boolean isUserExist(User user);
}
package com.websystique.springboot.service;
import java.util.List;
import com.websystique.springboot.model.User;
import com.websystique.springboot.repositories.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service("userService")
@Transactional
public class UserServiceImpl implements UserService{
@Autowired
private UserRepository userRepository;
public User findById(Long id) {
return userRepository.findOne(id);
}
public User findByName(String name) {
return userRepository.findByName(name);
}
public void saveUser(User user) {
userRepository.save(user);
}
public void updateUser(User user){
saveUser(user);
}
public void deleteUserById(Long id){
userRepository.delete(id);
}
public void deleteAllUsers(){
userRepository.deleteAll();
}
public List<User> findAllUsers(){
return userRepository.findAll();
}
public boolean isUserExist(User user) {
return findByName(user.getName()) != null;
}
}
9. Controllers
Trong ví dụ này chúng ta sử dụng 2 controller, 1 sử dụng cho việc view, 1 để handle REST API.
package com.websystique.springboot.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class AppController {
@RequestMapping("/")
String home(ModelMap modal) {
modal.addAttribute("title","CRUD Example");
return "index";
}
@RequestMapping("/partials/{page}")
String partialHandler(@PathVariable("page") final String page) {
return page;
}
}
package com.websystique.springboot.controller;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
import com.websystique.springboot.model.User;
import com.websystique.springboot.service.UserService;
import com.websystique.springboot.util.CustomErrorType;
@RestController
@RequestMapping("/api")
public class RestApiController {
public static final Logger logger = LoggerFactory.getLogger(RestApiController.class);
@Autowired
UserService userService; //Service which will do all data retrieval/manipulation work
// -------------------Retrieve All Users---------------------------------------------
@RequestMapping(value = "/user/", method = RequestMethod.GET)
public ResponseEntity<List<User>> listAllUsers() {
List<User> users = userService.findAllUsers();
if (users.isEmpty()) {
return new ResponseEntity(HttpStatus.NO_CONTENT);
// You many decide to return HttpStatus.NOT_FOUND
}
return new ResponseEntity<List<User>>(users, HttpStatus.OK);
}
// -------------------Retrieve Single User------------------------------------------
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public ResponseEntity<?> getUser(@PathVariable("id") long id) {
logger.info("Fetching User with id {}", id);
User user = userService.findById(id);
if (user == null) {
logger.error("User with id {} not found.", id);
return new ResponseEntity(new CustomErrorType("User with id " + id
+ " not found"), HttpStatus.NOT_FOUND);
}
return new ResponseEntity<User>(user, HttpStatus.OK);
}
// -------------------Create a User-------------------------------------------
@RequestMapping(value = "/user/", method = RequestMethod.POST)
public ResponseEntity<?> createUser(@RequestBody User user, UriComponentsBuilder ucBuilder) {
logger.info("Creating User : {}", user);
if (userService.isUserExist(user)) {
logger.error("Unable to create. A User with name {} already exist", user.getName());
return new ResponseEntity(new CustomErrorType("Unable to create. A User with name " +
user.getName() + " already exist."),HttpStatus.CONFLICT);
}
userService.saveUser(user);
HttpHeaders headers = new HttpHeaders();
headers.setLocation(ucBuilder.path("/api/user/{id}").buildAndExpand(user.getId()).toUri());
return new ResponseEntity<String>(headers, HttpStatus.CREATED);
}
// ------------------- Update a User ------------------------------------------------
@RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)
public ResponseEntity<?> updateUser(@PathVariable("id") long id, @RequestBody User user) {
logger.info("Updating User with id {}", id);
User currentUser = userService.findById(id);
if (currentUser == null) {
logger.error("Unable to update. User with id {} not found.", id);
return new ResponseEntity(new CustomErrorType("Unable to upate. User with id " + id + " not found."),
HttpStatus.NOT_FOUND);
}
currentUser.setName(user.getName());
currentUser.setAge(user.getAge());
currentUser.setSalary(user.getSalary());
userService.updateUser(currentUser);
return new ResponseEntity<User>(currentUser, HttpStatus.OK);
}
// ------------------- Delete a User-----------------------------------------
@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
public ResponseEntity<?> deleteUser(@PathVariable("id") long id) {
logger.info("Fetching & Deleting User with id {}", id);
User user = userService.findById(id);
if (user == null) {
logger.error("Unable to delete. User with id {} not found.", id);
return new ResponseEntity(new CustomErrorType("Unable to delete. User with id " + id + " not found."),
HttpStatus.NOT_FOUND);
}
userService.deleteUserById(id);
return new ResponseEntity<User>(HttpStatus.NO_CONTENT);
}
// ------------------- Delete All Users-----------------------------
@RequestMapping(value = "/user/", method = RequestMethod.DELETE)
public ResponseEntity<User> deleteAllUsers() {
logger.info("Deleting All Users");
userService.deleteAllUsers();
return new ResponseEntity<User>(HttpStatus.NO_CONTENT);
}
- Class helper để gửi các error từ API trong JSON
package com.websystique.springboot.util;
public class CustomErrorType {
private String errorMessage;
public CustomErrorType(String errorMessage){
this.errorMessage = errorMessage;
}
public String getErrorMessage() {
return errorMessage;
}
}
10. Sử dụng MySQL setup database cho ứng dụng.
create table APP_USER (
id BIGINT NOT NULL AUTO_INCREMENT,
name VARCHAR(30) NOT NULL,
age INTEGER NOT NULL,
salary REAL NOT NULL,
PRIMARY KEY (id)
);
/* Populate USER Table */
INSERT INTO APP_USER(name,age,salary)
VALUES ('Dat',23,1000000000);
INSERT INTO APP_USER(name,age,salary)
VALUES ('Jose',23,5000000000);
Front-end
1. Freemarker Templates
src/main/resources/templates/index.ftl
<!DOCTYPE html>
<html lang="en" ng-app="crudApp">
<head>
<title>${title}</title>
<link href="css/bootstrap.css" rel="stylesheet"/>
<link href="css/app.css" rel="stylesheet"/>
</head>
<body>
<div ui-view></div>
<script src="js/lib/angular.min.js" ></script>
<script src="js/lib/angular-ui-router.min.js" ></script>
<script src="js/lib/localforage.min.js" ></script>
<script src="js/lib/ngStorage.min.js"></script>
<script src="js/app/app.js"></script>
<script src="js/app/UserService.js"></script>
<script src="js/app/UserController.js"></script>
</body>
</html>
src/main/resources/templates/list.ftl:
<div class="generic-container">
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading"><span class="lead">Specific User </span></div>
<div class="panel-body">
<div class="formcontainer">
<div class="alert alert-success" role="alert" ng-if="ctrl.successMessage">{{ctrl.successMessage}}</div>
<div class="alert alert-danger" role="alert" ng-if="ctrl.errorMessage">{{ctrl.errorMessage}}</div>
<form ng-submit="ctrl.submit()" name="myForm" class="form-horizontal">
<input type="hidden" ng-model="ctrl.user.id" />
<div class="row">
<div class="form-group col-md-12">
<label class="col-md-2 control-lable" for="uname">Name</label>
<div class="col-md-7">
<input type="text" ng-model="ctrl.user.name" id="uname" class="username form-control input-sm" placeholder="Enter your name" required ng-minlength="3"/>
</div>
</div>
</div>
<div class="row">
<div class="form-group col-md-12">
<label class="col-md-2 control-lable" for="age">Age</label>
<div class="col-md-7">
<input type="text" ng-model="ctrl.user.age" id="age" class="form-control input-sm" placeholder="Enter your Age." required ng-pattern="ctrl.onlyIntegers"/>
</div>
</div>
</div>
<div class="row">
<div class="form-group col-md-12">
<label class="col-md-2 control-lable" for="salary">Salary</label>
<div class="col-md-7">
<input type="text" ng-model="ctrl.user.salary" id="salary" class="form-control input-sm" placeholder="Enter your Salary." required ng-pattern="ctrl.onlyNumbers"/>
</div>
</div>
</div>
<div class="row">
<div class="form-actions floatRight">
<input type="submit" value="{{!ctrl.user.id ? 'Add' : 'Update'}}" class="btn btn-primary btn-sm" ng-disabled="myForm.$invalid || myForm.$pristine">
<button type="button" ng-click="ctrl.reset()" class="btn btn-warning btn-sm" ng-disabled="myForm.$pristine">Reset Form</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading"><span class="lead">List of Users </span></div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<th>AGE</th>
<th>SALARY</th>
<th width="100"></th>
<th width="100"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="u in ctrl.getAllUsers()">
<td>{{u.id}}</td>
<td>{{u.name}}</td>
<td>{{u.age}}</td>
<td>{{u.salary}}</td>
<td><button type="button" ng-click="ctrl.editUser(u.id)" class="btn btn-success custom-width">Edit</button></td>
<td><button type="button" ng-click="ctrl.removeUser(u.id)" class="btn btn-danger custom-width">Remove</button></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
- Static resources: chứa resource của project: js, css, image...
- Error Page src/main/resources/templates/error.ftl
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="css/bootstrap.css" />
</head>
<body>
<div class="container">
<div class="jumbotron alert-danger">
<h1>Oops. Something went wrong</h1>
<h2>${status} ${error}</h2>
</div>
</div>
</body>
</html>
2. AngularJs [ui-router based app]
src/main/resources/static/js/app.js
var app = angular.module('crudApp',['ui.router','ngStorage']);
app.constant('urls', {
BASE: 'http://localhost:8080/SpringBootCRUDApp',
USER_SERVICE_API : 'http://localhost:8080/SpringBootCRUDApp/api/user/'
});
app.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/',
templateUrl: 'partials/list',
controller:'UserController',
controllerAs:'ctrl',
resolve: {
users: function ($q, UserService) {
console.log('Load all users');
var deferred = $q.defer();
UserService.loadAllUsers().then(deferred.resolve, deferred.resolve);
return deferred.promise;
}
}
});
$urlRouterProvider.otherwise('/');
}]);
src/main/resources/static/js/UserService.js
'use strict';
angular.module('crudApp').factory('UserService',
['$localStorage', '$http', '$q', 'urls',
function ($localStorage, $http, $q, urls) {
var factory = {
loadAllUsers: loadAllUsers,
getAllUsers: getAllUsers,
getUser: getUser,
createUser: createUser,
updateUser: updateUser,
removeUser: removeUser
};
return factory;
function loadAllUsers() {
console.log('Fetching all users');
var deferred = $q.defer();
$http.get(urls.USER_SERVICE_API)
.then(
function (response) {
console.log('Fetched successfully all users');
$localStorage.users = response.data;
deferred.resolve(response);
},
function (errResponse) {
console.error('Error while loading users');
deferred.reject(errResponse);
}
);
return deferred.promise;
}
function getAllUsers(){
return $localStorage.users;
}
function getUser(id) {
console.log('Fetching User with id :'+id);
var deferred = $q.defer();
$http.get(urls.USER_SERVICE_API + id)
.then(
function (response) {
console.log('Fetched successfully User with id :'+id);
deferred.resolve(response.data);
},
function (errResponse) {
console.error('Error while loading user with id :'+id);
deferred.reject(errResponse);
}
);
return deferred.promise;
}
function createUser(user) {
console.log('Creating User');
var deferred = $q.defer();
$http.post(urls.USER_SERVICE_API, user)
.then(
function (response) {
loadAllUsers();
deferred.resolve(response.data);
},
function (errResponse) {
console.error('Error while creating User : '+errResponse.data.errorMessage);
deferred.reject(errResponse);
}
);
return deferred.promise;
}
function updateUser(user, id) {
console.log('Updating User with id '+id);
var deferred = $q.defer();
$http.put(urls.USER_SERVICE_API + id, user)
.then(
function (response) {
loadAllUsers();
deferred.resolve(response.data);
},
function (errResponse) {
console.error('Error while updating User with id :'+id);
deferred.reject(errResponse);
}
);
return deferred.promise;
}
function removeUser(id) {
console.log('Removing User with id '+id);
var deferred = $q.defer();
$http.delete(urls.USER_SERVICE_API + id)
.then(
function (response) {
loadAllUsers();
deferred.resolve(response.data);
},
function (errResponse) {
console.error('Error while removing User with id :'+id);
deferred.reject(errResponse);
}
);
return deferred.promise;
}
}
]);
src/main/resources/static/js/UserController.js
'use strict';
angular.module('crudApp').controller('UserController',
['UserService', '$scope', function( UserService, $scope) {
var self = this;
self.user = {};
self.users=[];
self.submit = submit;
self.getAllUsers = getAllUsers;
self.createUser = createUser;
self.updateUser = updateUser;
self.removeUser = removeUser;
self.editUser = editUser;
self.reset = reset;
self.successMessage = '';
self.errorMessage = '';
self.done = false;
self.onlyIntegers = /^\d+$/;
self.onlyNumbers = /^\d+([,.]\d+)?$/;
function submit() {
console.log('Submitting');
if (self.user.id === undefined || self.user.id === null) {
console.log('Saving New User', self.user);
createUser(self.user);
} else {
updateUser(self.user, self.user.id);
console.log('User updated with id ', self.user.id);
}
}
function createUser(user) {
console.log('About to create user');
UserService.createUser(user)
.then(
function (response) {
console.log('User created successfully');
self.successMessage = 'User created successfully';
self.errorMessage='';
self.done = true;
self.user={};
$scope.myForm.$setPristine();
},
function (errResponse) {
console.error('Error while creating User');
self.errorMessage = 'Error while creating User: ' + errResponse.data.errorMessage;
self.successMessage='';
}
);
}
function updateUser(user, id){
console.log('About to update user');
UserService.updateUser(user, id)
.then(
function (response){
console.log('User updated successfully');
self.successMessage='User updated successfully';
self.errorMessage='';
self.done = true;
$scope.myForm.$setPristine();
},
function(errResponse){
console.error('Error while updating User');
self.errorMessage='Error while updating User '+errResponse.data;
self.successMessage='';
}
);
}
function removeUser(id){
console.log('About to remove User with id '+id);
UserService.removeUser(id)
.then(
function(){
console.log('User '+id + ' removed successfully');
},
function(errResponse){
console.error('Error while removing user '+id +', Error :'+errResponse.data);
}
);
}
function getAllUsers(){
return UserService.getAllUsers();
}
function editUser(id) {
self.successMessage='';
self.errorMessage='';
UserService.getUser(id).then(
function (user) {
self.user = user;
},
function (errResponse) {
console.error('Error while removing user ' + id + ', Error :' + errResponse.data);
}
);
}
function reset(){
self.successMessage='';
self.errorMessage='';
self.user={};
$scope.myForm.$setPristine(); //reset Form
}
}
]);
Run the application
-- Mở trình duyệt vào truy cập tới: http://localhost:8080/SpringBootCRUDApp/
-- Thêm 1 vài bản ghi:
-- Error khi đã tồn tại bản ghi trùng tên:
-- Update bản ghi:
-- Xóa 1 bản ghi:
Link tải về source code: https://github.com/datvv/Spring-Boot-AngularJS-Spring-Data-JPA-CRUD-App-Example
All Rights Reserved