Skip to content

Fix database structure#183

Open
TheMeinerLP wants to merge 33 commits intomainfrom
fix/database-structure-audit
Open

Fix database structure#183
TheMeinerLP wants to merge 33 commits intomainfrom
fix/database-structure-audit

Conversation

@TheMeinerLP
Copy link
Copy Markdown
Contributor

  • fix(db): stop closing shared SessionFactory in flag service
  • fix(db): return HomePosition model from entityToModel mapper
  • fix(db): fail fast when the Hibernate SessionFactory cannot be built
  • fix(plugin): disable plugin cleanly when bootstrap throws
  • fix(db): correct chunk-removal return value and parameter binding
  • fix(db): tighten transaction boundaries in land service
  • fix(db): add explicit join columns and id-based equality to LandEntity
  • fix(db): make FlagContainerEntity the inverse side of the Land link
  • fix(db): switch LandMemberEntity.member to ManyToOne
  • fix(db): add unique, NOT NULL and length constraints to LandPlayerEntity
  • fix(db): enforce NOT NULL on HomePositionEntity columns
  • fix(db): add name column and id-based equality to LandAreaEntity
  • fix(db): prevent duplicate claims on the same chunk
  • fix(db): constrain LandRoleFlagEntity columns and de-duplicate
  • fix(db): constrain LandNaturalFlagEntity columns and de-duplicate
  • fix(db): constrain LandEntityCapFlagEntity columns and de-duplicate
  • fix(db): use ClaimedChunkMappingStrategy for chunk mapping
  • fix(db): use per-flag strategies when mapping FlagContainer children
  • docs(db): document session-open requirement for LandMappingStrategy
  • chore(config): consolidate entity mappings into common/hibernate.cfg.xml
  • chore(config): drop duplicate plugin-side hibernate.cfg.xml
  • chore(test): run common tests against in-memory H2
  • chore(build): pull in H2 for the common test classpath
  • chore(test): remove obsolete constants.kt
  • chore(test): remove obsolete TestDatabaseServiceImpl.kt
  • chore(test): remove obsolete TestLandFlagService.kt
  • chore(test): remove obsolete TestLandPlayerService.kt
  • chore(test): remove obsolete TestLandService.kt
  • chore(test): remove obsolete TestPandorasClusterApiImpl.kt
  • chore(build): drop unused Liquibase plugin alias
  • chore(build): drop Liquibase and migrate Shadow to com.gradleup.shadow

TheMeinerLP and others added 30 commits April 18, 2026 19:53
Each of the 9 flag operations wrapped the SessionFactory in
try-with-resources, closing the shared singleton after the first call.
Subsequent flag operations then failed with "SessionFactory is closed".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The entity-to-model direction was constructing a new HomePositionEntity
instead of the HomePosition API model, so callers received an entity
where they expected the DTO.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously a HibernateException was swallowed, databaseService was left
null, and the plugin continued to enable with null service references,
NPEing on the first command. Throw IllegalStateException instead so the
plugin entry point can log and disable cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wrap the PandorasClusterImpl construction in onEnable so a failed
Hibernate bootstrap no longer leaves the plugin half-initialized — the
PluginManager disables the plugin and the error is surfaced in the log.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- removeClaimedChunk hard-coded return false, so callers never saw a
  successful deletion.
- getClaimedChunk declared a :chunkIndex named parameter but never bound
  it, making the query return an arbitrary row.
- Document unclaimArea as a best-effort composition of already-
  transactional sub-operations so future readers do not assume atomicity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- createLand now persists the land, its home, its flag container, the
  default LandArea and its initial ClaimedChunk in a single transaction
  so a partial failure rolls back cleanly. Previously the area creation
  ran after the outer commit, leaving orphan rows on exception.
- unclaimLand moves its cross-service sub-calls (removeFlagsFromLand,
  landAreaService::unclaimArea) outside the session, then loads the
  LandEntity by id and removes it along with its HomePositionEntity.
  The old code called session.remove(land) with the API model.
- getLands eagerly fetches owner, home, flagContainer and areas to
  prevent LazyInitializationException after the session closes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Without @joincolumn, Hibernate generated implementation-defined FK
