Spring Boot REST API for managing hotels, rooms, bookings, and cached room availability with MySQL persistence, Redis caching, JWT authentication, OAuth2 login support via Keycloak and Google, Swagger/OpenAPI 3 docs, and GitHub Actions CI.
This release keeps the hotel-management workflow intact while refreshing the security layer, extending the domain model, and adding a persistent audit trail. The application supports Keycloak-backed JWT/resource-server access, Google OAuth2 login, Redis-backed room availability, and role-based endpoint protection through Spring Security annotations.
| Layer | Responsibility |
|---|---|
| API layer | Exposes hotel, room, user, and booking endpoints |
| Domain layer | Models hotels, rooms, users, and booking lifecycle states |
| Security layer | Uses JWT, OAuth2 login, and method-level authorization |
| Cache layer | Caches room-availability lookups in Redis and invalidates them on booking changes |
| Audit layer | Persists booking and security-sensitive actions in an audit log |
| Persistence layer | Stores hotel, room, user, and booking data in MySQL |
| View layer | Provides a Thymeleaf login page for browser sign-in |
- Spring Boot REST API setup
- Spring Data JPA repository pattern
- MySQL-backed persistence
- Spring Security with JWT resource-server support
- OAuth2 login with Keycloak
- OAuth2 login support for Google
- Method-level authorization with
@PreAuthorize - Public user registration and user listing
- Hotel creation, retrieval, listing, and deletion endpoints
- Room management for hotel inventory
- Booking creation with request/confirm/reject statuses
- Date-range validation for room availability
- Concurrency-safe booking writes using a locked room lookup
- Redis-backed caching for room availability searches
- Cache invalidation when rooms or bookings change
- Persistent audit logging for login, profile access, CRUD, and booking actions
/logincustom page backed by Thymeleaf- Google user authority mapping from the local user table
- Swagger/OpenAPI 3 integration for interactive API documentation at
/swagger-ui.html - Global exception handling via
@RestControllerAdvicefor structured JSON error responses - Spring Security integration tests with
MockMvcand@WithMockUser - GitHub Actions CI pipeline — automated
mvn teston every push and pull request
- Java 17
- Spring Boot 3.3
- Spring Web
- Spring Data JPA
- Spring Security
- Spring Validation
- Spring OAuth2 Client
- Spring OAuth2 Resource Server
- Spring Cache
- Thymeleaf
- Redis
- MySQL
- Hibernate/JPA auditing patterns
- Maven
- Lombok
- JJWT
- Springdoc OpenAPI (Swagger UI)
- H2 (test isolation)
- GitHub Actions (CI)
hotel/
├── .github/
│ └── workflows/
│ └── ci.yml ← GitHub Actions CI
├── CHANGELOG.md
├── README.md
├── pom.xml
├── mvnw / mvnw.cmd
└── src/
├── main/
│ ├── java/com/cn/hotelDemo/
│ │ ├── config/ (HotelSecurityConfig, OpenApiConfig)
│ │ ├── controller/ (Hotel, Room, Booking, User, Audit, Login, GlobalExceptionHandler)
│ │ ├── dto/
│ │ ├── model/
│ │ ├── repository/
│ │ ├── service/
│ │ └── HotelDemoApplication.java
│ └── resources/
│ ├── application.yml
│ └── templates/
│ └── login.html
└── test/
├── java/com/cn/hotelDemo/
│ ├── controller/ (SecurityIntegrationTest)
│ └── service/ (AuditServiceTest, BookingServiceTest, RoomServiceTest)
└── resources/
└── application.yml (H2 in-memory test config)
The application uses environment variables for sensitive or environment-specific settings. Create a .env file in the project root or export these variables before running the application:
DATASOURCE_URL: The JDBC connection URL for MySQL (default: Azure MySQL database URI)DATASOURCE_USERNAME: The MySQL username (default:demouser)DATASOURCE_PASSWORD: The MySQL password (e.g.Password~1234)KEYCLOAK_CLIENT_SECRET: Keycloak client secret for OIDC registrationKEYCLOAK_ISSUER_URI: Keycloak token issuer realm URIKEYCLOAK_AUTH_SERVER_URL: Keycloak authentication server endpoint URLGOOGLE_CLIENT_ID: Google OAuth client ID (optional)GOOGLE_CLIENT_SECRET: Google OAuth client secret (optional)REDIS_HOST: Redis server hostname (default:localhost)REDIS_PORT: Redis server port (default:6379)
See .env.example for a complete template.
- Open a terminal in the project root.
- Copy
.env.exampleto.envand configure your credentials and connection strings. - Run
mvn test(executes the test suite against an isolated in-memory H2 database, requiring no active MySQL or Redis instances). - Run
mvn spring-boot:run. - Open
http://localhost:8082/loginfor the custom login page. - Use the API under
http://localhost:8082.
Available endpoints:
GET /loginGET /hotel/userDetailPOST /hotel/createGET /hotel/id/{id}GET /hotel/getAllDELETE /hotel/remove/id/{id}POST /hotel/rooms/createGET /hotel/rooms/id/{id}GET /hotel/rooms/hotel/{hotelId}GET /hotel/rooms/hotel/{hotelId}/available?checkInDate=YYYY-MM-DD&checkOutDate=YYYY-MM-DDGET /hotel/rooms/getAllPOST /hotel/bookings/createPOST /hotel/bookings/cancel/{id}GET /hotel/bookings/id/{id}GET /hotel/bookings/getAllGET /hotel/bookings/user/{userId}GET /hotel/bookings/hotel/{hotelId}GET /audit/getAllGET /user/getUsersGET /user/getUsers/{id}POST /user/createUserDELETE /user/remove/id/{id}
Access notes:
/loginis public.GET /hotel/id/{id}is forNORMALusers.POST /hotel/create,GET /hotel/getAll, andDELETE /hotel/remove/id/{id}are restricted to admin-style access.POST /hotel/rooms/createandGET /hotel/rooms/getAllare admin-only operations.GET /audit/getAllis admin-only.GET /hotel/bookings/getAllandGET /hotel/bookings/hotel/{hotelId}are admin-only operations.POST /hotel/bookings/createis available to authenticated hotel users.GET /hotel/userDetailuses the authenticated OIDC principal.- Google login uses local user-role mapping from the MySQL user table.
Example user registration body:
{
"username": "john",
"password": "john123",
"email": "john@example.com",
"role": "NORMAL"
}Example hotel creation body:
{
"name": "Sea View Inn",
"rating": 8,
"city": "Goa"
}Example room creation body:
{
"hotelId": 1,
"roomNumber": "101",
"roomType": "Deluxe",
"capacity": 2,
"nightlyRate": 4500.00,
"status": "AVAILABLE"
}Example room availability check:
GET /hotel/rooms/hotel/1/available?checkInDate=2026-06-05&checkOutDate=2026-06-08
Example booking creation body:
{
"hotelId": 1,
"roomId": 1,
"userId": 2,
"checkInDate": "2026-06-05",
"checkOutDate": "2026-06-08",
"guestCount": 2,
"specialRequests": "Late check-in"
}Example booking response:
{
"bookingReference": "BOOK-1A2B3C4D",
"status": "CONFIRMED",
"message": "Booking confirmed successfully",
"bookingId": 10
}flowchart LR
Browser["🌐 Browser"] --> Login["GET /login"]
Login --> Keycloak["Keycloak OIDC"]
Login --> Google["Google OIDC"]
Keycloak --> Token["JWT / OIDC claims"]
Google --> Token
Token --> Security["HotelSecurityConfig"]
Security --> RoleMap["Role mapping from token + local user table"]
RoleMap --> PreAuth["@PreAuthorize"]
PreAuth --> Endpoint["Controller endpoint"]
Endpoint --> Audit["Audit log entry"]
PreAuth -->|denied| ExHandler["GlobalExceptionHandler → 403 JSON"]
flowchart LR
Guest["Authenticated Guest"] --> Create["POST /bookings/create"]
Guest --> Cancel["POST /bookings/cancel/{id}"]
Create --> RoomLock["Pessimistic room lock"]
RoomLock --> Overlap["Date-range overlap check"]
Overlap -->|available| Confirmed["CONFIRMED"]
Overlap -->|conflict| Rejected["REJECTED"]
Cancel --> StateCheck["Status transition check"]
StateCheck -->|valid| Cancelled["CANCELLED"]
StateCheck -->|invalid| Error["400 Bad Request JSON"]
Confirmed --> CacheEvict["Evict room-availability cache"]
Rejected --> CacheEvict
Cancelled --> CacheEvict
Confirmed --> Audit["Audit log"]
Rejected --> Audit
Cancelled --> Audit
flowchart LR
Client["Client / Swagger UI"] --> Security["Spring Security"]
Security --> UserAPI["/user/*"]
Security --> HotelAPI["/hotel/*"]
Security --> AuditAPI["/audit/*"]
Security --> SwaggerUI["/swagger-ui.html"]
HotelAPI --> Hotels["Hotel CRUD"]
HotelAPI --> Rooms["Room inventory"]
HotelAPI --> Bookings["Booking lifecycle"]
Rooms --> Cache["Redis cache"]
Rooms --> LockedRoom["Pessimistic lock"]
LockedRoom --> Availability["Date-range check"]
Availability --> Cache
Bookings --> Audit["Audit trail"]
Hotels --> Audit
Security -->|error| ExHandler["GlobalExceptionHandler"]
ExHandler --> ErrorJSON["Structured JSON errors"]
CI["GitHub Actions CI"] --> Tests["mvn test on H2"]
- Suggested repository description:
Spring Boot REST API for hotel, room, booking, and audit-log management with MySQL, Redis caching, JWT/OAuth2 auth, Swagger/OpenAPI 3 docs, Spring Security tests, and GitHub Actions CI. - Suggested topics:
java,java-17,spring-boot,spring-boot-3,spring-security,spring-security-test,spring-data-jpa,spring-validation,spring-cache,redis,mysql,h2-database,rest-api,hotel-management,room-booking,room-availability,cache-invalidation,audit-log,observability,concurrency,pessimistic-locking,jwt,oauth2,keycloak,google-login,thymeleaf,swagger,openapi,springdoc,github-actions,ci-cd,maven,learning-project,portfolio-project