前言
注:本篇为纯实践篇,主要用于技术整合,介绍如何搭建一个完整全面的Web项目。如果对于技术原理还不了解的童鞋可点击下方链接,学习后在来~
H2数据库教程:H2数据库入门
缓存使用教程:在Spring中使用缓存
Spring Data JPA使用教程:Spring Data JPA学习导引
使用Redis缓存(与本章内容基本相同,唯一的差别是使用Redis缓存):在SpringBoot中使用Redis缓存
1、环境配置
pom.xml需要引入的包如下:
<?xml version="1.0" encoding="UTF-8"?>
<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.scb</groupId>
<artifactId>h2demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>h2demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</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-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 阿里系的Druid依赖包 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
<!-- Druid 依赖 log4j包 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- Ehcache依赖包 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>4.1.0.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
然后在application.yml中进行配置
spring:
jpa:
generate-ddl: false
show-sql: true //输出sql语句
hibernate:
ddl-auto: none
h2:
console:
path: /h2-console
enabled: true
settings:
web-allow-others: true
datasource:
platform: h2
url: jdbc:h2:~/test //test是我创建的数据库名
username: sa
password:
schema: classpath:schema.sql //程序运行时,使用schema.sql来创建数据库中的表
data: classpath:data.sql //程序运行时,使用data.sql来创建初始数据
name: test
type: com.alibaba.druid.pool.DruidDataSource //使用阿里druid连接池
druid:
min-idle: 2
initial-size: 5
max-active: 10
max-wait: 5000
validation-query: select 1
server:
port: 8080
schema.sql代码如下:
drop table if exists staff;
CREATE TABLE staff(
id INTEGER not null primary key,
name char(20),
age INTEGER
);
data.sql代码如下:
insert into staff
values (1, '张三', 26);
insert into staff
values (2, '李四', 23);
insert into staff
values (3,'张某', 26);
insert into staff
values (4,'王五', 26);
insert into staff
values (5,'甲六', 26);
insert into staff
values (6,'已七', 26);
insert into staff
values (7,'丙八', 26);
insert into staff
values (8,'丁九', 26)
2、代码详解
要使用Ehcache进行缓存的话,需要对其进行配置。首先创建一个ehcache.xml,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false">
<!-- 指定一个文件目录,当EhCache把数据写到硬盘上时,将把数据写到这个文件目录下 -->
<diskStore path="java.io.tmpdir"/>
<!-- 设定缓存的默认数据过期策略 -->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<cache
name="myCache"
maxElementsInMemory="10000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="30"
timeToLiveSeconds="60"
memoryStoreEvictionPolicy="LFU"
/>
</ehcache>
在创建一个CachingConfig类进行配置
package com.scb.h2demo.config;
import net.sf.ehcache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
@Configuration
@EnableCaching
public class CachingConfig {
/*
* 配置EhCacheCacheManager
*/
@Bean
public EhCacheCacheManager cacheManager(CacheManager cacheManager){
EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager(cacheManager);
return ehCacheCacheManager;
}
/*
* 配置EhCacheManagerFactoryBean
*/
@Bean
public EhCacheManagerFactoryBean ehCacheManagerFactoryBean(){
EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean ();
cacheManagerFactoryBean.setConfigLocation (new ClassPathResource("ehcache.xml"));
cacheManagerFactoryBean.setShared (true);
return cacheManagerFactoryBean;
}
}
接下来创建实体层对象Staff
package com.scb.h2demo.entity;
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
@Data
@Entity
@Table(name="staff")
public class Staff implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name="name")
private String name;
@Column(name="age")
private Integer age;
}
在创建DAO层
package com.scb.h2demo.dao;
import com.scb.h2demo.entity.Staff;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface StaffRepository extends JpaRepository<Staff,Integer> {
@Query("from Staff where name like %:name%")
List<Staff> getByNameIsLike(@Param("name")String name);
Staff getById(Integer id);
void deleteById(Integer id);
}
接着创建Service层(在Service层接口上,应用缓存,此时其所有实现类都将继承缓存。)
package com.scb.h2demo.service;
import com.scb.h2demo.entity.Staff;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import java.util.List;
public interface IStaffService {
List<Staff> getAllList();
Page<Staff> queryAllStaffList(int pageNum,int pageSize);
List<Staff> getByNameIsLike(String name);
@Cacheable("myCache")
Staff findOne(Integer id);
@CachePut(value="myCache", key = "#result.id")
Staff insert(Staff staff);
@CacheEvict("myCache")
void remove(Integer id);
@CacheEvict("myCache")
void deleteAll();
}
package com.scb.h2demo.service;
import com.scb.h2demo.dao.StaffRepository;
import com.scb.h2demo.entity.Staff;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.List;
@Service
@Transactional
public class StaffServiceImpl implements IStaffService {
@Autowired
private StaffRepository staffRepository;
@Override
public List<Staff> getAllList() {
return staffRepository.findAll();
}
@Override
public Page<Staff> queryAllStaffList(int pageNum,int pageSize) {
Sort sort=new Sort(Sort.Direction.DESC,"id");
Pageable pageable=new PageRequest(pageNum,pageSize,sort);
return staffRepository.findAll(pageable);
}
@Override
public List<Staff> getByNameIsLike(String name) {
return staffRepository.getByNameIsLike(name);
}
@Override
public Staff findOne(Integer id) {
return staffRepository.getById(id);
}
@Override
public Staff insert(Staff staff) {
return staffRepository.save(staff);
}
@Override
public void remove(Integer id) {
staffRepository.deleteById(id);
}
@Override
public void deleteAll() {
staffRepository.deleteAll();
}
}
最后是Controller层(RESTful风格)
package com.scb.h2demo.controller;
import com.scb.h2demo.entity.Staff;
import com.scb.h2demo.exception.Error;
import com.scb.h2demo.exception.StaffNotFoundException;
import com.scb.h2demo.service.IStaffService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping(path="/staffRest",produces="application/json;charset=utf-8")
public class RestServiceController {
@Autowired
private IStaffService staffService;
@ExceptionHandler(StaffNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public Error staffNotFound(StaffNotFoundException e){
Integer id=e.getStaffId();
return new Error(4,"Staff ["+id+"] not found");
}
@GetMapping("/{id}")
public Staff staffById(@PathVariable Integer id){
Staff staff=staffService.findOne(id);
if(staff==null){
throw new StaffNotFoundException(id);
}
return staff;
}
@GetMapping
public List<Staff> getAllStaffs(){
return staffService.getAllList();
}
@PostMapping
public Staff createStaff(Staff staff){
return staffService.insert(staff);
}
@PutMapping
public Staff updateStaff(Staff staff){
return staffService.insert(staff);
}
@DeleteMapping("/{id}")
public Staff deleteStaffById(@PathVariable Integer id){
Staff staff=staffService.findOne(id);
if(staff==null){
throw new StaffNotFoundException(id);
}
staffService.remove(id);
return staff;
}
@DeleteMapping
public List<Staff> deleteAllStaffs(){
List<Staff> staffList=staffService.getAllList();
staffService.deleteAll();
return staffList;
}
}
其中Error是自定义的POJO类,用来记录异常。
package com.scb.h2demo.exception;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public class Error {
private int code;
private String message;
}
而StaffNotFoundException是自定义的异常类
package com.scb.h2demo.exception;
public class StaffNotFoundException extends RuntimeException {
private Integer staffId;
public StaffNotFoundException(Integer staffId){
this.staffId=staffId;
}
public Integer getStaffId(){
return staffId;
}
}
现在,整个项目已经开发完毕,接下来,让我们使用Postman来进行测试。
3、使用Postman进行测试
当我们以GET请求访问 http://localhost:8080/staffRest/1 时,将返回 id 为1的员工。
如果以GET请求访问 http://localhost:8080/staffRest/9 时,即访问不存在的员工时,将返回Error对象:
而以GET请求访问 http://localhost:8080/staffRest 时,返回的是所有员工列表。
以POST请求访问 http://localhost:8080/staffRest 并加上staff参数时,将create一个staff
以PUT请求访问 http://localhost:8080/staffRest 并加上staff参数时,将update一个staff(其实update和create操作都一样,通过调用JpaRepository的insert方法实现,而insert方法,他首先会查找是否存在相同主键,如果存在则update,否则create)
最后,以DELETE请求访问 http://localhost:8080/staffRest/1 时,是删除id为1的员工。
以DELETE请求访问 http://localhost:8080/staffRest 时,是删除所有的员工。