根据你提到的要求,以下是使用 Spring Boot 和 Spring Data JPA 来实现乐观锁的项目的完整结构。项目的目录结构如下:
1. 项目结构
my-spring-boot-jpa-optimistic-locking
│
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── optimisticlocking
│ │ │ ├── controller
│ │ │ │ └── ProductController.java
│ │ │ ├── entity
│ │ │ │ └── Product.java
│ │ │ ├── repository
│ │ │ │ └── ProductRepository.java
│ │ │ ├── service
│ │ │ │ └── ProductService.java
│ │ │ ├── MySpringBootJpaOptimisticLockingApplication.java
│ │ │ └── OptimisticLockTestRunner.java
│ │ └── resources
│ │ ├── application.properties
│ │ └── schema.sql
│ └── test
│ └── java
│ └── com
│ └── example
│ └── optimisticlocking
│ └── ProductServiceTest.java
├── pom.xml
└── README.md
2. 目录详细说明
- src/main/java/com/example/optimisticlocking/
MySpringBootJpaOptimisticLockingApplication.java
这是 Spring Boot 应用程序的入口,包含 @SpringBootApplication 注解。
文件路径: com/example/optimisticlocking/MySpringBootJpaOptimisticLockingApplication.java
package com.example.optimisticlocking;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MySpringBootJpaOptimisticLockingApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringBootJpaOptimisticLockingApplication.class, args);
}
}
controller/ProductController.java
用于处理产品相关的 REST API 请求。
文件路径: com/example/optimisticlocking/controller/ProductController.java
package com.example.optimisticlocking.controller;
import com.example.optimisticlocking.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductService productService;
@PutMapping("/{id}/update-price")
public String updateProductPrice(@PathVariable Long id, @RequestParam double priceIncrement) {
try {
productService.updateProductPrice(id, priceIncrement);
return "Product price updated successfully!";
} catch (Exception e) {
return "Failed to update product price due to optimistic locking.";
}
}
}
entity/Product.java
定义了 Product 实体类,包含乐观锁的 @Version 字段。
文件路径: com/example/optimisticlocking/entity/Product.java
package com.example.optimisticlocking.entity;
import javax.persistence.*;
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
@Version
private Integer version;
// Getters and setters...
}
repository/ProductRepository.java
继承 JpaRepository,用于数据访问操作。
文件路径: com/example/optimisticlocking/repository/ProductRepository.java
package com.example.optimisticlocking.repository;
import com.example.optimisticlocking.entity.Product;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ProductRepository extends JpaRepository<Product, Long> {
// 可以自定义查询方法
}
service/ProductService.java
包含产品的增删改查逻辑,特别是更新产品价格时的乐观锁控制。
文件路径: com/example/optimisticlocking/service/ProductService.java
package com.example.optimisticlocking.service;
import com.example.optimisticlocking.entity.Product;
import com.example.optimisticlocking.repository.ProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Transactional
public void updateProductPrice(Long productId, double priceIncrement) {
Product product = productRepository.findById(productId).orElseThrow(() -> new RuntimeException("Product not found"));
product.setPrice(product.getPrice() + priceIncrement);
productRepository.save(product); // 使用@Version字段进行乐观锁控制
}
}
OptimisticLockTestRunner.java
用于模拟并发更新产品价格,测试乐观锁的效果。
文件路径: com/example/optimisticlocking/OptimisticLockTestRunner.java
package com.example.optimisticlocking;
import com.example.optimisticlocking.entity.Product;
import com.example.optimisticlocking.repository.ProductRepository;
import com.example.optimisticlocking.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class OptimisticLockTestRunner implements CommandLineRunner {
@Autowired
private ProductService productService;
@Autowired
private ProductRepository productRepository;
@Override
public void run(String... args) throws Exception {
// 创建产品
Product product = new Product();
product.setName("Test Product");
product.setPrice(100.00);
productRepository.save(product);
// 模拟并发更新
new Thread(() -> productService.updateProductPrice(product.getId(), 10.00)).start();
new Thread(() -> productService.updateProductPrice(product.getId(), 20.00)).start();
}
}
src/main/resources/
application.properties
Spring Boot 应用程序的配置文件。
文件路径: src/main/resources/application.properties
# 数据库配置 (MySQL 示例)
spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name
spring.datasource.username=your_username
spring.datasource.password=your_password
# Hibernate 配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
schema.sql(可选)
如果需要手动定义表结构,可以使用此文件。
文件路径: src/main/resources/schema.sql
CREATE TABLE products (
id BIGINT NOT NULL AUTO_INCREMENT,
name VARCHAR(255),
price DOUBLE,
version INT,
PRIMARY KEY (id)
);
src/test/java/com/example/optimisticlocking/ProductServiceTest.java
用于测试产品的增删改查操作,特别是乐观锁的行为。
文件路径: src/test/java/com/example/optimisticlocking/ProductServiceTest.java
package com.example.optimisticlocking;
import com.example.optimisticlocking.entity.Product;
import com.example.optimisticlocking.repository.ProductRepository;
import com.example.optimisticlocking.service.ProductService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import static org.junit.jupiter.api.Assertions.assertThrows;
@SpringBootTest
public class ProductServiceTest {
@Autowired
private ProductService productService;
@Autowired
private ProductRepository productRepository;
@Test
@Transactional
public void testOptimisticLocking() {
// 创建一个产品
Product product = new Product();
product.setName("Test Product");
product.setPrice(100.00);
productRepository.save(product);
// 模拟第一个事务
Product product1 = productRepository.findById(product.getId()).get();
product1.setPrice(product1.getPrice() + 10);
// 模拟第二个事务
Product product2 = productRepository.findById(product.getId()).get();
product2.setPrice(product2.getPrice() + 20);
productRepository.save(product2); // 第二个事务保存成功
// 尝试保存第一个事务,应该抛出乐观锁异常
assertThrows(javax.persistence.OptimisticLockException.class, () -> {
productRepository.save(product1);
});
}
}
根目录 pom.xml
用于管理项目的依赖,Spring Boot,JPA,MySQL 和 JUnit。
文件路径: pom.xml
xml
<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.example</groupId>
<artifactId>my-spring-boot-jpa-optimistic-locking</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>My Spring Boot JPA Optimistic Locking Example</name>
<description>Spring Boot and JPA example with optimistic locking</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version> <!-- 请根据最新版本调整 -->
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>17</java.version> <!-- 请确保 Java 版本与本地环境兼容 -->
<spring-cloud.version>2022.0.3</spring-cloud.version> <!-- Spring Cloud 版本(可选) -->
</properties>
<dependencies>
<!-- Spring Boot Starter for Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Starter for JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL JDBC Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Spring Boot Starter for Test (JUnit, AssertJ, Mockito) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Hibernate Validator (Optional if using Bean Validation) -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<!-- Optional: H2 Database for Testing (if needed) -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<!-- Optional: Spring Boot DevTools (for development, auto-restart, etc.) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!-- Spring Boot Maven Plugin -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- Maven Compiler Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

1万+

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



