Skip to content

Feat: Add an ORM to save homes - #5

Merged
AxenoDev merged 6 commits into
masterfrom
feat/add-orm
Feb 6, 2026
Merged

Feat: Add an ORM to save homes#5
AxenoDev merged 6 commits into
masterfrom
feat/add-orm

Conversation

@AxenoDev

@AxenoDev AxenoDev commented Feb 6, 2026

Copy link
Copy Markdown
Owner

#1

Summary by CodeRabbit

  • New Features

    • Persistent home storage with SQLite or MySQL support; homes load on startup and save on shutdown
    • Thread-safe in-memory cache for more reliable concurrent access
  • Configuration

    • New database section in settings (SQLite default) with connection details and MySQL options
  • Changes

    • Homes are persisted on shutdown; player unload-on-quit handling removed to centralize lifecycle management

@AxenoDev AxenoDev added this to the 1.0.0-beta-1 milestone Feb 6, 2026
@AxenoDev AxenoDev self-assigned this Feb 6, 2026
@AxenoDev AxenoDev added the 📦 Features Ajout d'une fonctionnalité label Feb 6, 2026
@coderabbitai

coderabbitai Bot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds ORMLite-backed persistence and DB configuration (SQLite/MySQL), a new DatabaseManager, concurrent in-memory player homes cache with load/save lifecycle (init/shutdown), model ORM annotations and signature change for Home, removes PlayerQuit unload listener, and updates build properties for JDBC drivers.

Changes

Cohort / File(s) Summary
Build & Properties
build.gradle, gradle.properties
Added ORMLite, SQLite, and MySQL JDBC dependency declarations and corresponding version properties; modrinth versionType logic added.
Database manager & config
src/main/java/me/axeno/hommr/managers/DatabaseManager.java, src/main/resources/config.yml
New DatabaseManager: initializes JDBC connection (sqlite/mysql), creates DAO/table, provides getAllHomes/saveAllHomes/close; config.yml gains database section with sqlite default and mysql option comments.
Home persistence model
src/main/java/me/axeno/hommr/models/Home.java
Annotated Home with ORMLite (@DatabaseTable, @DatabaseField), added no-arg constructor, changed fromLocation signature to include owner UUID, and added createdAt/id handling.
Player homes cache model
src/main/java/me/axeno/hommr/models/PlayerHomes.java
Switched to ConcurrentHashMap, added thread-safe set/get/remove/has/getHomeNames methods returning Optionals where appropriate.
Home manager & lifecycle
src/main/java/me/axeno/hommr/managers/HomeManager.java, src/main/java/me/axeno/hommr/Hommr.java
HomeManager now holds concurrent per-player cache and DatabaseManager; added init() (load from DB) and shutdown() (persist and close DB); set/get/delete/teleport flows updated to use cache and fire events; Hommr exposes Lombok getters and calls shutdown on disable; removed PlayerListener registration.
Removed listener
src/main/java/me/axeno/hommr/listeners/PlayerListener.java
Deleted PlayerListener that handled PlayerQuitEvent and previously unloaded player homes.

Sequence Diagram(s)

sequenceDiagram
    participant Plugin as Hommr (Plugin)
    participant Manager as HomeManager
    participant DB as DatabaseManager

    Plugin->>Manager: onEnable -> init()
    Manager->>DB: init(): open connection, ensure table
    DB-->>Manager: return all homes
    Manager->>Manager: populate in-memory cache

    Plugin->>Manager: onDisable -> shutdown()
    Manager->>DB: saveAllHomes(cached homes)
    DB->>DB: transaction: clear table, batch insert
    DB-->>Manager: close()
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I dug a tiny DB den tonight,
Homes tucked safe away from sight,
ORMLite roots snug in loam,
Cached burrows hum — I call them home,
I save and sleep until first light.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately summarizes the main change: adding ORM (Object-Relational Mapping) integration to persist homes to a database.
Docstring Coverage ✅ Passed Docstring coverage is 90.91% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/add-orm

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai

coderabbitai Bot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #6

Docstrings generation was requested by @AxenoDev.

* #5 (comment)

The following files were modified:

