Spring开发:从图像服务器到数据库REST服务的实现
1. 扩展Web界面与Swagger配置
1.1 使用Thymeleaf扩展Web界面
若想为Web界面添加更多页面并提升其外观和用户体验,可考虑使用Thymeleaf。它是一个服务器端Java模板引擎,能将HTML文件与代码分离,使代码更简洁,网页更易维护。Thymeleaf官网: https://www.thymeleaf.org/
1.2 Swagger配置
为配置Swagger,需添加一个类。为保持结构清晰,可在新包“configuration”中创建文件“SwaggerConfig.java”,代码如下:
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
此简单配置会自动在“localhost:8080/swagger-ui.html”创建一个包含应用中所有Web服务信息的网页。
1.3 在树莓派上运行图像服务器
步骤如下:
1. 修改 application.properties 文件,指定树莓派上用于加载图像的目录。
2. 使用“mvn clean package”命令构建应用。
3. 将“target”目录下的“java - spring - image - server - 0.0.1 - SNAPSHOT.jar”文件复制到树莓派。
4. 在树莓派上使用“java -jar java - spring - image - server - 0.0.1 - SNAPSHOT.jar”命令启动应用。
5. 在PC浏览器中访问树莓派的IP地址,如“http://192.168.0.223:8080/files”。
通过以上步骤,一个简单的图像服务器就搭建完成了。从一个空的Spring项目开始,添加一个类(Swagger并非必需,仅作示例)即可。
2. 树莓派上的数据库REST服务示例
2.1 项目概述
该项目是一个概念验证应用,用于在树莓派的数据库中存储数据。数据可由任何提供REST服务的源提供。项目使用H2数据库,它是一个纯Java解决方案,无需额外安装,是应用的一部分。近年来,H2已发展成为一个非常稳定的数据库解决方案,适合嵌入式项目。JPA是用于与数据库“通信”的持久化规范,可帮助定义哪些对象(实体)能以何种结构(表)存储在数据库中。
2.2 pom.xml设置
从一个最小的Spring项目开始,修改 pom.xml 文件,添加正确的设置和更多依赖:
<groupId>be.webtechie</groupId>
<artifactId>java-spring-rest-db</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>java-spring-rest-db</name>
<description>Spring Boot project to store data in a H2 database</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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-data-rest</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2.3 创建数据库实体
JPA的主要优势在于可在代码中定义数据库结构,无需手动创建和定义表。示例中使用的模型如下:
- 传感器(Sensor)
- 每个传感器有无限数量的测量值(Measurement)
为实现该模型,需在“entity”包中添加两个类。
2.3.1 SensorEntity.java
/**
* Maps a database entity from the table SENSORS to a Java object.
* The ID is marked as the unique identifier.
*/
@Entity
@Table(name = "SENSORS",
uniqueConstraints={@UniqueConstraint(
name="UN_SENSOR_ID",
columnNames={"ID"})})
public class SensorEntity {
/**
* Auto-generated identifier to have a unique key for this sensor.
*/
@Id
@GeneratedValue
private Long id;
/**
* Name of the sensor, a required value.
*/
@Column(nullable = false)
private String name;
/**
* Relationship between the sensor and a list of measurements.
*/
@OneToMany(
mappedBy = "sensorEntity",
cascade = {CascadeType.MERGE},
fetch = FetchType.LAZY
)
private Set<MeasurementEntity> measurements = new HashSet<>();
/**
* No-argument constructor is needed for JPA.
*/
public SensorEntity() {
// NOP
}
/**
* Constructor with a name value.
* @param name
*/
public SensorEntity(String name) {
this.name = name;
}
// Getters and setters needed by JPA and the code.
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<MeasurementEntity> getDataEntries() {
return measurements;
}
public void setDataEntries(Set<MeasurementEntity> dataEntries) {
this.measurements = dataEntries;
}
}
2.3.2 MeasurementEntity.java
/**
* Maps a database entity from the table MEASUREMENTS to a Java object.
* The ID is marked as the unique identifier.
*/
@Entity
@Table(name = "MEASUREMENTS",
uniqueConstraints={@UniqueConstraint(
name="UN_MEASUREMENT_ID",
columnNames={"ID"})})
public class MeasurementEntity {
/**
* Auto-generated identifier to have a unique key for this sensor.
*/
@Id
@GeneratedValue
private Long id;
/**
* Relationship between the measurement and its sensor.
*/
@ManyToOne
@JoinColumn(
name = "SENSOR_ID",
nullable = false,
foreignKey = @ForeignKey(name="FK_MEASUREMENT_SENSOR"))
private SensorEntity sensorEntity;
/**
* Timestamp of the measurement.
*/
@Column(nullable = false)
private long timestamp;
/**
* Key for the type of measurement, e.g. "temperature", "distance"...
*/
@Column(nullable = false)
private String key;
/**
* Value of the measurement
*/
@Column(nullable = false)
private double value;
/**
* No-argument constructor is needed for JPA.
*/
public MeasurementEntity() {
// NOP
}
/**
* Constructor with a name value.
* @param sensorEntity
* @param timestamp
* @param key
* @param value
*/
public MeasurementEntity(SensorEntity sensorEntity, long timestamp,
String key, double value) {
this.sensorEntity = sensorEntity;
this.timestamp = timestamp;
this.key = key;
this.value = value;
}
// Getters and setters needed by JPA and the code.
...
@JsonIgnore
public SensorEntity getSensor() {
return sensorEntity;
}
...
}
注意 MeasurementEntity 类中 getSensor() 方法上的 @JsonIgnore 注解,它用于避免在通过REST服务加载数据时出现无限循环。否则,应用会尝试将包含传感器的测量值嵌套到包含测量值的传感器中,导致错误。
2.4 存储数据到数据库
使用两个仓库类和最少的代码来实现数据存储。
2.4.1 SensorRepository.java
@Repository
public interface SensorRepository extends JpaRepository<SensorEntity, Long>{
Page<SensorEntity> findAll(Pageable pageable);
List<SensorEntity> findAllByName(String name);
SensorEntity findById(@Param("id") long id);
}
2.4.2 MeasurementRepository.java
@Repository
public interface MeasurementRepository extends
JpaRepository<MeasurementEntity, Long>{
Page<MeasurementEntity> findAll(Pageable pageable);
}
2.5 添加REST服务
为了让任何设备都能通过网络读写数据库中的数据,需添加REST服务。
2.5.1 SensorResource.java
@RestController
public class SensorResource {
@Autowired
private SensorRepository sensorRepository;
@GetMapping("/sensor")
public List<SensorEntity> retrieveAllSensors() {
return sensorRepository.findAll();
}
@GetMapping("/sensor/{id}")
public SensorEntity retrieveSensor(@RequestParam long id) {
return sensorRepository.findById(id);
}
@PostMapping("/sensor")
public ResponseEntity createSensor(@RequestParam String name) {
List<SensorEntity> sensorEntities = sensorRepository.findAllByName(name);
if (sensorEntities.size() > 0) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body("There is already a sensor with the name: " + name);
}
SensorEntity sensorEntity = new SensorEntity(name);
sensorRepository.save(sensorEntity);
return ResponseEntity.ok(sensorEntity);
}
}
2.5.2 MeasurementResource.java
@RestController
public class MeasurementResource {
@Autowired
private SensorRepository sensorRepository;
@Autowired
private MeasurementRepository measurementRepository;
@GetMapping("/measurement")
public List<MeasurementEntity> retrieveAllMeasurements() {
return measurementRepository.findAll();
}
@PostMapping("/measurement")
public ResponseEntity createMeasurement(
@RequestParam long sensorId,
@RequestParam String key,
@RequestParam double value) {
SensorEntity sensorEntity = sensorRepository.findById(sensorId);
if (sensorEntity == null) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body("No sensor defined with the ID: " + sensorId);
}
MeasurementEntity measurementEntity = new MeasurementEntity(
sensorEntity, System.currentTimeMillis(), key, value);
measurementRepository.save(measurementEntity);
return ResponseEntity.ok().build();
}
}
2.6 添加Swagger
为了快速测试应用,可添加Swagger。在“configuration”包中创建“SwaggerConfig.java”文件,代码与前面的Swagger配置相同。
2.7 运行应用并使用REST服务
步骤如下:
1. 运行“JavaSpringRestDbApplication.java”中的主函数。
2. 浏览器访问“http://localhost:8080/swagger-ui.html”。
3. 在Swagger中点击“POST /sensor”并“Try it out”,输入一些值(如“temp1”,“temp2”等),每次点击“Execute”创建传感器。若尝试添加相同名称的传感器,会返回错误。
4. 可通过Swagger中的GET请求查看创建的记录,也可直接在浏览器中访问“http://localhost:8080/sensor”。
5. 为某个传感器(如ID为1)存储测量值,然后通过访问“http://localhost:8080/sensor/1”获取该传感器的测量值。
2.8 在H2控制台查看数据
在“src > main > resources > application.properties”文件中添加以下设置:
spring.h2.console.enabled=true
重启应用,由于H2默认将数据存储在内存中,测试数据会丢失,需重新使用Swagger生成数据。然后在浏览器中访问“http://localhost:8080/h2 - console”,确保使用“jdbc:h2:mem:testdb”作为“JDBC URL”登录。登录后可看到数据库结构与代码中定义的表一致。
2.9 在树莓派上运行配置
步骤如下:
1. 在树莓派上创建应用目录:
$ mkdir /home/pi/dbapp
$ cd /home/pi/dbapp
$ mkdir config
$ nano config/application.properties
- 在
application.properties文件中添加以下内容:
spring.datasource.url=jdbc:h2:file:/home/pi/dbapp/spring-boot-h2-db
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
spring.jpa.hibernate.ddl-auto=update
- 在PC上使用“mvn clean package”命令将应用打包成jar文件。
- 将“target”目录下的“java - spring - rest - db - 0.0.1 - SNAPSHOT.jar”文件复制到树莓派的“/home/pi/dbapp”目录。
- 在树莓派上运行应用:
$ java -jar java-spring-rest-db-0.0.1-SNAPSHOT.jar
与在开发PC上相比,应用在树莓派上的启动时间会更长,但启动后可通过网络中任何PC上的浏览器访问树莓派的IP地址(如“192.168.0.223”)来使用Swagger和REST服务。
2.10 总结
这只是一个包含两个表的基本示例,但展示了如何使用最少的代码快速构建数据库应用。REST服务可供所有能通过网络连接到树莓派的设备使用,它们可将数据存储在一个中央位置或读取该数据。REST服务提供的JSON数据可被各种设备或应用用于可视化结果。
2.11 与Vlad Mihalcea的访谈
Vlad Mihalcea是Java冠军、Hypersistence的CEO、JPA专家和Hibernate ORM顶级提交者之一。在访谈中,他分享了对Java、JPA和Hibernate的看法:
- 贡献动机:JPA和Hibernate在构建数据访问层时非常方便,被大多数企业应用使用。他的主要动机是教导人们如何充分利用JPA、Hibernate或他们使用的数据库系统。为此,他撰写了《High - Performance Java Persistence》,并启动了Hibernate Types和Hypersistence Optimizer等项目。
- 工作与生活平衡:起初,全职工作的同时在博客上写作或在StackOverflow上回答问题很困难。但4年前,他辞去工作,将对高性能数据库系统的热情转化为全职工作。现在,博客、培训和写书是他工作的一部分,他能在每周不到40小时的时间内完成所有工作。在家工作还让他有更多时间陪伴家人。
- 对Java和数据库发展的看法:Java新的开发周期(每6个月发布一次)使Java和JDK开发者能够引入许多有用的功能,未来还有Project Loom和Valhalla等项目。
- 学习Java的时机:Java拥有丰富的开源框架生态系统,能在从小型设备到超级计算机的各种系统上运行,因此现在是学习Java的绝佳时机。
- 正在进行的项目:他目前正在进行Hypersistence Optimizer项目,该项目可自动检测JPA和Hibernate问题,让开发者专注于数据访问逻辑,而不是解决性能相关问题。
综上所述,Spring开发结合树莓派和数据库技术,能以简洁的方式实现强大的功能,无论是图像服务器还是数据库REST服务,都为开发者提供了便捷的解决方案。同时,Java和相关框架的发展也为开发者带来了更多的可能性和机遇。
3. 技术要点分析与总结
3.1 关键技术选型分析
3.1.1 Thymeleaf
Thymeleaf作为服务器端Java模板引擎,其核心优势在于实现了HTML文件与代码的分离。这种分离使得代码结构更加清晰,便于维护和扩展。在大型项目中,不同团队成员可以分别负责前端页面设计和后端代码开发,提高开发效率。例如,前端设计师可以专注于HTML页面的美化和布局,而后端开发人员则可以专注于业务逻辑的实现。
3.1.2 H2数据库
H2数据库是一个纯Java的解决方案,无需额外安装,可作为应用的一部分。它在嵌入式项目中表现出色,具有良好的稳定性。对于树莓派这样的资源有限的设备,H2数据库占用资源少,启动速度快。同时,H2数据库支持内存模式和文件模式,可根据实际需求灵活配置。在测试环境中,可以使用内存模式快速验证功能;在生产环境中,可以使用文件模式确保数据的持久化。
3.1.3 JPA
JPA(Java Persistence API)是Java的持久化规范,它简化了数据库操作。通过JPA,开发者可以使用面向对象的方式操作数据库,无需编写复杂的SQL语句。JPA提供了丰富的注解,如 @Entity 、 @Id 、 @Column 等,用于定义实体类和数据库表之间的映射关系。这种方式使得数据库结构的定义更加直观,代码可读性更高。
3.2 代码结构与设计模式
3.2.1 实体类设计
在数据库实体设计中,使用了 SensorEntity 和 MeasurementEntity 两个类来映射数据库表。通过注解的方式,明确了实体类与数据库表之间的关系,以及字段的约束条件。例如, @Entity 注解表示该类是一个数据库实体, @Id 注解表示该字段是主键, @Column 注解可以设置字段的属性,如是否可为空等。这种设计模式使得代码与数据库结构紧密结合,便于维护和扩展。
3.2.2 仓库类设计
SensorRepository 和 MeasurementRepository 继承自 JpaRepository ,利用Spring Data JPA的特性,自动实现了大部分CRUD操作。开发者只需定义额外需要的方法,Spring会根据方法名自动生成相应的SQL语句。例如, findAllByName 方法会根据传感器名称查询所有匹配的传感器记录。这种设计模式减少了重复代码的编写,提高了开发效率。
3.2.3 REST服务设计
通过 SensorResource 和 MeasurementResource 两个类,将数据库操作封装成REST服务。使用 @RestController 注解将类标记为REST控制器,使用 @GetMapping 、 @PostMapping 等注解定义不同的HTTP请求方法。这种设计模式使得服务接口清晰,便于客户端调用。同时,通过 ResponseEntity 类可以灵活处理响应结果,返回不同的HTTP状态码和响应体。
3.3 性能与优化建议
3.3.1 数据库性能
在使用H2数据库时,对于大量数据的读写操作,可考虑使用连接池来提高数据库连接的复用性,减少连接开销。同时,合理设置索引可以提高查询效率。例如,在 SensorEntity 的 name 字段上创建索引,可以加快根据名称查询传感器的速度。
3.3.2 代码性能
在处理数据时,尽量减少不必要的对象创建和内存占用。例如,在使用 Pageable 进行分页查询时,合理设置每页的记录数,避免一次性加载过多数据。同时,对于频繁调用的方法,可以考虑使用缓存机制,减少重复计算。
3.3.3 网络性能
在树莓派上运行应用时,由于网络带宽和性能有限,可考虑对数据进行压缩传输,减少数据传输量。同时,优化REST服务的响应时间,避免长时间的阻塞操作。
3.4 应用场景与拓展
3.4.1 物联网数据存储
该项目可以作为物联网数据存储的基础架构。传感器设备可以通过REST服务将采集到的数据发送到树莓派上的数据库中进行存储。例如,温度传感器、湿度传感器等可以定期将测量数据发送到服务器,实现数据的集中管理和分析。
3.4.2 数据可视化
REST服务提供的JSON数据可以被各种前端应用或可视化工具使用。例如,使用JavaScript库(如Chart.js)可以将传感器数据以图表的形式展示出来,方便用户直观地查看数据变化趋势。
3.4.3 分布式系统
可以将该项目扩展为分布式系统,多个树莓派节点可以通过网络连接,共同存储和处理数据。通过负载均衡和数据同步机制,可以提高系统的可用性和性能。
3.5 总结与展望
本文详细介绍了Spring开发中从图像服务器到数据库REST服务的实现过程,涵盖了Web界面扩展、Swagger配置、数据库实体设计、REST服务开发等多个方面。通过使用Thymeleaf、H2数据库和JPA等技术,实现了代码的简洁性和可维护性。同时,通过树莓派的应用,展示了这些技术在嵌入式设备上的可行性。
未来,随着Java和相关技术的不断发展,我们可以期待更多的功能和优化。例如,利用Java的新特性(如Project Loom和Valhalla)提高系统的并发性能和资源利用率;使用更先进的数据库技术(如NoSQL数据库)处理复杂的数据结构和大规模数据。此外,结合人工智能和机器学习技术,可以对存储的数据进行深度分析和挖掘,为用户提供更有价值的信息。
总之,Spring开发结合数据库和树莓派技术,为开发者提供了一个强大而灵活的解决方案,无论是在小型项目还是大型企业级应用中,都具有广阔的应用前景。
3.6 流程图总结
graph LR
A[开始项目] --> B[配置Thymeleaf和Swagger]
B --> C[创建图像服务器]
C --> D[配置数据库REST服务]
D --> E[修改pom.xml]
E --> F[创建数据库实体]
F --> G[创建仓库类]
G --> H[添加REST服务]
H --> I[添加Swagger配置]
I --> J[运行应用]
J --> K[测试REST服务]
K --> L[在H2控制台查看数据]
L --> M[配置树莓派运行]
M --> N[完成项目]
3.7 表格总结
| 技术点 | 作用 | 关键代码示例 |
|---|---|---|
| Thymeleaf | 分离HTML与代码,提高可维护性 | 无(官网:https://www.thymeleaf.org/ ) |
| H2数据库 | 纯Java数据库,用于存储数据 | spring.datasource.url=jdbc:h2:file:/home/pi/dbapp/spring-boot-h2-db |
| JPA | 简化数据库操作,定义实体与表的映射 | @Entity 、 @Id 、 @Column 等注解 |
| Swagger | 自动生成API文档,方便测试 | |
| ```java | ||
| @Configuration | ||
| @EnableSwagger2 | ||
| public class SwaggerConfig { | ||
| @Bean | ||
| public Docket api() { | ||
| return new Docket(DocumentationType.SWAGGER_2) | ||
| .select() | ||
| .apis(RequestHandlerSelectors.any()) | ||
| .paths(PathSelectors.any()) | ||
| .build(); | ||
| } | ||
| } | ||
| ``` | ||
| REST服务 | 提供数据读写接口 | |
| ```java | ||
| @RestController | ||
| public class SensorResource { | ||
| @Autowired | ||
| private SensorRepository sensorRepository; |
@GetMapping("/sensor")
public List<SensorEntity> retrieveAllSensors() {
return sensorRepository.findAll();
}
...
}
``` |
超级会员免费看
17万+

被折叠的 条评论
为什么被折叠?