column names. Bind owner/home/flagContainerEntity to owner_id, home_id
and flag_container_id respectively, all NOT NULL, and mark the
flag_container side as the owning side of the OneToOne (the inverse
mappedBy moves to FlagContainerEntity). Add id-based equals/hashCode
with a null-id identity fallback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the unmapped OneToOne so the LandEntity side alone owns the FK
column (flag_container_id). Add id-based equals/hashCode.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A player can be a member of multiple land areas, so the OneToOne was
semantically wrong — the second addLandMember for the same player would
violate the uniqueness implied by OneToOne. Also add an explicit
member_id join column.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace dialect-specific columnDefinition with portable length /
nullable / unique annotations. uuid becomes the natural unique key
(length 36), name is length 16 NOT NULL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A position without world + coordinates has no meaning; declare every
column non-nullable and give world a 64-char bound.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
name had no @column annotation, so Hibernate generated an unbounded
nullable column. Declare length 64 NOT NULL and add id-based
equals/hashCode.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rename chunkIndex to chunk_index and add a composite unique constraint
on (landArea_id, chunk_index) so the same chunk can no longer be
claimed twice in the same area. Also add id-based equals/hashCode.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add length/NOT NULL on name, state, role and flagContainer_id, plus a
uk_role_flags_container_name unique constraint so the same role flag
cannot be persisted twice for one container. Add id-based equality.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add length/NOT NULL on name, state and flagContainer_id, plus a
uk_natural_flags_container_name unique constraint. Add id-based
equality.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add length/NOT NULL on name, spawn_limit and flagContainer_id, plus a
uk_entitycap_flags_container_name unique constraint. Add id-based
equality.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
getChunks was feeding ClaimedChunkDto instances into a new
LandAreaMappingStrategy, so the instanceof check failed and every
chunk came back null. Use ClaimedChunkMappingStrategy.create()
instead. Also document that the strategy must run inside an open
Hibernate session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The three flag collections were previously mapped through a single
LandMappingStrategy instance, so every NaturalFlag / RoleFlag /
EntityCapFlag failed the instanceof check and came back null. Wire each
collection to its own NaturalFlagMappingStrategy / RoleFlagMappingStrategy
/ EntityCapFlagMappingStrategy and keep the land link on its own context.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The common module builds the SessionFactory, so the <mapping class="…"/>
list lives here. The plugin-side hibernate.cfg.xml becomes redundant and
is removed in a follow-up commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entity mappings now live in common/hibernate.cfg.xml. Keeping two copies
invited drift — the plugin copy had the mappings while the common copy
had the dialect/connection block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The test config pointed at localhost PostgreSQL, so a developer needed a
running Postgres container to execute :common:test. Switch to an
in-memory H2 database with H2Dialect and create-drop so tests can run in
isolation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New in-memory test configuration in connection.cfg.xml needs the H2
driver at runtime for :common:test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The common module does not apply the Kotlin plugin, so this test
support file never compiled. It also referenced DBO classes that were
removed long ago.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dead test support code referencing removed DatabaseService contract.
Common module has no Kotlin plugin applied so it never compiled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dead test support code referencing removed DBO types. Common module has
no Kotlin plugin applied so it never compiled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dead test support code referencing removed DBO types. Common module has
no Kotlin plugin applied so it never compiled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dead test support code referencing removed DBO types. Common module has
no Kotlin plugin applied so it never compiled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dead test support code referencing a removed PandorasClusterApi
interface and the wrong service constructors. Common module has no
Kotlin plugin applied so it never compiled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The liquibase Gradle plugin was applied but never configured — no
changelog file, no liquibase {} block, no activities. Schema is owned
by Hibernate via hbm2ddl.auto=update, so remove the dead alias.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Remove the unused liquibase version/plugin entries. Schema is owned by
  Hibernate via hbm2ddl.auto=update.
- Bump Shadow 8.1.1 → 9.4.1 and switch plugin id from the archived
  com.github.johnrengelman.shadow to com.gradleup.shadow so shadowJar
  works under Gradle 9.x.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@TheMeinerLP TheMeinerLP requested a review from a team as a code owner April 18, 2026 17:58
TheMeinerLP and others added 2 commits April 18, 2026 20:18
…chException

Hibernate 7 rejects JOIN FETCH-ing two bag-style List collections in the
same query. The LandAreaEntity has two such bags (members and chunks),
so the original query failed at translation time with
"cannot simultaneously fetch multiple bags".

Fetch the area together with its parent land in the primary query, then
initialize the two bag collections separately via
Hibernate.initialize(). Still inside the session, so the mapper can
walk them without a LazyInitializationException.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After home_id and flag_container_id became NOT NULL, Hibernate started
throwing TransientPropertyValueException because LandEntity was
persisted before its HomePositionEntity. Reorder so:

1. home and flag container (no outgoing non-null FKs) are persisted first
2. owner is referenced via session.getReference (player already exists)
3. landEntity is persisted once all three FKs point at managed entities
4. area and its initial chunk follow

This also drops the flagContainer.withLand(landEntity) call — after
Phase 3, FlagContainerEntity.land is mappedBy and carries no FK column,
so the back-reference is purely ornamental at persist time.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant