Spring Boot + AngularJS + Spring Data + JPA CRUD App Example
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à ...
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
<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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>${h2.version}</version> </dependency> <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> <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.
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.
- 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; } }
- 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
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 }
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); }
- 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; } }
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; } }
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);
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"> <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"> <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 awidth="100"></th> <th awidth="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-awidth">Edit</button></td> <td><button type="button" ng-click="ctrl.removeUser(u.id)" class="btn btn-danger custom-awidth">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 } } ]);
-- 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