{"id":28046437,"url":"https://github.com/mewebstudio/spring-boot-jpa-translatable","last_synced_at":"2026-05-06T00:33:11.842Z","repository":{"id":292053532,"uuid":"979668161","full_name":"mewebstudio/spring-boot-jpa-translatable","owner":"mewebstudio","description":"Spring Boot JPA Translatable - Maven package","archived":false,"fork":false,"pushed_at":"2025-05-07T22:51:32.000Z","size":14,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-07T23:27:04.989Z","etag":null,"topics":["hibernate","java","jpa","maven","spring-boot","translatable"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mewebstudio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-07T22:03:45.000Z","updated_at":"2025-05-07T22:51:35.000Z","dependencies_parsed_at":"2025-05-07T23:27:28.309Z","dependency_job_id":"f39f278b-cd46-4d54-be8a-ca16a6a04801","html_url":"https://github.com/mewebstudio/spring-boot-jpa-translatable","commit_stats":null,"previous_names":["mewebstudio/spring-boot-jpa-translatable"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mewebstudio%2Fspring-boot-jpa-translatable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mewebstudio%2Fspring-boot-jpa-translatable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mewebstudio%2Fspring-boot-jpa-translatable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mewebstudio%2Fspring-boot-jpa-translatable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mewebstudio","download_url":"https://codeload.github.com/mewebstudio/spring-boot-jpa-translatable/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253620104,"owners_count":21937308,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hibernate","java","jpa","maven","spring-boot","translatable"],"created_at":"2025-05-11T19:27:27.538Z","updated_at":"2025-11-04T11:02:39.622Z","avatar_url":"https://github.com/mewebstudio.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Translatable for Spring Boot JPA\n\n[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![Maven badge](https://maven-badges.herokuapp.com/maven-central/com.mewebstudio/spring-boot-jpa-translatable/badge.svg?style=flat)](https://central.sonatype.com/artifact/com.mewebstudio/spring-boot-jpa-translatable)\n[![javadoc](https://javadoc.io/badge2/com.mewebstudio/spring-boot-jpa-translatable/javadoc.svg)](https://javadoc.io/doc/com.mewebstudio/spring-boot-jpa-translatable)\n\nThis module provides an abstract and reusable foundation for supporting **translatable (multi-language) entities** using Spring Data JPA.  \nIt defines core interfaces, abstract repositories, and a base service class to handle translations with locale-specific logic.\n\n---\n\n## 📦 Package Structure\n\n```\ncom.mewebstudio.springboot.jpa.translatable\n├── ITranslatable.java\n├── ITranslation.java\n├── JpaTranslatableRepository.java\n├── JpaTranslationRepository.java\n└── AbstractTranslatableService.java\n```\n\n---\n\n## 🧩 Interfaces\n\n### `ITranslatable\u003cID, T extends ITranslation\u003cID, ?\u003e\u003e`\n\nRepresents an entity that supports translations.\n\n```java\npublic interface ITranslatable\u003cID, T extends ITranslation\u003cID, ?\u003e\u003e {\n    ID getId();\n    List\u003cT\u003e getTranslations();\n}\n```\n\n---\n\n### `ITranslation\u003cID, T\u003e`\n\nRepresents a translation of an entity in a specific locale.\n\n```java\npublic interface ITranslation\u003cID, T\u003e {\n    ID getId();\n    T getOwner();\n    String getLocale();\n}\n```\n\n---\n\n## 🗃 Repositories\n\n### `JpaTranslatableRepository\u003cT, ID, TR\u003e`\n\nGeneric JPA repository for translatable entities.\n\n```java\n@NoRepositoryBean\npublic interface JpaTranslatableRepository\u003cT extends ITranslatable\u003cID, TR\u003e, ID, TR extends ITranslation\u003cID, ?\u003e\u003e\n        extends JpaRepository\u003cT, ID\u003e {\n\n    @Query(\"SELECT COUNT(e) \u003e 0 FROM #{#entityName} e JOIN e.translations t WHERE e.id = :id AND t.locale = :locale\")\n    boolean existsByIdAndLocale(ID id, String locale);\n\n    @Query(\"SELECT e FROM #{#entityName} e JOIN e.translations t WHERE e.id = :id AND t.locale = :locale\")\n    T findByIdAndLocale(ID id, String locale);\n\n    @Query(\"SELECT DISTINCT e FROM #{#entityName} e JOIN e.translations t WHERE t.locale = :locale\")\n    List\u003cT\u003e findAllByLocale(String locale);\n\n    Page\u003cT\u003e findAllByLocale(String locale, Pageable pageable);\n\n    @Query(\"SELECT t FROM #{#entityName} e JOIN e.translations t WHERE e.id = :id\")\n    List\u003cTR\u003e findTranslationsById(ID id);\n\n    Page\u003cTR\u003e findTranslationsById(ID id, Pageable pageable);\n\n    @Modifying\n    @Query(\"DELETE FROM #{#entityName} e WHERE EXISTS (SELECT 1 FROM e.translations t WHERE t.locale = :locale)\")\n    int deleteByLocale(String locale);\n\n    @Modifying\n    @Query(\"DELETE FROM #{#entityName} e WHERE e.id = :id AND EXISTS (SELECT 1 FROM e.translations t WHERE t.locale = :locale)\")\n    int deleteByIdAndLocale(ID id, String locale);\n}\n```\n\n---\n\n### `JpaTranslationRepository\u003cT, ID, OWNER\u003e`\n\nGeneric JPA repository for translation entities.\n\n```java\n@NoRepositoryBean\npublic interface JpaTranslationRepository\u003cT extends ITranslation\u003cID, OWNER\u003e, ID, OWNER\u003e\n        extends JpaRepository\u003cT, ID\u003e {\n\n    @Query(\"SELECT CASE WHEN COUNT(t) \u003e 0 THEN true ELSE false END FROM #{#entityName} t WHERE t.locale = :locale\")\n    boolean existsByLocale(String locale);\n\n    @Query(\"SELECT CASE WHEN COUNT(t) \u003e 0 THEN true ELSE false END FROM #{#entityName} t WHERE t.owner.id = :ownerId AND t.locale = :locale\")\n    boolean existsByOwnerIdAndLocale(ID ownerId, String locale);\n\n    @Query(\"SELECT t FROM #{#entityName} t WHERE t.owner.id = :ownerId\")\n    List\u003cT\u003e findByOwnerId(ID ownerId);\n\n    Page\u003cT\u003e findByOwnerId(ID ownerId, Pageable pageable);\n\n    @Query(\"SELECT t FROM #{#entityName} t WHERE t.owner.id = :ownerId AND t.locale = :locale\")\n    T findByOwnerIdAndLocale(ID ownerId, String locale);\n\n    @Modifying\n    @Query(\"DELETE FROM #{#entityName} t WHERE t.locale = :locale\")\n    int deleteByLocale(String locale);\n\n    @Modifying\n    @Query(\"DELETE FROM #{#entityName} t WHERE t.owner.id = :ownerId AND t.locale = :locale\")\n    int deleteByOwnerIdAndLocale(ID ownerId, String locale);\n}\n```\n\n---\n\n## 🧠 Abstract Service\n\n### `AbstractTranslatableService\u003cT, ID, TR\u003e`\n\nProvides a base service class for business logic operations.\n\n```java\npublic abstract class AbstractTranslatableService\u003cT extends ITranslatable\u003cID, TR\u003e, ID, TR extends ITranslation\u003cID, ?\u003e\u003e {\n    protected final JpaTranslatableRepository\u003cT, ID, TR\u003e repository;\n\n    public AbstractTranslatableService(JpaTranslatableRepository\u003cT, ID, TR\u003e repository) {\n        this.repository = repository;\n    }\n\n    public boolean existsByIdAndLocale(ID id, String locale) {\n        return repository.existsByIdAndLocale(id, locale);\n    }\n\n    public T findByIdAndLocale(ID id, String locale) {\n        return repository.findByIdAndLocale(id, locale);\n    }\n\n    public List\u003cT\u003e findAllByLocale(String locale) {\n        return repository.findAllByLocale(locale);\n    }\n\n    public Page\u003cT\u003e findAllByLocale(String locale, Pageable pageable) {\n        return repository.findAllByLocale(locale, pageable);\n    }\n\n    public List\u003cTR\u003e findTranslationsById(ID id) {\n        return repository.findTranslationsById(id);\n    }\n\n    public Page\u003cTR\u003e findTranslationsById(ID id, Pageable pageable) {\n        return repository.findTranslationsById(id, pageable);\n    }\n\n    @Transactional\n    public int deleteByLocale(String locale) {\n        return repository.deleteByLocale(locale);\n    }\n\n    @Transactional\n    public int deleteByIdAndLocale(ID id, String locale) {\n        return repository.deleteByIdAndLocale(id, locale);\n    }\n}\n```\n\n---\n\n## 📥 Installation\n\n#### for maven users\nAdd the following dependency to your `pom.xml` file:\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.mewebstudio\u003c/groupId\u003e\n  \u003cartifactId\u003espring-boot-jpa-translatable\u003c/artifactId\u003e\n  \u003cversion\u003e0.1.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n#### for gradle users\nAdd the following dependency to your `build.gradle` file:\n```groovy\nimplementation 'com.mewebstudio:spring-boot-jpa-translatable:0.1.1'\n```\n\n---\n\n## 📌 Usage\n\nYou can extend these interfaces and abstract class to implement your own translatable entities and services:\n\n### Translatable Entity Example\n```java\n@Entity\npublic class Category implements ITranslatable\u003cLong, CategoryTranslation\u003e {\n    @Id private Long id;\n\n    @OneToMany(mappedBy = \"owner\")\n    private List\u003cCategoryTranslation\u003e translations;\n\n    // getters...\n}\n```\n\n### Translation Entity Example\n```java\n@Entity\npublic class CategoryTranslation implements ITranslation\u003cLong, Category\u003e {\n    @Id private Long id;\n\n    @ManyToOne\n    private Category owner;\n\n    private String locale;\n    private String name;\n\n    // getters...\n}\n```\n\n### Translatable Repository Example\n```java\npublic interface CategoryRepository extends JpaTranslatableRepository\u003cCategory, String, CategoryTranslation\u003e {\n    // Custom query methods can be added here\n}\n```\n\n### Translation Repository Example\n```java\npublic interface CategoryTranslationRepository extends JpaTranslationRepository\u003cCategoryTranslation, String, Category\u003e {\n    // Custom query methods can be added here\n}\n```\n\n### Translatable Service Example\n```java\n@Service\n@Slf4j\npublic class CategoryService extends AbstractTranslatableService\u003cCategory, String, CategoryTranslation\u003e {\n    private final CategoryRepository categoryRepository;\n    private final CategoryTranslationRepository categoryTranslationRepository;\n\n    /**\n     * Constructs a new CategoryService.\n     *\n     * @param categoryRepository            the category repository\n     * @param categoryTranslationRepository the category translation repository\n     */\n    public CategoryService(CategoryRepository categoryRepository, CategoryTranslationRepository categoryTranslationRepository) {\n        super(categoryRepository);\n        this.categoryRepository = categoryRepository;\n        this.categoryTranslationRepository = categoryTranslationRepository;\n\n        log.debug(\"CategoryService initialized with repository: {}\", categoryRepository);\n        Objects.requireNonNull(categoryRepository, \"CategoryRepository cannot be null\");\n    }\n\n    // Custom business logic methods can be added here\n}    \n```\n\n---\n\n## 🛠 Requirements\n\n- Java 17+\n- Spring Boot 3.x\n- Spring Data JPA\n\n---\n\n## 🔁 Other Implementations\n\n[Spring Boot JPA Translatable (Kotlin Maven Package)](https://github.com/mewebstudio/spring-boot-jpa-translatable-kotlin)\n\n## 💡 Example Implementations\n\n[Spring Boot JPA Translatable - Java Implementation](https://github.com/mewebstudio/spring-boot-jpa-translatable-java-impl)\n\n[Spring Boot JPA Translatable - Kotlin Implementation](https://github.com/mewebstudio/spring-boot-jpa-translatable-kotlin-impl)\n\n## 📃 License\n\nMIT © [mewebstudio](https://github.com/mewebstudio)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmewebstudio%2Fspring-boot-jpa-translatable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmewebstudio%2Fspring-boot-jpa-translatable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmewebstudio%2Fspring-boot-jpa-translatable/lists"}