trail-sense-database-persistence
Automates the boilerplate for adding Room persistence, including entity mapping and database configuration.
Install
mkdir -p .claude/skills/trail-sense-database-persistence && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/4634" && unzip -o skill.zip -d .claude/skills/trail-sense-database-persistence && rm skill.zipInstalls to .claude/skills/trail-sense-database-persistence
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Add new Room database persistence to Trail-Sense Android app. Use when the user asks to create, add, or implement database persistence for a model, including Entity, DAO, Repository, and AppDatabase migration. Covers entity-to-model mapping, index configuration, and standard CRUD operations.Key capabilities
- →Create a Room entity with mapping functions for a domain model
- →Generate a Data Access Object (DAO) interface with methods for CRUD operations
- →Update AppDatabase with the new entity, DAO accessor, and migration logic
- →Create a repository that exposes domain models and maps DAO entities
- →Register the repository singleton in ToolRegistration
- →Add indices for foreign keys, time-based columns, and frequently queried columns
How it works
It generates Room persistence components, including entities, DAOs, and repositories, and integrates them into the AppDatabase and ToolRegistration.
Inputs & outputs
When to use trail-sense-database-persistence
- →Create a new Room database entity
- →Implement DAO interfaces for CRUD operations
- →Add migration logic for database schema changes
- →Register repositories in the application container
About this skill
Trail-Sense Database Persistence
Add Room database persistence for a domain model following Trail-Sense patterns.
File Locations
app/src/main/java/com/kylecorry/trail_sense/tools/{toolName}/
├── domain/
│ └── {Model}.kt # Domain model (if not already exists)
└── infrastructure/persistence/
├── {Model}Entity.kt # Room entity
├── {Model}Dao.kt # DAO interface
└── {Model}Repo.kt # Repository
Also update:
app/src/main/java/com/kylecorry/trail_sense/main/persistence/AppDatabase.ktapp/src/main/java/com/kylecorry/trail_sense/main/persistence/Converters.kt(if new types needed){ToolName}ToolRegistration.kt- register repo singleton
Workflow
- Inspect nearby persistence packages and the target domain model. This step is complete when table name, columns, nullability, indices, and conversion rules are known.
- Create the Entity with mapping functions. This step is complete when every persisted domain property round-trips through
from()andto{Model}(). - Create the DAO interface. This step is complete when repository-needed reads, upserts, and deletes have DAO methods.
- Add the entity, DAO accessor, version bump, and migration to
AppDatabase. This step is complete when the migration SQL exactly matches the entity columns, nullability, primary key, and indices. - Create the Repository. This step is complete when public methods expose domain models, run blocking work on IO, and map DAO entities only inside the repository.
- Register the repo singleton in ToolRegistration. This step is complete when the tool owns the repository lifecycle.
- Validate the persistence change with the narrowest useful build or test. This step is complete when
./scripts/build.shor a focused unit test passes, or the exact blocker is reported.
1. Entity
package com.kylecorry.trail_sense.tools.{toolname}.infrastructure.persistence
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import com.kylecorry.trail_sense.tools.{toolname}.domain.{Model}
@Entity(
tableName = "{table_name}", // plural, lowercase, snake_case
indices = [
// Add indices for foreign keys and frequently queried columns
// Index(value = ["parent_id"]),
// Index(value = ["time"])
]
)
data class {Model}Entity(
@ColumnInfo(name = "column_name") val property: Type,
// ... other properties
) {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "_id")
var id: Long = 0
fun to{Model}(): {Model} {
return {Model}(
id = id,
// Map entity properties to domain model
)
}
companion object {
fun from(model: {Model}): {Model}Entity {
return {Model}Entity(
// Map domain model properties to entity
).also {
it.id = model.id
}
}
}
}
Index Guidelines
Add indices for:
- Foreign key columns (e.g.,
parent_id,group_id) - Time-based columns if queried by time range
- Columns used in WHERE clauses frequently
- Composite indices for multi-column filters:
Index(value = ["col1", "col2"])
Type Mapping
- Inspect nearby entities before choosing stored types. Entity fields, converter use, and migration SQL must produce the same Room schema.
Instant-> SQLINTEGERepoch millis; entity field may useInstantwith converter orLongmappingDuration-> SQLINTEGERmillis; entity field may useDurationwith converter orLongmappingCoordinate-> split intolatitude: Double,longitude: DoubleDistance-> store asFloatin meters- Enums with
idproperty -> SQLINTEGER; use existing converters or manual id mapping AppColor-> SQLINTEGER; converter exists- Lists/collections -> join to comma-separated string, parse in mapping
2. DAO
package com.kylecorry.trail_sense.tools.{toolname}.infrastructure.persistence
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Query
import androidx.room.Upsert
import kotlinx.coroutines.flow.Flow
@Dao
interface {Model}Dao {
@Query("SELECT * FROM {table_name}")
fun getAll(): Flow<List<{Model}Entity>>
@Query("SELECT * FROM {table_name}")
suspend fun getAllSync(): List<{Model}Entity>
@Query("SELECT * FROM {table_name} WHERE _id = :id LIMIT 1")
suspend fun get(id: Long): {Model}Entity?
@Upsert
suspend fun upsert(entity: {Model}Entity): Long
@Delete
suspend fun delete(entity: {Model}Entity)
}
Optional DAO Methods
// Filter by parent/group
@Query("SELECT * FROM {table_name} WHERE parent_id IS :parentId")
suspend fun getAllInGroup(parentId: Long?): List<{Model}Entity>
// Time-based cleanup
@Query("DELETE FROM {table_name} WHERE time < :minEpochMillis")
suspend fun deleteOlderThan(minEpochMillis: Long)
// Get latest
@Query("SELECT * FROM {table_name} ORDER BY _id DESC LIMIT 1")
suspend fun getLast(): {Model}Entity?
3. AppDatabase Updates
Add Entity to Database
In AppDatabase.kt, add entity to @Database annotation:
@Database(
entities = [..., {Model}Entity::class],
version = {NEXT_VERSION}, // Increment from current
exportSchema = false
)
Add DAO Accessor
abstract fun {model}Dao(): {Model}Dao
Add Migration
Inside buildDatabase(), add migration before the return Room.databaseBuilder:
val MIGRATION_{PREV}_{NEXT} = object : Migration({PREV}, {NEXT}) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("""
CREATE TABLE IF NOT EXISTS `{table_name}` (
`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`column_name` TEXT NOT NULL,
`nullable_column` TEXT DEFAULT NULL
-- Match column types: TEXT, INTEGER, REAL
-- NOT NULL for non-nullable, DEFAULT NULL for nullable
)
""".trimIndent())
// Add indices if defined in entity
// db.execSQL("CREATE INDEX IF NOT EXISTS index_{table_name}_{column} ON {table_name}({column})")
}
}
Register Migration
Add to .addMigrations():
.addMigrations(
...,
MIGRATION_{PREV}_{NEXT}
)
4. Repository
package com.kylecorry.trail_sense.tools.{toolname}.infrastructure.persistence
import android.annotation.SuppressLint
import android.content.Context
import com.kylecorry.luna.coroutines.onIO
import com.kylecorry.trail_sense.main.persistence.AppDatabase
import com.kylecorry.trail_sense.tools.{toolname}.domain.{Model}
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
class {Model}Repo private constructor(context: Context) {
private val dao = AppDatabase.getInstance(context).{model}Dao()
fun getAll(): Flow<List<{Model}>> = dao.getAll()
.map { it.map { entity -> entity.to{Model}() } }
.flowOn(Dispatchers.IO)
suspend fun getAllSync(): List<{Model}> = onIO {
dao.getAllSync().map { it.to{Model}() }
}
suspend fun get(id: Long): {Model}? = onIO {
dao.get(id)?.to{Model}()
}
suspend fun add(model: {Model}): Long = onIO {
dao.upsert({Model}Entity.from(model))
}
suspend fun delete(model: {Model}) = onIO {
dao.delete({Model}Entity.from(model))
}
companion object {
@SuppressLint("StaticFieldLeak")
private var instance: {Model}Repo? = null
@Synchronized
fun getInstance(context: Context): {Model}Repo {
if (instance == null) {
instance = {Model}Repo(context.applicationContext)
}
return instance!!
}
}
}
5. Register Singleton in ToolRegistration
In {ToolName}ToolRegistration.kt, add the repo to singletons:
object {ToolName}ToolRegistration : ToolRegistration {
override fun getTool(context: Context): Tool {
return Tool(
// ... other properties
singletons = listOf(
{Model}Repo::getInstance
),
// ...
)
}
}
SQL Type Reference
| Kotlin Type | SQLite Type | Notes |
|---|---|---|
Long, Int | INTEGER | |
Double, Float | REAL | |
String | TEXT | |
Boolean | INTEGER | 0/1 |
Instant | INTEGER | epoch millis |
| Enums | INTEGER | via id property |
| Nullable | Add DEFAULT NULL |
How it compares
This skill automates the creation and integration of Room persistence components, unlike manual setup that requires individual file creation and configuration.
Compared to similar skills
trail-sense-database-persistence side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| trail-sense-database-persistence (this skill) | 1 | 6mo | No flags | Intermediate |
| repository | 0 | 3mo | No flags | Intermediate |
| drizzle | 238 | 2mo | No flags | Intermediate |
| database-development | 1 | 2mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by kylecorry31
View all by kylecorry31 →You might also like
repository
navikt
Repository-mønster med Kotlin object og Connection/TransactionalSession extension-funksjoner, DBUtils-transaksjoner og ResultSet-mapping
drizzle
lobehub
Drizzle ORM schema and database guide. Use when working with database schemas (src/database/schemas/*), defining tables, creating migrations, or database model code. Triggers on Drizzle schema definition, database migrations, or ORM usage questions.
database-development
vm0-ai
Database migrations and Drizzle ORM guidelines for the vm0 project
dbx-regenerate
storj
Regenerate DBX code after making changes to .dbx schema files. Runs code generation, shows diff summary, validates compilation, and reports any errors.
database-schema
alinaqi
Schema awareness - read before coding, type generation, prevent column errors
nop-orm-modeler
entropy-cloud
Generate, validate, and modify Nop ORM models from MySQL DDL/SQL or business requirements. Covers entity modeling, relationships, domains, dictionaries, displayName localization, and ORM file organization (Delta mode). Use for database-first or requirements-first ORM development.