16、Spring开发:从图像服务器到数据库REST服务的实现

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
  1. 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
  1. 在PC上使用“mvn clean package”命令将应用打包成jar文件。
  2. 将“target”目录下的“java - spring - rest - db - 0.0.1 - SNAPSHOT.jar”文件复制到树莓派的“/home/pi/dbapp”目录。
  3. 在树莓派上运行应用:
$ 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();
}
...

}
``` |

【EI复现】基于深度强化学习的微能源网能量管理与优化策略研究(Python代码实现)内容概要:本文围绕“基于深度强化学习的微能源网能量管理与优化策略”展开研究,重点利用深度Q网络(DQN)等深度强化学习算法对微能源网中的能量调度进行建模与优化,旨在应对可再生能源出力波动、负荷变化及运行成本等问题。文中结合Python代码实现,构建了包含光伏、储能、负荷等元素的微能源网模型,通过强化学习智能体动态决策能量分配策略,实现经济性、稳定性和能效的多重优化目标,并可能与其他优化算法进行对比分析以验证有效性。研究属于电力系统与人工智能交叉领域,具有较强的工程应用背景和学术参考价值。; 适合人群:具备一定Python编程基础和机器学习基础知识,从事电力系统、能源互联网、智能优化等相关方向的研究生、科研人员及工程技术人员。; 使用场景及目标:①学习如何将深度强化学习应用于微能源网的能量管理;②掌握DQN等算法在实际能源系统调度中的建模与实现方法;③为相关课题研究或项目开发提供代码参考和技术思路。; 阅读建议:建议读者结合提供的Python代码进行实践操作,理解环境建模、状态空间、动作空间及奖励函数的设计逻辑,同时可扩展学习其他强化学习算法在能源系统中的应用。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值