体验Spring Boot 3.0.x和Spring JPA集成
推荐之前阅读
- 在AlmaLinux 8上安装Oracle Database 23c
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(预告篇)
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列1) - Spring Boot 3.0.x和Oracle Database 23c Database API for MongoDB的示例
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列2) - 使用MongoDB Compass管理Oracle Database 23c的Oracle Database API for MongoDB
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列3) - 往Oracle Database 23c(beta)里导入示例数据
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列4) - Oracle Database 23c(beta)的JSON RELATIONAL DUALITY示例
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列5) - Spring Boot 3.0.x和Cassandra的示例
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列6) - Spring Boot 3.0.x和ActiveMQ Artemis的示例
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列7) - Spring Boot 3.0.x和RabbitMQ的示例
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列8) - Spring Boot 3.0.x和Kafka的示例
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列9) - Oracle Database 23c(beta)容器化安装ORDS
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列10) - Oracle Database 23c(beta)替换Istio示例Bookinfo中的MongoDB
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列11) - Spring Boot 3.0.x和Spring Boot Admin的示例
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列12) - Oracle Database 23c(beta)和Golang的示例
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列13) - Oracle Database 23c(beta)替换Istio示例Bookinfo中Details的外部服务
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列14) - 体验Spring Boot 3.0.x中的spring-native
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列15) - 体验Spring Boot 3.0.2中的spring-native和spring aop
- Spring Boot 3.0.x和Oracle Database 23c(beta)和GraalVM Enterprise 22.3(JDK17)的综合体验(系列16) - 体验Spring Boot 3.0.x和Mustache及React的集成
示例代码
https://github.com/engchina/springboot3withdb23c/tree/main/SpringBoot3WithJPA
SpringBoot pom.xml的配置(相关部分)
<properties>
<java.version>17</java.version>
<project.build.souceEncoding>UTF-8</project.build.souceEncoding>
<spring-boot.build-image.imageName>springboot3withjpa</spring-boot.build-image.imageName>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mustache</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.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>springboot3withjpa</finalName>
<plugins>
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<version>${hibernate.version}</version>
<executions>
<execution>
<id>enhance</id>
<goals>
<goal>enhance</goal>
</goals>
<configuration>
<enableLazyInitialization>true</enableLazyInitialization>
<enableDirtyTracking>true</enableDirtyTracking>
<enableAssociationManagement>true</enableAssociationManagement>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<configuration>
<imageName>springboot3withjpa</imageName>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
VideoSearch.java 代码
package com.oracle.db23c.springboot3withjpa.vo;
public record VideoSearch(String name, String description) {
}
UniversalSearch.java 代码
package com.oracle.db23c.springboot3withjpa.vo;
public record UniversalSearch(String value) {
}
VideoEntity.java 代码
package com.oracle.db23c.springboot3withjpa.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
// @Entity是 JPA 的注解,用于指示这是 JPA 管理的类型。
@Entity
public class VideoEntity {
// @Id是 JPA 标记主键的注解。
// @GeneratedValue 是一个 JPA 注解,用于将key的生成卸载到 JPA 提供程序。
private @Id
@GeneratedValue Long id;
private String name;
private String description;
// JPA 需要一个公共或受保护的无参数构造函数方法。
public VideoEntity() {
this(null, null);
}
// 我们还有一个未提供 id 字段的构造函数:一个专为在数据库中创建新条目而设计的构造函数。
// 当 id 字段为 null 时,它会告诉 JPA 我们要在表中创建一个新行。
public VideoEntity(String name, String description) {
this.id = null;
this.description = description;
this.name = name;
}
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 String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
NewVideo.java 代码
package com.oracle.db23c.springboot3withjpa.entity;
public record NewVideo(String name, String description) {
}
VideoRepository.java 代码
package com.oracle.db23c.springboot3withjpa.repository;
import com.oracle.db23c.springboot3withjpa.entity.VideoEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
// 它使用两个通用参数VideoEntity 和 Long(域类型和主键类型)扩展了 JpaRepository
// JpaRepository是一个Spring Data JPA接口,包含一组已经支持的更改替换更新删除(CRUD)操作。
public interface VideoRepository extends JpaRepository
<VideoEntity, Long> {
// 创建自定义查找器。我们永远不必实现此方法。Spring Data将按照本节所述为我们完成此操作。
// 返回类型为 List<VideoEntity>,指示它必须返回存储库域类型的列表。
// 所有以 findBy 开头的存储库方法都标记为查询。
// select video.* from VideoEntity video where video.name = ?1
List<VideoEntity> findByName(String name);
// @Query是Spring Data JPA提供自定义JPQL语句的方式。
@Query("select v from VideoEntity v where v.name = ?1")
List<VideoEntity> findCustomerReport(String name);
// Spring Data JPA 3.0 包含 JSqlParser,一个 SQL 解析库,可以按如下方式编写查询
@Query(value="select * from VIDEO_ENTITY where NAME = ?1", nativeQuery=true)
List<VideoEntity> findCustomWithPureSql(String name);
List<VideoEntity> findByNameContainsIgnoreCase(String partialName);
List<VideoEntity> findByDescriptionContainsIgnoreCase(String partialDescription);
List<VideoEntity> findByNameContainsOrDescriptionContainsAllIgnoreCase(String partialName,
String partialDescription);
}
MainService.java 代码
package com.oracle.db23c.springboot3withjpa.service;
import com.oracle.db23c.springboot3withjpa.entity.NewVideo;
import com.oracle.db23c.springboot3withjpa.vo.UniversalSearch;
import com.oracle.db23c.springboot3withjpa.entity.VideoEntity;
import com.oracle.db23c.springboot3withjpa.repository.VideoRepository;
import com.oracle.db23c.springboot3withjpa.vo.VideoSearch;
import jakarta.annotation.PostConstruct;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.Collections;
import java.util.List;
@Service
public class MainService {
private final VideoRepository repository;
public MainService(VideoRepository repository) {
this.repository = repository;
}
public List<VideoEntity> getVideos() {
return repository.findAll();
}
public VideoEntity create(NewVideo newVideo) {
return repository.saveAndFlush(new VideoEntity(newVideo.name(), newVideo.description()));
}
public List<VideoEntity> search(VideoSearch videoSearch) {
if (StringUtils.hasText(videoSearch.name()) //
&& StringUtils.hasText(videoSearch.description())) {
return repository //
.findByNameContainsOrDescriptionContainsAllIgnoreCase( //
videoSearch.name(), videoSearch.description());
}
if (StringUtils.hasText(videoSearch.name())) {
return repository.findByNameContainsIgnoreCase(videoSearch.name());
}
if (StringUtils.hasText(videoSearch.description())) {
return repository.findByDescriptionContainsIgnoreCase(videoSearch.description());
}
// 最后,如果两个字段都为空(或 null),则只返回一个结果
return Collections.emptyList();
}
public List<VideoEntity> search(UniversalSearch search) {
VideoEntity probe = new VideoEntity();
if (StringUtils.hasText(search.value())) {
probe.setName(search.value());
probe.setDescription(search.value());
}
Example<VideoEntity> example = Example.of(probe, //
ExampleMatcher.matchingAny() //
.withIgnoreCase() //
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING));
return repository.findAll(example);
}
@PostConstruct
void initDatabase() {
repository.save(new VideoEntity("Need HELP with your SPRING BOOT 3 App?",
"SPRING BOOT 3 will only speed things up and make it super SIMPLE to serve templates and raw data."));
repository.save(new VideoEntity("Don't do THIS to your own CODE!",
"As a pro developer, never ever EVER do this to your code. Because you'll ultimately be doing it to YOURSELF!"));
repository.save(new VideoEntity("SECRETS to fix BROKEN CODE!",
"Discover ways to not only debug your code, but to regain your confidence and get back in the game as a software developer."));
}
}
WebController.java 代码
package com.oracle.db23c.springboot3withjpa.controller;
import com.oracle.db23c.springboot3withjpa.entity.NewVideo;
import com.oracle.db23c.springboot3withjpa.vo.UniversalSearch;
import com.oracle.db23c.springboot3withjpa.entity.VideoEntity;
import com.oracle.db23c.springboot3withjpa.service.MainService;
import com.oracle.db23c.springboot3withjpa.vo.VideoSearch;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
@Controller
public class WebController {
private final MainService mainService;
public WebController(MainService mainService) {
this.mainService = mainService;
}
@GetMapping("/")
public String index(Model model) {
model.addAttribute("videos", mainService.getVideos());
return "index";
}
@PostMapping("/new-video")
public String newVideo(@ModelAttribute NewVideo newVideo) {
mainService.create(newVideo);
return "redirect:/";
}
// @ModelAttribute注解是Spring MVC对传入表单进行反序列化的信号。
// @Model参数是一种发送信息进行呈现的机制。
@PostMapping("/multi-field-search")
public String multiFieldSearch( //
@ModelAttribute VideoSearch search, //
Model model) {
List<VideoEntity> searchResults = //
mainService.search(search);
model.addAttribute("videos", searchResults);
return "index";
}
@PostMapping("/universal-search")
public String universalSearch(@ModelAttribute UniversalSearch search, Model model) {
List<VideoEntity> searchResults = mainService.search(search);
model.addAttribute("videos", searchResults);
return "index";
}
}
Spring Boot启动类
package com.oracle.db23c.springboot3withjpa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBoot3WithJpaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBoot3WithJpaApplication.class, args);
}
}
构建native image可执行文件
./mvnw -Pnative clean native:compile
运行native image可执行文件
./target/springboot3withjpa
构建native image的容器镜像
./mvnw -Pnative spring-boot:build-image
运行native image的容器镜像
docker run --rm -p 8080:8080 springboot3withjpa:latest
访问http://localhost:8080/
完结!