- CRUD 操作的 HTTP URL、方法和响应状态代码
- 使用 Angular 的 Http.post() 进行创建操作
- 使用 Angular 的 Http.get() 进行读取操作
- 使用 Angular 的 Http.put() 进行更新操作
- 使用 Angular 的 Http.delete() 进行删除操作
- 使用 Angular 与 Typescript 完整的客户端应用
- 1. 客户端应用中使用的技术
- 2. 客户项目结构
- 3. 使用 Angular 的 Http API 为 CRUD 操作创建服务
- 4. 为CRUD操作创建组件和HTML模板
- 5. 创建应用程序组件和模块
- 使用 Maven 的 Spring Boot 完整的 REST Web 服务应用
- 1. REST 网络服务应用中使用的技术
- 2. MySQL 数据库结构
- 3. 项目结构
- 4. Maven 文件
- 5. 在 application.properties 中配置属性
- 6. 为 CRUD 操作创建 DAO
- 7. 创建 Service
- 8. 创建 Controller
- 9. 创建 Java 主类来运行 Spring Boot 应用程序
- 运行 REST 网络服务和 Angular 应用程序
- 1. 使用 Spring Boot 运行 REST 网络服务
- 2. 使用 Angular CLI 运行 Angular 应用程序
- 参考文献
- 源码下载
本页将介绍Spring Boot REST + Angular + JPA + Hibernate + MySQL进行CRUD的实例。
我们将使用Spring Boot创建一个REST网络服务应用程序,并使用Angular创建一个客户端应用程序。REST网络服务将暴露创建、读取、更新和删除操作的方法。Angular应用程序将使用Angular的Http API进行CRUD操作。如果我们的客户端应用程序运行在与Web服务域不同的域上,那么Spring Boot Web服务控制器将使用@CrossOrigin注解配置客户端域的URL,以处理跨域资源共享(CORS)。
Angular应用程序将是单页应用程序,将执行CRUD操作。在REST网络服务响应HTTP状态代码的基础上,Angular应用程序将显示CRUD操作的成功和失败信息。
在我们的REST网络服务应用程序中,我们将提供两种方法用于读取操作,一种用于按ID获取数据,另一种用于获取所有数据。在我们的例子中,我们将对文章表进行CRUD操作。当我们创建文章时,文章的ID将由数据库自动生成。为了按ID获取和删除文章,我们将使用请求参数将文章的ID从客户端传递给REST Web服务应用程序。
在我们的Spring Boot应用程序中,我们将使用application.properties文件配置数据库。为了与数据库交互,我们将使用JPA EntityManager。现在找到完整的客户端和REST web服务应用程序的步骤。
CRUD 操作的 HTTP URL、方法和响应状态代码在我们的例子中,我们将使用以下HTTP 网址、方法和响应状态代码进行CRUD操作。
1. Create :
HTTP 方法: POST, 网址: /user/article
Angular API: Http.post()
HTTP响应状态代码: 201 CREATED 和 409 CONFLICT
2. Read :
HTTP 方法: GET, 网址: /user/article?id={id} (Fetches article by id)
HTTP 方法: GET, 网址: /user/all-articles (Fetches all articles)
Angular API: Http.get()
HTTP响应状态代码: 200 OK
3. Update :
HTTP 方法: PUT, 网址: /user/article
Angular API: Http.put()
HTTP响应状态代码: 200 OK
4. Delete :
HTTP 方法: DELETE, 网址: /user/article?id={id}
Angular API: Http.delete()
HTTP响应状态代码: 204 NO CONTENT
我们的Angular应用程序的CRUD操作的输出将如下。
使用 Angular 的 Http.post() 进行创建操作我们将使用Angular的Http.post()方法进行创建操作。它使用HTTP POST方法访问URL。Http.post()方法的语法如下。
post(url: string, body: any, options?: RequestOptionsArgs) : Observable
参数的说明如下。
url: 这是创建文章的REST网络服务的URL。
body: 这是任何类型的对象,将被传递给REST web服务服务器。在我们的例子中,我们将创建一个Angular类Article,并将其实例传递给body参数。
options: 这是可选的。它接受Angular的RequestOptions实例,这个实例是用Angular的RequestOptionsArgs实例化的。使用RequestOptions,我们传递请求参数、请求头信息等。
Http.post()返回Observable的实例。Observable是对任何时间内的任何一组数值的表示。
客户端代码
找到客户端的代码来创建文章。这里我们将使用Angular的Http.post()方法。
articleUrl = "http://localhost:8080/user/article"; createArticle(article: Article):Observable{ let cpHeaders = new Headers({ 'Content-Type': 'application/json' }); let options = new RequestOptions({ headers: cpHeaders }); return this.http.post(this.articleUrl, article, options) .map(success => success.status) .catch(this.handleError); }
我们将头信息Content-Type传递为application/json。在操作成功后,我们将状态代码作为Observable的一个实例返回。
服务端代码
找到创建操作的Web服务方法。
@PostMapping("article")
public ResponseEntity createArticle(@RequestBody Article article, UriComponentsBuilder builder) {
boolean flag = articleService.createArticle(article);
if (flag == false) {
return new ResponseEntity(HttpStatus.CONFLICT);
}
HttpHeaders headers = new HttpHeaders();
headers.setLocation(builder.path("/article?id={id}").buildAndExpand(article.getArticleId()).toUri());
return new ResponseEntity(headers, HttpStatus.CREATED);
}
在上述代码中,Article是一个java实体,相当于Angular的Article类。@PostMapping是HTTP POST方法的请求映射。当这个过程成功时,它会返回HTTP状态201 CREATED,并在location header中返回新文章的URL。如果文章已经存在,那么上述服务器代码将返回HTTP状态09 CONFLICT。
使用 Angular 的 Http.get() 进行读取操作我们将使用Angular的Http.get()方法进行读取操作。它使用HTTP GET方法访问URL。找到它的语法。
get(url: string, options?: RequestOptionsArgs) : Observable
查找参数说明。
url: 读取文章的Web服务URL。
options: 这是可选的。它用于传递请求参数和头文件等。
Http.get()返回Observable的实例。
客户端代码
找到使用Http.get()的Angular代码,它将传递请求参数来过滤结果。
articleUrl = "http://localhost:8080/user/article"; getArticleById(articleId: string): Observable{ let cpHeaders = new Headers({ 'Content-Type': 'application/json' }); let cpParams = new URLSearchParams(); cpParams.set('id', articleId); let options = new RequestOptions({ headers: cpHeaders, params: cpParams }); return this.http.get(this.articleUrl, options) .map(this.extractData) .catch(this.handleError); }
上面的代码将获取指定id的文章。
现在查找angular客户端代码,该代码将从服务器获取所有文章。
allArticlesUrl = "http://localhost:8080/user/all-articles"; getAllArticles(): Observable{ return this.http.get(this.allArticlesUrl) .map(this.extractData) .catch(this.handleError); }
服务端代码
找到将接受请求参数的网络服务方法,以过滤结果。
@GetMapping("article")
public ResponseEntity getArticleById(@RequestParam("id") String id) {
Article article = articleService.getArticleById(Integer.parseInt(id));
return new ResponseEntity(article, HttpStatus.OK);
}
@GetMapping是HTTP GET方法的请求映射。它接受文章的id作为请求参数,用于按id获取文章。操作成功后,它将返回给定id的文章和HTTP状态代码200 OK。
现在找到将返回所有文章的网络服务方法。
@GetMapping("all-articles")
public ResponseEntity> getAllArticles() {
List list = articleService.getAllArticles();
return new ResponseEntity>(list, HttpStatus.OK);
}
操作成功后,它将返回所有文章和HTTP状态代码200 OK。
使用 Angular 的 Http.put() 进行更新操作我们将使用Angular的Http.put()方法进行更新操作。它使用HTTP PUT方法访问URL。找到它的语法。
put(url: string, body: any, options?: RequestOptionsArgs) : Observable
查找参数说明。
url: 这是用于更新文章的REST网络服务URL。
body: 这是任意类型的对象,将被传递给REST的web服务服务器。在我们的例子中,我们将创建一个Angular的Article类,并将其实例传递给body参数。文章的实例必须有文章的ID,文章的其他字段将在此基础上被更新。
options: 这是可选的。这是用来传递请求参数和请求头信息等。
Http.put()返回Observable实例。
客户端代码
找到使用Http.put()方法来更新文章的angular代码。
articleUrl = "http://localhost:8080/user/article"; updateArticle(article: Article):Observable{ let cpHeaders = new Headers({ 'Content-Type': 'application/json' }); let options = new RequestOptions({ headers: cpHeaders }); return this.http.put(this.articleUrl, article, options) .map(success => success.status) .catch(this.handleError); }
在我们的Angular应用程序中,我们已经创建了一个Article类,并将其实例传递给Http.put()方法。文章将在文章ID的基础上被更新,文章ID是我们Angular文章类的一个字段。
服务端代码
找到更新文章的Web服务方法。
@PutMapping("article")
public ResponseEntity updateArticle(@RequestBody Article article) {
articleService.updateArticle(article);
return new ResponseEntity(article, HttpStatus.OK);
}
在上述代码中,Article是一个java实体,相当于Angular的Article类。@PutMapping是HTTP PUT方法的请求映射。操作成功后,会返回HTTP状态200 OK。
使用 Angular 的 Http.delete() 进行删除操作我们将使用Angular的Http.delete()方法进行删除操作。Http.delete()使用HTTP DELETE方法访问URL。找到它的语法。
delete(url: string, options?: RequestOptionsArgs) : Observable
查找参数说明。
url: 用于删除文章的Web服务URL。
options: 这是可选的。它用于传递请求参数和头文件等。
Http.get()返回Observable实例。
客户端代码
找到使用Http.delete()方法的客户端代码,按ID删除文章。
articleUrl = "http://localhost:8080/user/article"; deleteArticleById(articleId: string): Observable{ let cpHeaders = new Headers({ 'Content-Type': 'application/json' }); let cpParams = new URLSearchParams(); cpParams.set('id', articleId); let options = new RequestOptions({ headers: cpHeaders, params: cpParams }); return this.http.delete(this.articleUrl, options) .map(success => success.status) .catch(this.handleError); }
在请求参数中,我们传递文章ID来删除文章。
服务端代码
查找网络服务方法,该方法将删除作为请求参数的给定文章ID的文章。
@DeleteMapping("article")
public ResponseEntity deleteArticle(@RequestParam("id") String id) {
articleService.deleteArticle(Integer.parseInt(id));
return new ResponseEntity(HttpStatus.NO_CONTENT);
}
操作成功后,将返回HTTP状态代码204 NO CONTENT。
使用 Angular 与 Typescript 完整的客户端应用我们将使用Angular和Typescript创建完整的客户端应用程序。对于开发环境,我们使用Angular CLI。我们的客户端项目将是一个独立于REST web服务项目的项目。现在让我们来讨论一下完整的客户端项目。
1. 客户端应用中使用的技术- Angular 4.0.0
- Typescript 2.2.0
- Node.js 6.10.1
- Angular CLI 1.0.4
- Angular Compiler CLI 4.0.0
angular-demo | |--src | | | |--app | | | | | |--article.component.ts | | |--article.service.ts | | |--article.ts | | |--article.component.html | | |--article.component.css | | | | | |--app.component.ts | | |--app.module.ts | | | |--assets | | | | | |--images | | | | | | | |--loading.gif | | | | | | |--main.ts | |--index.html | |--styles.css | |--node_modules |--package.json3. 使用 Angular 的 Http API 为 CRUD 操作创建服务
我们将创建一个服务,其中我们将处理所有的HTTP操作,以创建、读取、更新和删除文章。Angular在@angular/http库中提供了Http类,它有get()、post()、put()、delete()等方法。我们将使用Http.post()方法编写创建方法。为了获取文章,我们将使用Http.get()创建两个方法,一个用于按ID获取文章,另一个用于获取所有文章。要更新文章,我们将使用Http.put()创建更新方法,要删除文章,我们将使用Http.delete()创建删除方法。现在找到服务类。
article.service.ts
import { Injectable } from '@angular/core';
import { Http, Response, Headers, URLSearchParams, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { Article } from './article';
@Injectable()
export class ArticleService {
//URLs for CRUD operations
allArticlesUrl = "http://localhost:8080/user/all-articles";
articleUrl = "http://localhost:8080/user/article";
//Create constructor to get Http instance
constructor(private http:Http) {
}
//Fetch all articles
getAllArticles(): Observable {
return this.http.get(this.allArticlesUrl)
.map(this.extractData)
.catch(this.handleError);
}
//Create article
createArticle(article: Article):Observable {
let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: cpHeaders });
return this.http.post(this.articleUrl, article, options)
.map(success => success.status)
.catch(this.handleError);
}
//Fetch article by id
getArticleById(articleId: string): Observable {
let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
let cpParams = new URLSearchParams();
cpParams.set('id', articleId);
let options = new RequestOptions({ headers: cpHeaders, params: cpParams });
return this.http.get(this.articleUrl, options)
.map(this.extractData)
.catch(this.handleError);
}
//Update article
updateArticle(article: Article):Observable {
let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: cpHeaders });
return this.http.put(this.articleUrl, article, options)
.map(success => success.status)
.catch(this.handleError);
}
//Delete article
deleteArticleById(articleId: string): Observable {
let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
let cpParams = new URLSearchParams();
cpParams.set('id', articleId);
let options = new RequestOptions({ headers: cpHeaders, params: cpParams });
return this.http.delete(this.articleUrl, options)
.map(success => success.status)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body;
}
private handleError (error: Response | any) {
console.error(error.message || error);
return Observable.throw(error.status);
}
}
article.ts
export class Article {
constructor(public articleId: string, public title: string, public category: string) {
}
}
4. 为CRUD操作创建组件和HTML模板
在我们的例子中,我们正在创建Angular响应式表单来提交文章,它使用@angular/forms库中的FormGroup和FormControl等。我们将创建不同的方法来调用服务方法来处理文章的CRUD操作。现在找到文章组件。
article.component.ts
import { Component, onInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { ArticleService } from './article.service';
import { Article } from './article';
@Component({
selector: 'app-article',
templateUrl: './article.component.html',
styleUrls: ['./article.component.css']
})
export class ArticleComponent implements OnInit {
//Component properties
allArticles: Article[];
statusCode: number;
requestProcessing = false;
articleIdToUpdate = null;
processValidation = false;
//Create form
articleForm = new FormGroup({
title: new FormControl('', Validators.required),
category: new FormControl('', Validators.required)
});
//Create constructor to get service instance
constructor(private articleService: ArticleService) {
}
//Create ngonInit() and and load articles
ngOnInit(): void {
this.getAllArticles();
}
//Fetch all articles
getAllArticles() {
this.articleService.getAllArticles()
.subscribe(
data => this.allArticles = data,
errorCode => this.statusCode = errorCode);
}
//Handle create and update article
onArticleFormSubmit() {
this.processValidation = true;
if (this.articleForm.invalid) {
return; //Validation failed, exit from method.
}
//Form is valid, now perform create or update
this.preProcessConfigurations();
let title = this.articleForm.get('title').value.trim();
let category = this.articleForm.get('category').value.trim();
if (this.articleIdToUpdate === null) {
//Handle create article
let article= new Article(null, title, category);
this.articleService.createArticle(article)
.subscribe(successCode => {
this.statusCode = successCode;
this.getAllArticles();
this.backToCreateArticle();
},
errorCode => this.statusCode = errorCode);
} else {
//Handle update article
let article= new Article(this.articleIdToUpdate, title, category);
this.articleService.updateArticle(article)
.subscribe(successCode => {
this.statusCode = successCode;
this.getAllArticles();
this.backToCreateArticle();
},
errorCode => this.statusCode = errorCode);
}
}
//Load article by id to edit
loadArticleToEdit(articleId: string) {
this.preProcessConfigurations();
this.articleService.getArticleById(articleId)
.subscribe(article => {
this.articleIdToUpdate = article.articleId;
this.articleForm.setValue({ title: article.title, category: article.category });
this.processValidation = true;
this.requestProcessing = false;
},
errorCode => this.statusCode = errorCode);
}
//Delete article
deleteArticle(articleId: string) {
this.preProcessConfigurations();
this.articleService.deleteArticleById(articleId)
.subscribe(successCode => {
this.statusCode = successCode;
this.getAllArticles();
this.backToCreateArticle();
},
errorCode => this.statusCode = errorCode);
}
//Perform preliminary processing configurations
preProcessConfigurations() {
this.statusCode = null;
this.requestProcessing = true;
}
//Go back from update to create
backToCreateArticle() {
this.articleIdToUpdate = null;
this.articleForm.reset();
this.processValidation = false;
}
}
article.component.html
Angular 2 CRUD Operation
Update Article for Id: {{articleIdToUpdate}}
Create New Article
Article added successfully.Article already exists.Article updated successfully.Article deleted successfully.Internal Server Error.
Article Details
| Id | Title | Category | ||
|---|---|---|---|---|
| {{article.articleId}} | {{article.title}} | {{article.category}} |
article.component.css
h1 {
font-size: 2.0em;
margin: 20px 0 0 0;
font-weight: 400;
}
h3 {
color: blue;
}
table {
border-collapse: collapse;
}
table, th, td {
border: 1px solid black;
font-size:17px;
}
input {
width: 225px;
margin: 8px 0;
background-color: #dfdfdf;
font-size:17px;
}
button {
background-color: #008CBA;
color: white;
}
.error{
color: red;
font-size: 20px;
}
.success{
color: green;
font-size: 20px;
}
5. 创建应用程序组件和模块
app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
`
})
export class AppComponent {
}
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
import { ArticleComponent } from './article.component';
import { ArticleService } from './article.service';
@NgModule({
imports: [
BrowserModule,
HttpModule,
ReactiveFormsModule
],
declarations: [
AppComponent,
ArticleComponent
],
providers: [
ArticleService
],
bootstrap: [
AppComponent
]
})
export class AppModule { }
使用 Maven 的 Spring Boot 完整的 REST Web 服务应用
在这里,我们将使用Spring Boot和Maven创建一个完整的REST Web服务应用程序。我们将在MySQL数据库中创建一个表,使用JPA和Hibernate,我们的Spring Boot应用程序将与数据库交互。在创建文章时,我们将不传递文章ID,因为文章ID将由数据库自动生成。在我们的Web服务控制器中,我们将公开文章的创建、读取、更新和删除操作的方法。
1. REST 网络服务应用中使用的技术- Java 8
- Spring Boot 1.5.3.RELEASE
- Maven 3.3
- MySQL 5.5
- Eclipse Mars
在我们的数据库中,我们创建了一个名为articles的表。找到数据库的结构。
Database Schema
-- Dumping database structure for concretepage CREATE DATAbase IF NOT EXISTS `concretepage`; USE `concretepage`; -- Dumping structure for table concretepage.articles CREATE TABLE IF NOT EXISTS `articles` ( `article_id` int(5) NOT NULL AUTO_INCREMENT, `title` varchar(200) NOT NULL, `category` varchar(100) NOT NULL, PRIMARY KEY (`article_id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1; -- Dumping data for table concretepage.articles: ~4 rows (approximately) ; INSERT INTO `articles` (`article_id`, `title`, `category`) VALUES (1, 'Angular 2 Tutorial using CLI', 'Angular'), (2, 'Spring Boot Getting Started', 'Spring Boot'), (3, 'Lambda expressions Java 8 Example', 'Java 8'), (4, 'Android AsyncTask Example', 'Android');
当我们创建文章时,文章的ID将由数据库自动生成。现在为上述表找到对应的java实体。
Article.java
package com.concretepage.entity;
import java.io.Serializable;
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="articles")
public class Article implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="article_id")
private int articleId;
@Column(name="title")
private String title;
@Column(name="category")
private String category;
public int getArticleId() {
return articleId;
}
public void setArticleId(int articleId) {
this.articleId = articleId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
}
3. 项目结构
4. Maven 文件
pom.xml
4.0.0 com.concretepage spring-boot-demo 0.0.1-SNAPSHOT jar spring-demo Spring Boot Demo Project org.springframework.boot spring-boot-starter-parent 1.5.3.RELEASE 1.8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-jpa mysql mysql-connector-java org.springframework.boot spring-boot-devtools true org.springframework.boot spring-boot-maven-plugin
找到maven文件中配置的spring boot启动器的描述。
spring-boot-starter-parent: 用于依赖关系管理的父POM。
spring-boot-starter-web: 构建Web、REST应用程序的启动器。它使用Tomcat服务器作为默认的嵌入式服务器。
spring-boot-starter-data-jpa: 具备hibernate的spring data JPA的启动程序。
spring-boot-devtools: 它提供了开发者工具。这些工具在应用开发模式中很有帮助。开发者工具的一个特点是在代码发生任何变化时自动重新启动服务器。
spring-boot-maven-plugin: 它用于创建应用程序的可执行JAR。
5. 在 application.properties 中配置属性与数据源、使用Hibernate的JPA和日志有关的属性将被配置在application.properties文件中。
application.properties
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/concretepage spring.datasource.username=root spring.datasource.password= spring.datasource.tomcat.max-wait=20000 spring.datasource.tomcat.max-active=50 spring.datasource.tomcat.max-idle=20 spring.datasource.tomcat.min-idle=15 spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect spring.jpa.properties.hibernate.id.new_generator_mappings = false spring.jpa.properties.hibernate.format_sql = true logging.level.org.hibernate.SQL=DEBUG logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE6. 为 CRUD 操作创建 DAO
在我们的例子中找到用于执行CRUD操作的DAO。我们将使用JPA EntityManager来查询数据库。
IArticleDAO.java
package com.concretepage.dao;
import java.util.List;
import com.concretepage.entity.Article;
public interface IArticleDAO {
List getAllArticles();
Article getArticleById(int articleId);
void createArticle(Article article);
void updateArticle(Article article);
void deleteArticle(int articleId);
boolean articleExists(String title, String category);
}
ArticleDAO.java
package com.concretepage.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.concretepage.entity.Article;
@Transactional
@Repository
public class ArticleDAO implements IArticleDAO {
@PersistenceContext
private EntityManager entityManager;
@Override
public Article getArticleById(int articleId) {
return entityManager.find(Article.class, articleId);
}
@SuppressWarnings("unchecked")
@Override
public List getAllArticles() {
String hql = "FROM Article as atcl ORDER BY atcl.articleId DESC";
return (List) entityManager.createQuery(hql).getResultList();
}
@Override
public void createArticle(Article article) {
entityManager.persist(article);
}
@Override
public void updateArticle(Article article) {
Article artcl = getArticleById(article.getArticleId());
artcl.setTitle(article.getTitle());
artcl.setCategory(article.getCategory());
entityManager.flush();
}
@Override
public void deleteArticle(int articleId) {
entityManager.remove(getArticleById(articleId));
}
@Override
public boolean articleExists(String title, String category) {
String hql = "FROM Article as atcl WHERe atcl.title = ? and atcl.category = ?";
int count = entityManager.createQuery(hql).setParameter(1, title)
.setParameter(2, category).getResultList().size();
return count > 0 ? true : false;
}
}
7. 创建 Service
IArticleService.java
package com.concretepage.service;
import java.util.List;
import com.concretepage.entity.Article;
public interface IArticleService {
List getAllArticles();
Article getArticleById(int articleId);
boolean createArticle(Article article);
void updateArticle(Article article);
void deleteArticle(int articleId);
}
ArticleService.java
package com.concretepage.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.concretepage.dao.IArticleDAO;
import com.concretepage.entity.Article;
@Service
public class ArticleService implements IArticleService {
@Autowired
private IArticleDAO articleDAO;
@Override
public Article getArticleById(int articleId) {
Article obj = articleDAO.getArticleById(articleId);
return obj;
}
@Override
public List getAllArticles(){
return articleDAO.getAllArticles();
}
@Override
public synchronized boolean createArticle(Article article){
if (articleDAO.articleExists(article.getTitle(), article.getCategory())) {
return false;
} else {
articleDAO.createArticle(article);
return true;
}
}
@Override
public void updateArticle(Article article) {
articleDAO.updateArticle(article);
}
@Override
public void deleteArticle(int articleId) {
articleDAO.deleteArticle(articleId);
}
}
8. 创建 Controller
找到REST网络服务控制器,它将为CRUD操作公开网络服务方法。
ArticleController.java
package com.concretepage.controller;
import java.util.List;
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.stereotype.Controller;
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.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.RequestParam;
import org.springframework.web.util.UriComponentsBuilder;
import com.concretepage.entity.Article;
import com.concretepage.service.IArticleService;
@Controller
@RequestMapping("user")
@CrossOrigin(origins = {"http://localhost:4200"})
public class ArticleController {
@Autowired
private IArticleService articleService;
@GetMapping("article")
public ResponseEntity getArticleById(@RequestParam("id") String id) {
Article article = articleService.getArticleById(Integer.parseInt(id));
return new ResponseEntity(article, HttpStatus.OK);
}
@GetMapping("all-articles")
public ResponseEntity> getAllArticles() {
List list = articleService.getAllArticles();
return new ResponseEntity>(list, HttpStatus.OK);
}
@PostMapping("article")
public ResponseEntity createArticle(@RequestBody Article article, UriComponentsBuilder builder) {
boolean flag = articleService.createArticle(article);
if (flag == false) {
return new ResponseEntity(HttpStatus.CONFLICT);
}
HttpHeaders headers = new HttpHeaders();
headers.setLocation(builder.path("/article?id={id}").buildAndExpand(article.getArticleId()).toUri());
return new ResponseEntity(headers, HttpStatus.CREATED);
}
@PutMapping("article")
public ResponseEntity updateArticle(@RequestBody Article article) {
articleService.updateArticle(article);
return new ResponseEntity(article, HttpStatus.OK);
}
@DeleteMapping("article")
public ResponseEntity deleteArticle(@RequestParam("id") String id) {
articleService.deleteArticle(Integer.parseInt(id));
return new ResponseEntity(HttpStatus.NO_CONTENT);
}
}
@CrossOrigin注解可以处理跨资源共享(CORS)。这个注解可以在RESTful Web服务控制器的类级和方法级使用。在我们的例子中,Angular项目将在以下URL上运行。
http://localhost:4200
而REST网络服务项目将在以下URL上运行。
http://localhost:8080
为了让我们的Angular项目脚本能够访问网络服务,我们需要对@CrossOrigin进行如下配置
@CrossOrigin(origins = {"http://localhost:4200"})
我们在类的层面上使用它,这样所有的REST网络服务方法都可以在我们的Angular应用程序中使用。
9. 创建 Java 主类来运行 Spring Boot 应用程序创建一个带有main()方法的java类,它将调用SpringApplication.run()来运行应用程序。
MyApplication.java
package com.concretepage;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
我们需要用@SpringBootApplication来注解上述类,这相当于@Configuration、@EnableAutoConfiguration和@ComponentScan注解。
运行 REST 网络服务和 Angular 应用程序找到运行REST网络服务应用程序和angular应用程序的步骤。
1. 使用 Spring Boot 运行 REST 网络服务要运行REST网络服务应用程序,首先在MySQL中创建表,如例子中给出的。现在我们可以通过以下方式运行REST网络服务。
1. 使用Eclipse: 使用本页面下载部分给出的下载链接下载Web服务项目的源代码。将该项目导入eclipse中。使用命令提示符,进入项目的根文件夹并运行。
mvn clean eclipse:eclipse
然后在eclipse中刷新该项目。通过点击Run as -> Java Application来运行主类MyApplication。嵌入式tomcat服务器将被启动。
2. 使用Maven命令: 下载Web服务项目的源代码。使用命令提示符进入项目的根文件夹,运行命令。
mvn spring-boot:run
嵌入式tomcat服务器将被启动。
3. 使用可执行的JAR:使用命令提示符,进入项目的根文件夹并运行命令。
mvn clean package
我们将在目标文件夹中得到可执行的JAR包spring-boot-demo-0.0.1-SNAPSHOT.jar。以下列方式运行这个JAR
java -jar target/spring-boot-demo-0.0.1-SNAPSHOT.jar
嵌入式tomcat服务器将被启动。
2. 使用 Angular CLI 运行 Angular 应用程序要运行angular应用程序,找到以下步骤。
1. 访问链接安装Angular CLI QUICKSTART。
2. 使用本页面下载部分给出的下载链接下载Angular项目源代码。
3. 在你的angular CLI应用程序中,用下载的文件夹替换src文件夹。
4. 运行ng serve命令。
5. 我们的Angular应用程序已经在以下网址上就绪。
http://localhost:4200
a. 访问链接,当我们点击CREATE按钮而不输入数据时,我们会得到以下验证信息。
在表格字段中输入数据,然后点击CREATE按钮,创建新的文章。
b. 当我们点击 "EDIT"按钮时,我们在表格字段中加载文章来更新,如图所示。
点击UPDATE按钮来更新给定文章ID的文章。通过点击 "Go Back"按钮,我们可以返回到 "CREATE"页面。使用DELETE按钮,我们可以删除该文章。
【1】Angular Http
【2】Spring Boot REST + JPA + Hibernate + MySQL Example
【3】Angular 2 Http get() Parameters + Headers + URLSearchParams + RequestOptions Example
【4】Angular 2 Http post() Example
【5】Spring Boot REST + Angular 2/4 + JPA + Hibernate + MySQL CRUD Example
提取码:mao4
【1】服务端代码
【2】客户端代码




