Compare commits
3 Commits
c0d9de9120
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b5357d644b | |||
| 793b3b52fa | |||
| ac64321225 |
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Rien Nentjes
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
182
readme.md
Normal file
182
readme.md
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
# Simple Persistence for Kotlin
|
||||||
|
|
||||||
|
A lightweight, file-based persistence library for Kotlin applications that provides a simple way to store, retrieve, and query objects.
|
||||||
|
|
||||||
|
## Available on Maven Central
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// Gradle Kotlin DSL
|
||||||
|
implementation("nl.astraeus:simple-persistence-kotlin:1.1.4")
|
||||||
|
```
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
// Gradle Groovy DSL
|
||||||
|
implementation 'nl.astraeus:simple-persistence-kotlin:1.1.4'
|
||||||
|
```
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- Maven -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>nl.astraeus</groupId>
|
||||||
|
<artifactId>simple-persistence-kotlin</artifactId>
|
||||||
|
<version>1.1.4</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Simple and intuitive API for persisting Kotlin objects
|
||||||
|
- Transaction support with ACID properties
|
||||||
|
- Optimistic locking to handle concurrent modifications
|
||||||
|
- Flexible indexing for efficient querying
|
||||||
|
- Object references and collections handling
|
||||||
|
- Encryption support for sensitive data
|
||||||
|
- Snapshot and transaction log capabilities
|
||||||
|
- File-based storage with automatic cleanup
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Define your model
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
class Person(
|
||||||
|
override var id: Long = 0,
|
||||||
|
override var version: Long = 0,
|
||||||
|
val name: String,
|
||||||
|
val age: Int,
|
||||||
|
company: Company? = null
|
||||||
|
) : Persistable, Cloneable {
|
||||||
|
var company: Company? by nullableReference(company)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val serialVersionUID: Long = 1L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Company(
|
||||||
|
override var id: Long = 0,
|
||||||
|
override var version: Long = 0,
|
||||||
|
val name: String
|
||||||
|
) : Persistable, Cloneable {
|
||||||
|
val persons: MutableCollection<Person> = referenceCollection()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val serialVersionUID: Long = 1L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Initialize the persistence manager
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val persistent = Persistent(
|
||||||
|
directory = File("data", "my-app"),
|
||||||
|
indexes = arrayOf(
|
||||||
|
index<Person>("name") { p -> (p as? Person)?.name ?: "" },
|
||||||
|
index<Person>("age") { p -> (p as? Person)?.age ?: -1 },
|
||||||
|
index<Company>("name") { p -> (p as? Company)?.name ?: "" }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Perform CRUD operations
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// Create or update
|
||||||
|
persistent.transaction {
|
||||||
|
val person = Person(
|
||||||
|
id = 1L,
|
||||||
|
name = "John Doe",
|
||||||
|
age = 30
|
||||||
|
)
|
||||||
|
|
||||||
|
val company = Company(
|
||||||
|
id = 1L,
|
||||||
|
name = "ACME Inc."
|
||||||
|
)
|
||||||
|
|
||||||
|
person.company = company
|
||||||
|
company.persons.add(person)
|
||||||
|
|
||||||
|
store(person)
|
||||||
|
store(company)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read
|
||||||
|
persistent.query {
|
||||||
|
val person = find<Person>(1L)
|
||||||
|
println("Found person: $person")
|
||||||
|
|
||||||
|
// Query by index
|
||||||
|
val persons: List<Person> = findByIndex("age", 30)
|
||||||
|
|
||||||
|
// Search with predicate
|
||||||
|
val results = search(Person::class) { p -> p.name.startsWith("John") }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
persistent.transaction {
|
||||||
|
val person = find<Person>(1L)
|
||||||
|
if (person != null) {
|
||||||
|
delete(person)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
- In-memory model: Your objects live in memory during normal operation for fast reads and writes.
|
||||||
|
- Durable writes: Every create/update/delete inside a transaction is appended to on-disk transaction log files (*.trn). This is a write-ahead log approach.
|
||||||
|
- Startup recovery: On application startup, the library replays the transaction logs (and any snapshot, if present) to rebuild the in-memory model.
|
||||||
|
- Snapshots: You can create a compact on-disk snapshot (*.snp) of the current in-memory state. On the next startup, the snapshot is loaded first and only the newer transaction log files are replayed. This keeps startup time low without losing durability.
|
||||||
|
|
||||||
|
Tip: Call snapshot() periodically (or on controlled shutdown) and use removeOldFiles() to prune obsolete logs after a successful snapshot.
|
||||||
|
|
||||||
|
## Advanced Features
|
||||||
|
|
||||||
|
### Indexing
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// Define complex indexes
|
||||||
|
val persistent = Persistent(
|
||||||
|
directory = File("data", "my-app"),
|
||||||
|
indexes = arrayOf(
|
||||||
|
index<Person>("ageOver30") { p -> ((p as? Person)?.age ?: 0) > 30 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Query using indexes
|
||||||
|
persistent.query {
|
||||||
|
findByIndex<Person>("ageOver30", true).forEach { person ->
|
||||||
|
println("Person over 30: ${person.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Transactions
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// Read-only query
|
||||||
|
persistent.query {
|
||||||
|
// Only read operations allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read-write transaction
|
||||||
|
persistent.transaction {
|
||||||
|
// Both read and write operations allowed
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Snapshots and Maintenance
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// Create a snapshot of the current state
|
||||||
|
persistent.snapshot()
|
||||||
|
|
||||||
|
// Remove old transaction log files
|
||||||
|
persistent.removeOldFiles()
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License - see the LICENSE file for details.
|
||||||
Reference in New Issue
Block a user