Skip to content

Commit 09e1f3a

Browse files
authored
Add files via upload
1 parent 6d5be52 commit 09e1f3a

10 files changed

+307
-0
lines changed

pom.xml

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>org.springframework.boot</groupId>
7+
<artifactId>spring-boot-starter-parent</artifactId>
8+
<version>3.4.0</version>
9+
<relativePath/> <!-- lookup parent from repository -->
10+
</parent>
11+
<groupId>com.javatechie</groupId>
12+
<artifactId>locking-demo</artifactId>
13+
<version>0.0.1-SNAPSHOT</version>
14+
<name>locking-demo</name>
15+
<description>Demo project for Spring Boot</description>
16+
17+
<properties>
18+
<java.version>21</java.version>
19+
</properties>
20+
<dependencies>
21+
<dependency>
22+
<groupId>org.springframework.boot</groupId>
23+
<artifactId>spring-boot-starter-data-jpa</artifactId>
24+
</dependency>
25+
<dependency>
26+
<groupId>org.springdoc</groupId>
27+
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
28+
<version>2.0.4</version>
29+
</dependency>
30+
<dependency>
31+
<groupId>org.springframework.boot</groupId>
32+
<artifactId>spring-boot-starter-web</artifactId>
33+
</dependency>
34+
35+
<dependency>
36+
<groupId>com.mysql</groupId>
37+
<artifactId>mysql-connector-j</artifactId>
38+
<scope>runtime</scope>
39+
</dependency>
40+
<dependency>
41+
<groupId>org.projectlombok</groupId>
42+
<artifactId>lombok</artifactId>
43+
<optional>true</optional>
44+
</dependency>
45+
<dependency>
46+
<groupId>org.springframework.boot</groupId>
47+
<artifactId>spring-boot-starter-test</artifactId>
48+
<scope>test</scope>
49+
</dependency>
50+
</dependencies>
51+
52+
<build>
53+
<plugins>
54+
<plugin>
55+
<groupId>org.springframework.boot</groupId>
56+
<artifactId>spring-boot-maven-plugin</artifactId>
57+
<configuration>
58+
<excludes>
59+
<exclude>
60+
<groupId>org.projectlombok</groupId>
61+
<artifactId>lombok</artifactId>
62+
</exclude>
63+
</excludes>
64+
</configuration>
65+
</plugin>
66+
</plugins>
67+
</build>
68+
69+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.javatechie;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class LockingDemoApplication {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(LockingDemoApplication.class, args);
11+
}
12+
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.javatechie.controller;
2+
3+
import com.javatechie.service.OptimisticSeatBookingTestService;
4+
import com.javatechie.service.PessimisticSeatBookingTestService;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.web.bind.annotation.*;
7+
8+
@RestController
9+
@RequestMapping("/booking")
10+
public class BookingTestController {
11+
12+
@Autowired
13+
private OptimisticSeatBookingTestService optimisticSeatBookingTestService;
14+
@Autowired
15+
private PessimisticSeatBookingTestService pessimisticSeatBookingTestService;
16+
17+
18+
@GetMapping("/optimistic/{seatId}")
19+
public String testOptimistic(@PathVariable Long seatId) throws InterruptedException {
20+
optimisticSeatBookingTestService.testOptimisticLocking(seatId);
21+
return "Optimistic locking test started! Check logs for results.";
22+
}
23+
24+
@GetMapping("/pessimistic/{seatId}")
25+
public String testPessimistic(@PathVariable Long seatId) throws InterruptedException {
26+
pessimisticSeatBookingTestService.testPessimisticLocking(seatId);
27+
return "Pessimistic locking test started! Check logs for results.";
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.javatechie.entity;
2+
3+
import jakarta.persistence.*;
4+
import lombok.Data;
5+
6+
@Entity
7+
@Data
8+
public class Seat {
9+
10+
@Id
11+
@GeneratedValue(strategy = GenerationType.IDENTITY)
12+
private Long id;
13+
14+
private String movieName;
15+
16+
private boolean booked;
17+
18+
@Version
19+
private int version;
20+
21+
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.javatechie.repository;
2+
3+
import com.javatechie.entity.Seat;
4+
import jakarta.persistence.LockModeType;
5+
import org.springframework.data.jpa.repository.JpaRepository;
6+
import org.springframework.data.jpa.repository.Lock;
7+
import org.springframework.data.jpa.repository.Query;
8+
9+
public interface SeatRepository extends JpaRepository<Seat, Long> {
10+
11+
@Lock(LockModeType.PESSIMISTIC_WRITE)
12+
@Query("SELECT s FROM Seat s WHERE s.id= :seatId")
13+
Seat findByIdAndLock(Long seatId);
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.javatechie.service;
2+
3+
import com.javatechie.entity.Seat;
4+
import com.javatechie.repository.SeatRepository;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.stereotype.Service;
7+
import org.springframework.transaction.annotation.Transactional;
8+
9+
@Service
10+
public class MovieTicketBookingService {
11+
12+
@Autowired
13+
private SeatRepository seatRepository;
14+
15+
16+
@Transactional
17+
public Seat bookSeat(Long seatId) {
18+
//fetch the existing seat by id
19+
Seat seat = seatRepository.findById(seatId)
20+
.orElseThrow(() -> new RuntimeException("Seat not found with id " + seatId));
21+
22+
System.out.println(Thread.currentThread().getName() + " fetched seat with version " + seat.getVersion());
23+
24+
if (seat.isBooked()) {
25+
throw new RuntimeException("Seat already booked !");
26+
}
27+
//booking seat
28+
seat.setBooked(true);
29+
//version check will occurs here
30+
return seatRepository.save(seat);
31+
}
32+
33+
@Transactional
34+
public void bookSeatWithPessimistic(Long seatId) {
35+
36+
System.out.println(Thread.currentThread().getName() + " is attempting to fetch the seat");
37+
38+
//fetch the seat with Pessimistic lock
39+
Seat seat = seatRepository.findByIdAndLock(seatId);
40+
41+
System.out.println(Thread.currentThread().getName() + " acquired the lock for seat id " + seatId);
42+
43+
if (seat.isBooked()) {
44+
System.out.println(Thread.currentThread().getName() + " failed Seat Id " + seatId + " is already booked ");
45+
throw new RuntimeException("Seat already booked !");
46+
}
47+
//booking seat
48+
System.out.println(Thread.currentThread().getName() + " booking the seat " + seatId);
49+
50+
seat.setBooked(true);
51+
//version check will occurs here
52+
seatRepository.save(seat);
53+
System.out.println(Thread.currentThread().getName() + " successfully book the seat with ID " + seatId);
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.javatechie.service;
2+
3+
import com.javatechie.entity.Seat;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.stereotype.Service;
6+
7+
@Service
8+
public class OptimisticSeatBookingTestService {
9+
10+
@Autowired
11+
private MovieTicketBookingService movieTicketBookingService;
12+
13+
14+
public void testOptimisticLocking(Long seatId) throws InterruptedException {
15+
// 2 thread
16+
17+
Thread th1 = new Thread(() -> {
18+
try {
19+
System.out.println(Thread.currentThread().getName() + " is attempting to book the seat");
20+
Seat seat = movieTicketBookingService.bookSeat(seatId);
21+
System.out.println(Thread.currentThread().getName() + " successfully booked the seat with version " + seat.getVersion());
22+
} catch (Exception ex) {
23+
System.out.println(Thread.currentThread().getName() + " failed : " + ex.getMessage());
24+
}
25+
});
26+
27+
Thread th2 = new Thread(() -> {
28+
try {
29+
System.out.println(Thread.currentThread().getName() + " is attempting to book the seat");
30+
Seat seat = movieTicketBookingService.bookSeat(seatId);
31+
System.out.println(Thread.currentThread().getName() + " successfully booked the seat with version " + seat.getVersion());
32+
} catch (Exception ex) {
33+
System.out.println(Thread.currentThread().getName() + " failed : " + ex.getMessage());
34+
}
35+
});
36+
37+
th1.start();
38+
th2.start();
39+
th1.join();
40+
th2.join();
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.javatechie.service;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.stereotype.Service;
5+
6+
@Service
7+
public class PessimisticSeatBookingTestService {
8+
9+
@Autowired
10+
private MovieTicketBookingService movieTicketBookingService;
11+
12+
public void testPessimisticLocking(Long seatId) throws InterruptedException {
13+
Thread thread1 = new Thread(() -> {
14+
try {
15+
movieTicketBookingService.bookSeatWithPessimistic(seatId);
16+
} catch (RuntimeException e) {
17+
System.out.println(Thread.currentThread().getName() + " failed: " + e.getMessage());
18+
}
19+
});
20+
21+
Thread thread2 = new Thread(() -> {
22+
try {
23+
movieTicketBookingService.bookSeatWithPessimistic(seatId);
24+
} catch (RuntimeException e) {
25+
System.out.println(Thread.currentThread().getName() + " failed: " + e.getMessage());
26+
}
27+
});
28+
29+
thread1.start();
30+
thread2.start();
31+
32+
thread1.join();
33+
thread2.join();
34+
}
35+
}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
spring.application.name=locking-demo
2+
3+
server.port=9191
4+
5+
#DataBase PROPERTIES
6+
7+
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
8+
spring.datasource.url = jdbc:mysql://localhost:3306/javatechie
9+
spring.datasource.username = root
10+
spring.datasource.password = Password
11+
#spring.jpa.show-sql = true
12+
spring.jpa.hibernate.ddl-auto = update
13+
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect
14+
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
15+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.javatechie;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.springframework.boot.test.context.SpringBootTest;
5+
6+
@SpringBootTest
7+
class LockingDemoApplicationTests {
8+
9+
@Test
10+
void contextLoads() {
11+
}
12+
13+
}

0 commit comments

Comments
 (0)