diff --git a/README.md b/README.md index b1cccfd..690b418 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,309 @@ -## Submission Instructions - -To submit your Oracle JAVA Spring Boot Maven project as a solution, please follow these steps: - -### Step 1: Install git on your PC -- Install "git" as shown in this tutorial: [How to install git](https://youtu.be/iYkLrXobBbA?si=_l0haibv_X9NpIjJ) -- Open command prompt and run - ```bash - git version - ``` -- If you see the version, then git is successfully installed. - -### Step 2: Fork the Repository -- Navigate to [this repository](https://github.com/CodelineAtyab/oraclequantapi) provided by Codeline. -- Click on the "Fork" button at the top-right corner of the page to create a copy of the repository under your own GitHub account. - -### Step 3: Clone the Forked Repository -- Open your terminal or command prompt. -- Clone the forked repository to your local machine using the following command: - ```bash - git clone https://github.com/your-username/repo-name.git - ``` - -### Step 4: Create a new branch -- Navigate to the cloned repository directory - ```bash - cd repo-name - ``` -- Create a new branch for your code submissions (Replace your-name with your name in your-name-submission-branch): - ```bash - git checkout -b your-name-submission-branch - ``` - - -### Step 5: Add Your Code -- Implement the API - -### Step 6: Commit your changes -- Run the following commands in order to commit your changes: - ```bash - git add * - git commit -m "Meaningful commit message here" - ``` - -### Step 7: Push Your Branch to GitHub -- Run the following commands to upload the changes to the forked github repository (Replace your-name with your name in your-name-submission-branch): - ```bash - git push origin your-name-submission-branch - ``` - -### Step 8: Create a Pull Request -- Go to your forked repository on GitHub. -- You should see a prompt to create a pull request. Click on "Compare & pull request". -- Provide a title and description for your pull request, then click "Create pull request". - -### Step 9: Notify Codeline -- Notify on slack that you have created a PR for your solution. - -## Note: If you face any issues in the process above, Please do the following: -- Watch [this youtube tutorial](https://www.youtube.com/watch?v=a_FLqX3vGR4) -- Contact Ikhlas or Atyab. +## Project Overview + +Oracle Quant API is a Spring Boot 3.5.14 / Java 17 REST API that decodes submitted sequence strings using a self-delimiting length-encoded parser and persists the results in an Oracle database. + +**Tech Stack** +- Java 17 +- Spring Boot 3.5.14 +- Spring Web (embedded Tomcat) +- Spring Data JPA / Hibernate ORM (OracleDialect) +- Oracle JDBC (ojdbc11) +- Maven + +--- + +## How to Run + +**Prerequisites:** Java 17 JDK and an Oracle database configured in `application.yaml` (see Database Setup below). + +### Run from source + +```bash +# Windows +mvnw.cmd spring-boot:run + +# macOS / Linux +./mvnw spring-boot:run +``` + +### Run the JAR (v1.0) + +A pre-built executable JAR is available. + +```bash +# Windows +java -jar oraclequantapi-1.0.jar + +# macOS / Linux +java -jar oraclequantapi-1.0.jar +``` + +To override the datasource without modifying the bundled config, place an `application.yaml` in the same directory as the JAR — Spring Boot will pick it up automatically: + +```yaml +spring: + datasource: + url: jdbc:oracle:thin:@//your-host:1521/your-service + username: YOUR_USERNAME + password: YOUR_PASSWORD +``` + +The server starts on `http://localhost:8080`. + +--- + +## Architecture + +Clean 3-layer architecture with strict Single Responsibility Principle (SRP): + +``` +HTTP Request + | + v +[ Controller ] -- HTTP mappings only, zero business logic + | + v +[ Service ] -- Input validation and decoder algorithm + | + v +[Sequence_DATABASE] -- Exception-safe Oracle persistence wrapper + | + v +[ Repository ] -- Spring Data JPA interface + | + v +[ Oracle DB ] -- SEQUENCE_ENQUIRIES table +``` + +**Package structure:** +``` +com.oraclequantapi.oraclequantapi ++-- OraclequantapiApplication.java (entry point) ++-- controller/ +| +-- Controller.java (REST layer - @RestController) ++-- services/ +| +-- Service.java (business logic - @Service) ++-- module/ +| +-- Sequence.java (JPA entity mapped to SEQUENCE_ENQUIRIES) ++-- repository/ + +-- Repository.java (Spring Data JPA interface) + +-- Sequence_DATABASE.java (exception-safe Oracle persistence wrapper) +``` + +- The Controller delegates all logic to the Service via `@Autowired` injection. +- The Service validates input, runs the length-encoded parser, and delegates all persistence to `Sequence_DATABASE`. +- `Sequence.java` is the JPA `@Entity` mapped to the `SEQUENCE_ENQUIRIES` Oracle table. It owns all column mappings and JSON serialization rules. +- `id` and `currentTime` are always server-generated — never client-supplied. +- `input` is write-only — accepted in the request body but never returned in any response. +- Data persists across restarts via Oracle; the table is auto-created on first startup (`ddl-auto: update`). + +--- + +## Database Diagram + +``` ++-----------------------------+ +| SEQUENCE_ENQUIRIES | Oracle Table ++-----------------------------+ +| PK ID VARCHAR2 | UUID, server-generated +| INPUT VARCHAR2 | Raw input (write-only in API) +| CURRENT_TIME VARCHAR2 | Timestamp of save or last update ++-----------------------------+ + | + | managed by + v ++-----------------------------+ +| Repository.java | Spring Data JPA interface +| JpaRepository< | Auto-provides: save, findAll, +| Sequence, String> | existsById, deleteById ++-----------------------------+ + | + | wrapped by + v ++-----------------------------+ +| Sequence_DATABASE.java | Exception-safe persistence layer +| persist() | -> repository.save() +| retrieveAll() | -> repository.findAll() +| update() | -> existsById() + save() +| remove() | -> existsById() + deleteById() ++-----------------------------+ + | + | called by + v ++-----------------------------+ +| Service.java | Business logic layer +| addSequence() | -> persist() +| getAllSequences() | -> retrieveAll() +| updateSequence() | -> update() +| deleteSequence() | -> remove() ++-----------------------------+ +``` + +--- + +## Database Setup + +1. **Configure credentials** in `src/main/resources/application.yaml`: + ```yaml + spring: + datasource: + url: jdbc:oracle:thin:@//your-host:1521/your-service + username: YOUR_USERNAME + password: YOUR_PASSWORD + ``` + +2. **Start the application** — `ddl-auto: update` will auto-create the `SEQUENCE_ENQUIRIES` table on first startup. No manual SQL required. + +--- + +## Decoder Algorithm + +The `sequenceLogicAlgorithm` method processes the input string using a self-delimiting length-encoded parser: + +- Character values: `a=1, b=2, ... z=26, _=0` +- **Header phase:** consecutive `z` characters each add 26 to the block length; the first non-`z` character adds its own value. This determines how many characters to consume next. +- **Data phase:** consume exactly that many characters and sum their values; the sum is appended to the output array. +- Parsing repeats left-to-right until the full string is consumed. + +**Examples:** + +| Input | Decode steps | Output | +|---|---|---| +| `abbcc` | `a`=len 1 → `b`=2; `b`=len 2 → `c`+`c`=6 | `[2, 6]` | +| `cdaaabaa` | `c`=len 3 → `d`+`a`+`a`=6; `a`=len 1 → `b`=2; `a`=len 1 → `a`=1 | `[6, 2, 1]` | +| `zabc...` | `z`+`a`=len 27 → consume 27 chars | `[sum]` | + +--- + +## API Reference + +### POST `/sequenceDecoder` + +Submit a sequence string for decoding. The `input` field must contain **only lowercase letters a-z and underscores** and **must not start with `_`** — any violation returns 400. The server decodes the input, auto-generates `id` and `currentTime`, and returns the result. + +**Request:** +```http +POST http://localhost:8080/sequenceDecoder +Content-Type: application/json + +{ + "input": "abbcc" +} +``` + +**Response — 201 Created:** +```json +{ + "id": "a3f9c1d2-84ab-4e11-b3c7-2f4400000001", + "currentTime": "2026-05-24 14:30:00", + "output": [2, 6] +} +``` + +**Another valid example:** +```http +{ "input": "cdaaabaa" } +``` +```json +{ + "id": "b7e2d3a1-91cd-4f22-c4d8-3g5500000002", + "currentTime": "2026-05-24 14:31:05", + "output": [6, 2, 1] +} +``` + +**Invalid input — Response 400 Bad Request:** +```json +{ "input": "Hello123!" } +``` +```json +{ "input": "_abc" } +``` +``` +Input must only contain a-z and underscore, and must not start with underscore +``` + +--- + +### PUT `/sequenceDecoder` + +Update an existing enquiry by `id`. Applies the same input validation rules as POST. Re-runs the decoder on the new input and refreshes `currentTime`. + +**Request:** +```http +PUT http://localhost:8080/sequenceDecoder +Content-Type: application/json + +{ + "id": "a3f9c1d2-84ab-4e11-b3c7-2f4400000001", + "input": "cdaaabaa" +} +``` + +**Response — 201:** +```json +{ + "id": "a3f9c1d2-84ab-4e11-b3c7-2f4400000001", + "currentTime": "2026-05-24 15:00:00", + "output": [6, 2, 1] +} +``` + +**Failure — 400 Bad Request:** +- `id` not found, or `input` fails validation: +``` +Enquiry not found or input invalid +``` + +--- + +### DELETE `/sequenceDecoder` + +Remove an existing enquiry by `id`. + +**Request:** +```http +DELETE http://localhost:8080/sequenceDecoder +Content-Type: application/json + +{ + "id": "a3f9c1d2-84ab-4e11-b3c7-2f4400000001" +} +``` + +**Response — 201:** +``` +Enquiry deleted successfully +``` + +**Failure — 400 Bad Request:** +``` +Enquiry not found or already deleted +``` + +--- + +### GET `/sequenceDecoder` + +Retrieve all stored sequence enquiries with their decoded outputs. + +**Request:** +```http +GET http://localhost:8080/sequenceDecoder +``` + +**Response — 200 OK:** +```json +[ + { + "id": "a3f9c1d2-84ab-4e11-b3c7-2f4400000001", + "currentTime": "2026-05-24 14:30:00", + "output": [2, 6] + }, + { + "id": "b7e2d3a1-91cd-4f22-c4d8-3g5500000002", + "currentTime": "2026-05-24 14:31:05", + "output": [6, 2, 1] + } +] +``` diff --git a/oraclequantapi-1.0.jar b/oraclequantapi-1.0.jar new file mode 100644 index 0000000..174e9a6 Binary files /dev/null and b/oraclequantapi-1.0.jar differ diff --git a/pom.xml b/pom.xml index 20909d2..891f4df 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ com.oraclequantapi oraclequantapi - 0.0.1-SNAPSHOT + 1.0 @@ -35,6 +35,17 @@ spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-data-jpa + + + + com.oracle.database.jdbc + ojdbc11 + runtime + + org.springframework.boot spring-boot-starter-test diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/controller/Controller.java b/src/main/java/com/oraclequantapi/oraclequantapi/controller/Controller.java new file mode 100644 index 0000000..53466b6 --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/controller/Controller.java @@ -0,0 +1,59 @@ +package com.oraclequantapi.oraclequantapi.controller; + +import com.oraclequantapi.oraclequantapi.module.Sequence; +import com.oraclequantapi.oraclequantapi.services.Service; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * REST layer — routes /sequenceDecoder requests to Service. + * Contains zero business logic; all rules live in Service. + */ +@RestController +@RequestMapping("/sequenceDecoder") +public class Controller { + + @Autowired + private Service service; + + // Submit a new sequence — validates input, decodes, and stores it + @PostMapping + public ResponseEntity postSequence(@RequestBody Sequence sequence) { + Sequence stored = service.addSequence(sequence); + if (stored == null) { + return ResponseEntity.badRequest().body("Input must only contain a-z and underscore, and must not start with underscore"); + } + return ResponseEntity.status(HttpStatus.CREATED).body(stored); + } + + // Update an existing enquiry by id — re-runs decoder on new input + @PutMapping + public ResponseEntity updateSequence(@RequestBody Sequence sequence) { + Sequence updated = service.updateSequence(sequence); + if (updated == null) { + return ResponseEntity.badRequest().body("Enquiry not found or input invalid"); + } + return ResponseEntity.status(HttpStatus.CREATED).body(updated); + } + + // Remove an enquiry by id + @DeleteMapping + public ResponseEntity deleteSequence(@RequestBody Sequence sequence) { + boolean deleted = service.deleteSequence(sequence.getId()); + if (!deleted) { + return ResponseEntity.badRequest().body("Enquiry not found or already deleted"); + } + return ResponseEntity.status(HttpStatus.CREATED).body("Enquiry deleted successfully"); + } + + // Retrieve all stored sequence enquiries + @GetMapping + public ResponseEntity> getAllSequences() { + return ResponseEntity.ok(service.getAllSequences()); + } + +} diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/module/Sequence.java b/src/main/java/com/oraclequantapi/oraclequantapi/module/Sequence.java new file mode 100644 index 0000000..1970fc7 --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/module/Sequence.java @@ -0,0 +1,77 @@ +package com.oraclequantapi.oraclequantapi.module; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.UUID; + +/** + * JPA entity and domain model for a sequence enquiry. + * Owns all persistence column mappings and JSON serialization rules. + */ +@Entity +@Table(name = "SEQUENCE_ENQUIRIES") +public class Sequence { + + @Id + @Column(name = "ID", nullable = false, unique = true) + private String id; + + //------[DB] Write-only in JSON — accepted on requests, excluded from all responses + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + @Column(name = "INPUT") + private String input; + + @Column(name = "CURRENT_TIME") + private String currentTime; + + //------[DB] Not persisted — recomputed from input on every retrieval + @Transient + private List output; + + //------[DB] No-arg constructor auto-generates id and timestamp for new records + public Sequence() { + this.id = UUID.randomUUID().toString(); + this.currentTime = LocalDateTime.now() + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + + public String getId() { + return id; + } + + public String getInput() { + return input; + } + + public String getCurrentTime() { + return currentTime; + } + + public List getOutput() { + return output; + } + + public void setId(String id) { + this.id = id; + } + + public void setInput(String input) { + this.input = input; + } + + public void setCurrentTime(String currentTime) { + this.currentTime = currentTime; + } + + public void setOutput(List output) { + this.output = output; + } +} diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/repository/Repository.java b/src/main/java/com/oraclequantapi/oraclequantapi/repository/Repository.java new file mode 100644 index 0000000..33ccd6f --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/repository/Repository.java @@ -0,0 +1,12 @@ +package com.oraclequantapi.oraclequantapi.repository; + +import com.oraclequantapi.oraclequantapi.module.Sequence; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * Spring Data JPA interface for SEQUENCE_ENQUIRIES. + * Auto-proxied at startup when JPA auto-config is active. + * Provides: save, findAll, findById, existsById, deleteById. + */ +public interface Repository extends JpaRepository { +} diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/repository/Sequence_DATABASE.java b/src/main/java/com/oraclequantapi/oraclequantapi/repository/Sequence_DATABASE.java new file mode 100644 index 0000000..a1a7c61 --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/repository/Sequence_DATABASE.java @@ -0,0 +1,76 @@ +package com.oraclequantapi.oraclequantapi.repository; + +import com.oraclequantapi.oraclequantapi.module.Sequence; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * Oracle persistence wrapper — guards every DB call against an unconfigured datasource. + * Safe to instantiate without a live DB; failures surface as descriptive RuntimeExceptions. + */ +@Component +public class Sequence_DATABASE { + + //------[DB] Null when datasource auto-config is excluded; checked before every operation + @Autowired(required = false) + private Repository repository; + + //------[DB] Throws if the datasource is not configured — called at the top of every method + private void checkAvailable() { + if (repository == null) { + throw new IllegalStateException( + "Database is not available — contact your administrator to configure the datasource." + ); + } + } + + //------[DB] Save a new record to Oracle + public Sequence persist(Sequence sequence) { + checkAvailable(); + try { + return repository.save(sequence); + } catch (Exception e) { + throw new RuntimeException("Unable to save your request — a database error occurred.", e); + } + } + + //------[DB] Load all records from Oracle + public List retrieveAll() { + checkAvailable(); + try { + return repository.findAll(); + } catch (Exception e) { + throw new RuntimeException("Unable to retrieve records — a database error occurred.", e); + } + } + + //------[DB] Overwrite an existing record; returns null if the id does not exist + public Sequence update(Sequence sequence) { + checkAvailable(); + if (!repository.existsById(sequence.getId())) { + return null; + } + try { + return repository.save(sequence); + } catch (Exception e) { + throw new RuntimeException("Unable to update your request — a database error occurred.", e); + } + } + + //------[DB] Delete a record by id; returns false if the id does not exist + public boolean remove(String id) { + checkAvailable(); + if (!repository.existsById(id)) { + return false; + } + try { + repository.deleteById(id); + return true; + } catch (Exception e) { + throw new RuntimeException("Unable to delete the requested record — a database error occurred.", e); + } + } + +} diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/services/Service.java b/src/main/java/com/oraclequantapi/oraclequantapi/services/Service.java new file mode 100644 index 0000000..c4da8dd --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/services/Service.java @@ -0,0 +1,84 @@ +package com.oraclequantapi.oraclequantapi.services; + +import com.oraclequantapi.oraclequantapi.module.Sequence; +import com.oraclequantapi.oraclequantapi.repository.Sequence_DATABASE; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +/** + * Business logic layer — validates inputs, runs the decoder algorithm, + * and delegates all persistence to Oracle via Sequence_DATABASE. + */ +@org.springframework.stereotype.Service +public class Service { + + @Autowired + private Sequence_DATABASE sequenceDb; + + //------[DB] Validates input, computes output, then persists the new enquiry + public Sequence addSequence(Sequence sequence) { + String input = sequence.getInput(); + if (input == null || !input.matches("^[a-z_]+$")) { + return null; + } + if (input.charAt(0) == '_') { + return null; + } + sequence.setOutput(sequenceLogicAlgorithm(input)); + return sequenceDb.persist(sequence); + } + + //------[DB] Validates new input, refreshes timestamp and output, then overwrites the record + public Sequence updateSequence(Sequence request) { + String newInput = request.getInput(); + if (newInput == null || !newInput.matches("^[a-z_]+$") || newInput.charAt(0) == '_') { + return null; + } + request.setCurrentTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + request.setOutput(sequenceLogicAlgorithm(newInput)); + return sequenceDb.update(request); + } + + //------[DB] Removes the enquiry matching the given id; returns true if deleted, false if not found + public boolean deleteSequence(String id) { + return sequenceDb.remove(id); + } + + //------[DB] Loads all rows from Oracle and recomputes @Transient output from each stored input + public List getAllSequences() { + List list = sequenceDb.retrieveAll(); + list.forEach(seq -> seq.setOutput(sequenceLogicAlgorithm(seq.getInput()))); + return list; + } + + // Self-delimiting length-encoded parser: + // Header — leading 'z' chars each add 26 to blockLength; the first non-z char adds its value (a=1..z=26, _=0) + // Data — consume exactly blockLength chars, sum their values, append total to output + private List sequenceLogicAlgorithm(String input) { + List output = new ArrayList<>(); + int i = 0; + while (i < input.length()) { + int blockLength = 0; + while (i < input.length() && input.charAt(i) == 'z') { + blockLength += 26; + i++; + } + if (i < input.length()) { + char h = input.charAt(i++); + blockLength += (h == '_') ? 0 : (h - 'a' + 1); + } + int sum = 0; + for (int j = 0; j < blockLength && i < input.length(); j++, i++) { + char c = input.charAt(i); + sum += (c == '_') ? 0 : (c - 'a' + 1); + } + output.add(sum); + } + return output; + } + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 99d0060..82507de 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,2 @@ spring.application.name=oraclequantapi +server.port=8080 diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml new file mode 100644 index 0000000..69ae247 --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,19 @@ +spring: + datasource: + # TODO: Replace with your Oracle DB connection URL + # Example: jdbc:oracle:thin:@//prod-db.company.com:1521/ORCLPDB1 + url: jdbc:oracle:thin:@//localhost:1521/ORCLPDB1 + # TODO: Replace with your Oracle DB username + # Example: QUANT_USER + username: QUANT_USER + # TODO: Replace with your Oracle DB password + # Example: Str0ngP@ssw0rd! + password: Str0ngP@ssw0rd! + driver-class-name: oracle.jdbc.OracleDriver + jpa: + database-platform: org.hibernate.dialect.OracleDialect + hibernate: + # 'update' creates table on first connect, alters schema on changes — switch to 'validate' in production + ddl-auto: update + show-sql: true + open-in-view: false diff --git a/src/test/java/com/oraclequantapi/oraclequantapi/OraclequantapiApplicationTests.java b/src/test/java/com/oraclequantapi/oraclequantapi/OraclequantapiApplicationTests.java deleted file mode 100644 index 2de285b..0000000 --- a/src/test/java/com/oraclequantapi/oraclequantapi/OraclequantapiApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.oraclequantapi.oraclequantapi; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class OraclequantapiApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..80c2943 --- /dev/null +++ b/version.txt @@ -0,0 +1,35 @@ +[0.1] 24/05/26 +- Initialized Spring Boot REST API project +- Added in-memory sequence storage endpoint +- Implemented clean controller-service architecture + +[0.2] 24/05/26 +- Added underscore-prefix input rejection rule +- Implemented length-encoded sequence parser algorithm +- Output array now exposed on POST and GET responses + +[0.3] 24/05/26 +- Moved Sequence model to dedicated Module package +- Added PUT endpoint for updating stored enquiries +- Added DELETE endpoint for removing stored enquiries + +[0.4] 24/05/26 +- Added Oracle JPA dependency and datasource scaffold +- Created Repository interface for Spring Data JPA operations +- Implemented Sequence_DATABASE with full exception handling + +[0.5] 24/05/26 +- Wired Sequence_DATABASE into Service — all CRUD operations now persist to Oracle +- Recompute @Transient output field on retrieval from DB + +[0.6] 24/05/26 +- Extracted JPA entity to DATABASE.java in repository package (SRP) +- Stripped all framework annotations from Sequence.java — pure domain POJO +- Updated Controller, Service, Sequence_DATABASE, Repository to use DATABASE throughout + +[1.0] 26/05/26 +- Promoted Sequence.java as the sole JPA entity — DATABASE.java removed +- Removed dead code: retrieveById, DATABASE all-args constructor, empty test stub +- Polished comments across all source files (JavaDoc + //------[DB] style) +- Updated README: reflect Oracle persistence, JAR run instructions +- Built and released executable JAR