This module provides an abstract and reusable foundation for supporting translatable (multi-language) entities using Spring Data JPA.
It defines core interfaces, abstract repositories, and a base service class to handle translations with locale-specific logic.
com.mewebstudio.springboot.jpa.translatable
βββ ITranslatable.kt
βββ ITranslation.kt
βββ JpaTranslatableRepository.kt
βββ JpaTranslationRepository.kt
βββ AbstractTranslatableService.kt
Represents an entity that supports translations.
interface ITranslatable<ID, T : ITranslation<ID, *>> {
val id: ID
val translations: MutableList<T>
}
Represents a translation of an entity in a specific locale.
interface ITranslation<ID, T> {
val id: ID
val owner: T
val locale: String
}
JpaTranslatableRepository<T : ITranslatable<ID, TR>, ID, TR : ITranslation<ID, *>> : JpaRepository<T, ID>
Generic JPA repository for translatable entities.
@NoRepositoryBean
interface JpaTranslatableRepository<T : ITranslatable<ID, TR>, ID, TR : ITranslation<ID, *>> : JpaRepository<T, ID> {
// ...
}
Generic JPA repository for translation entities.
@NoRepositoryBean
interface JpaTranslationRepository<T : ITranslation<ID, OWNER>, ID, OWNER> : JpaRepository<T, ID> {
// ...
}
Provides a base service class for business logic operations.
abstract class AbstractTranslatableService<T : ITranslatable<ID, TR>, ID, TR : ITranslation<ID, *>>(
open val repository: JpaTranslatableRepository<T, ID, TR>
) {
// ...
}
Provides a base service class for business logic operations.
abstract class AbstractTranslationService<T : ITranslation<ID, OWNER>, ID, OWNER>(
open val repository: JpaTranslationRepository<T, ID, OWNER>
) {
// ...
}
Add the following dependency to your pom.xml
file:
<dependency>
<groupId>com.mewebstudio</groupId>
<artifactId>spring-boot-jpa-translatable-kotlin</artifactId>
<version>0.1.1</version>
</dependency>
Add the following dependency to your build.gradle
file:
implementation 'com.mewebstudio:spring-boot-jpa-translatable-kotlin:0.1.1'
You can extend these interfaces and abstract class to implement your own translatable entities and services:
@Entity
@Table(name = "categories")
class Category(
@Id
val id: Long,
@OneToMany(mappedBy = "owner", cascade = [CascadeType.ALL], orphanRemoval = true, fetch = FetchType.LAZY)
@OrderBy("locale ASC")
override var translations: MutableList<CategoryTranslation> = mutableListOf(),
) : ITranslatable<String, CategoryTranslation> {
override fun toString(): String = "${this::class.simpleName}(id = $id)"
}
@Entity
class CategoryTranslation(
@Id
val id: Long,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id", nullable = false)
@OnDelete(action = OnDeleteAction.CASCADE)
override val owner: Category,
@Column(name = "locale", nullable = false)
override val locale: String,
@Column(name = "name", nullable = false, length = 255)
var name: String,
@Column(name = "description", columnDefinition = "text")
var description: String? = null,
) : ITranslation<String, Category> {
override fun toString(): String =
"${this::class.simpleName}(id = $id, name = $name, locale = $locale, owner = $owner)"
}
interface CategoryRepository : JpaTranslatableRepository<Category, String, CategoryTranslation>
interface CategoryTranslationRepository : JpaTranslationRepository<CategoryTranslation, String, Category>
@Service
class CategoryService(
private val categoryRepository: CategoryRepository,
private val categoryTranslationRepository: CategoryTranslationRepository
) : AbstractTranslatableService<Category, String, CategoryTranslation>(categoryRepository) {
private val log: Logger by logger()
init {
log.debug("CategoryService initialized with repository: {}", repository)
requireNotNull(repository) { "CategoryRepository cannot be null" }
}
// Custom business logic methods can be added here...
}
- Java 17+
- Kotlin 1.9.23+
- Spring Boot 3.x
- Spring Data JPA
Spring Boot JPA Translatable (Java Maven Package)
Spring Boot JPA Translatable - Kotlin Implementation
Spring Boot JPA Translatable - Java Implementation
MIT Β© mewebstudio