* `src/main/java/me/axeno/hommr/Hommr.java`
* `src/main/java/me/axeno/hommr/managers/DatabaseManager.java`
* `src/main/java/me/axeno/hommr/managers/HomeManager.java`
* `src/main/java/me/axeno/hommr/models/Home.java`
* `src/main/java/me/axeno/hommr/models/PlayerHomes.java`

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In `@src/main/java/me/axeno/hommr/managers/DatabaseManager.java`:
- Around line 56-59: DatabaseManager currently swallows SQLException during
initialization leaving the field homeDao null and risking NPEs later; either
fail fast by re-throwing a runtime exception from the constructor/init block
(wrap the caught SQLException and throw new IllegalStateException("Failed to
initialize database", e)) so the application cannot continue with a null DAO, or
add defensive checks at the start of getAllHomes and saveAllHomes (e.g., if
(homeDao == null) throw new IllegalStateException("Database not initialized"))
to prevent usage when initialization failed — update the catch in the
DatabaseManager init and/or add the null-checks in getAllHomes and saveAllHomes
accordingly.
- Around line 38-46: In DatabaseManager where type.equals("mysql") and you build
databaseUrl/username/password via Hommr.getInstance().getConfig(), validate that
databaseUrl, username and password are non-null/non-empty before constructing
the JdbcConnectionSource; if any are missing, throw a clear
IllegalArgumentException or log an explicit error and abort initialization
(instead of calling new JdbcConnectionSource with nulls). Update the block that
sets databaseUrl/username/password and the creation of connectionSource to
perform these checks and emit a helpful message referencing the missing config
keys so the failure is actionable.
- Around line 75-90: The saveAllHomes method clears the Home table up front
(TableUtils.clearTable) and then performs inserts with homeDao.callBatchTasks,
which can leave the DB empty if interrupted; change the implementation to
perform the clear-and-insert inside a single transaction using
TransactionManager.callInTransaction (import
com.j256.ormlite.misc.TransactionManager) so the clear and all
homeDao.create(...) calls are atomic and will roll back on exception; ensure any
caught exceptions are rethrown as SQLException to preserve the current API.

In `@src/main/java/me/axeno/hommr/managers/HomeManager.java`:
- Around line 44-58: Current shutdown-only persistence risks data loss; update
HomeManager to persist changes incrementally by calling
databaseManager.saveHome(home) inside setHome(...) after updating the
PlayerHomes instance and call databaseManager.deleteHome(playerId, homeName)
inside deleteHome(...), handling SQLException with
Hommr.getInstance().getLogger().log(...). To avoid blocking server threads,
perform these DB calls asynchronously (use an ExecutorService or scheduler) or
add a periodic autosave task that calls databaseManager.saveAllHomes(new
ArrayList<>(collect homes from playerHomesCache.values())), and ensure
databaseManager is thread-safe and closed on shutdown in shutdown().
- Around line 24-42: HomeManager.init() assumes databaseManager is usable but
shutdown() leaves a closed instance, causing databaseManager.getAllHomes() to
fail; fix by ensuring shutdown() nullifies or replaces the closed
DatabaseManager: in HomeManager.shutdown() call the
DatabaseManager.close()/shutdown routine and set HomeManager.databaseManager =
null (or provide a DatabaseManager.isClosed/isConnected check), and in
HomeManager.init() re-create databaseManager when it is null or closed before
calling databaseManager.getAllHomes(); reference symbols: HomeManager.init(),
HomeManager.shutdown(), databaseManager, DatabaseManager,
databaseManager.getAllHomes().

In `@src/main/resources/config.yml`:
- Around line 14-16: The config.yml currently ships with real-looking default DB
credentials (url/username/password) which is insecure; update the username and
password fields in config.yml (the username and password keys) to non-credential
placeholders or empty strings (e.g., username: your_username and password:
your_password or username: "" and password: "") and add a short comment or
README note instructing users to populate them with real credentials before
deployment.
🧹 Nitpick comments (2)
src/main/java/me/axeno/hommr/models/Home.java (1)

23-27: Consider adding a unique constraint on (owner, name) to prevent duplicate home names per player.

Currently, the database schema doesn't prevent multiple homes with the same name for the same owner. While the in-memory PlayerHomes map would overwrite duplicates, if data is loaded from a corrupted DB state, this could cause issues.

♻️ Suggested approach using ORMLite's uniqueCombo
-    `@DatabaseField`(canBeNull = false, index = true)
+    `@DatabaseField`(canBeNull = false, index = true, uniqueCombo = true)
     private UUID owner;

-    `@DatabaseField`(canBeNull = false)
+    `@DatabaseField`(canBeNull = false, uniqueCombo = true)
     private String name;
src/main/java/me/axeno/hommr/managers/DatabaseManager.java (1)

71-73: Consider adding a method to save/update individual homes rather than bulk-only operations.

Currently, there's only saveAllHomes which requires clearing and re-inserting all data. For efficiency and safety, consider adding methods like saveHome(Home) and deleteHome(Home) for incremental updates.

♻️ Suggested additional methods
public void saveHome(Home home) throws SQLException {
    homeDao.createOrUpdate(home);
}

public void deleteHome(Home home) throws SQLException {
    homeDao.delete(home);
}

Comment thread src/main/java/me/axeno/hommr/managers/DatabaseManager.java
Comment thread src/main/java/me/axeno/hommr/managers/DatabaseManager.java
Comment thread src/main/java/me/axeno/hommr/managers/DatabaseManager.java
Comment thread src/main/java/me/axeno/hommr/managers/HomeManager.java
Comment thread src/main/java/me/axeno/hommr/managers/HomeManager.java
Comment thread src/main/resources/config.yml Outdated
@coderabbitai

coderabbitai Bot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

Note

Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/me/axeno/hommr/models/Home.java (1)

58-71: ⚠️ Potential issue | 🟡 Minor

Potential NullPointerException if location or world is null.

location.getWorld() can return null for unloaded worlds, and .getName() would then throw NPE. Consider adding null checks.

🛡️ Proposed defensive check
 public static Home fromLocation(UUID owner, String name, Location location) {
+    if (location == null || location.getWorld() == null) {
+        throw new IllegalArgumentException("Location and its world must not be null");
+    }
     return new Home(
             0,
             owner,
🤖 Fix all issues with AI agents
In `@src/main/java/me/axeno/hommr/managers/HomeManager.java`:
- Around line 76-78: playerHomesCache can be null if init() hasn't run, causing
getOrCreatePlayerHomes(UUID) to NPE; ensure playerHomesCache is initialized
inline (e.g., new ConcurrentHashMap<>) or add a null-guard in
getOrCreatePlayerHomes that initializes it before use, and adjust init() to
clear() rather than reassign the map; locate the playerHomesCache field and the
getOrCreatePlayerHomes and init() methods to implement the inline initialization
or lazy-init guard.
🧹 Nitpick comments (1)
src/main/java/me/axeno/hommr/Hommr.java (1)

38-38: Consider handling initialization failure.

If HomeManager.init() fails to connect to the database, the plugin continues with an empty cache. Depending on requirements, you may want to disable the plugin on critical initialization failure.

💡 Example: Fail-fast on init error
try {
    HomeManager.init();
} catch (Exception e) {
    getLogger().severe("Failed to initialize HomeManager: " + e.getMessage());
    getServer().getPluginManager().disablePlugin(this);
    return;
}

This would require HomeManager.init() to propagate exceptions rather than catching them internally.

Comment thread src/main/java/me/axeno/hommr/managers/HomeManager.java
@coderabbitai coderabbitai Bot mentioned this pull request Feb 6, 2026
@coderabbitai

coderabbitai Bot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

✅ Created PR with unit tests: #7

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/main/java/me/axeno/hommr/managers/DatabaseManager.java`:
- Around line 63-67: The TableUtils.createTableIfNotExists call inside
DatabaseManager currently swallows SQLException which leaves homeDao initialized
but the table missing; modify the catch to rethrow the exception (or wrap it in
a RuntimeException) so the outer try/catch in the DatabaseManager constructor
can handle it and abort initialization. Locate the block around
TableUtils.createTableIfNotExists(connectionSource, Home.class) and change the
SQLException handling to throw the caught exception (or new
RuntimeException("Failed to create Home table", e)) instead of only logging;
this prevents subsequent calls like getAllHomes() or saveAllHomes() from running
against an inconsistent state.
🧹 Nitpick comments (1)
src/main/java/me/axeno/hommr/managers/DatabaseManager.java (1)

97-99: Add null guard for homeDao in data access methods.

If getAllHomes() is called after a failed initialization or after close(), homeDao could be null, causing an NPE. Consider adding a defensive check.

🛡️ Suggested fix
     public List<Home> getAllHomes() throws SQLException {
+        if (homeDao == null) {
+            throw new IllegalStateException("Database not initialized");
+        }
         return homeDao.queryForAll();
     }

Comment thread src/main/java/me/axeno/hommr/managers/DatabaseManager.java Outdated
@AxenoDev
AxenoDev merged commit 4508866 into master Feb 6, 2026
6 checks passed
@AxenoDev
AxenoDev deleted the feat/add-orm branch February 6, 2026 23:44
@coderabbitai coderabbitai Bot mentioned this pull request Feb 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📦 Features Ajout d'une fonctionnalité

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant