Design and Implementation of a Lightweight Data Translation Framework for Java Backend Applications
This article introduces a lightweight, extensible Java framework that uses custom annotations to translate relational data, dictionaries, collections, and nested objects, detailing its background, architecture, core annotation design, usage examples, advanced features, Spring Boot integration, ORM support, and provides the open‑source repository link.
1. Background
In many backend projects, developers often need to translate data—e.g., returning related query results to the frontend. Typical ORM code looks like this:
// query table A
List<A> alist = aDao.findAll(query);
// extract B ids from A
List<Long> bids = alist.stream().map(a::bid).toList();
// query table B and map to id
Map<Long,B> bMap = bDao.findAll(bids).stream().toMap(b::id, x->x);
// assemble data
...Such repetitive logic motivates the creation of a dedicated translation framework.
The existing easy_trans framework solves the problem but has drawbacks: it is heavy (introduces Redis cache), requires entities to implement its interfaces, and generates translated fields in a map that is not convenient for Swagger documentation.
Therefore, a custom lightweight framework is developed.
2. Architecture Design
The design follows the idea of Spring Validation: custom annotations drive the translation process.
Core annotation definition:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface Trans {
/** field to translate */
String trans() default "";
/** key field to extract */
String key() default "";
/** repository that provides translation data */
Class
using();
}3. Advantages
Core source code is only a few hundred lines with no external dependencies.
Highly extensible; extending logic only requires implementing TransRepository .
Supports database translation, dictionary translation, collection translation, nested translation, etc.
Parallel translation of different fields for high performance.
4. Basic Usage
Add the Maven dependency:
<dependency>
<groupId>io.github.orangewest</groupId>
<artifactId>easy-trans-core</artifactId>
<version>0.0.3</version>
</dependency>Define DTOs (Teacher, Subject, User) and annotate fields that need translation with @Trans or @DictTrans :
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TeacherDto {
private Long id;
private String name;
private Long subjectId;
} @Data
@AllArgsConstructor
@NoArgsConstructor
public class SubjectDto {
private Long id;
private String name;
} @Data
public class UserDto {
private Long id;
private String name;
private String sex;
@DictTrans(trans = "sex", group = "sexDict")
private String sexName;
private String job;
@DictTrans(trans = "job", group = "jobDict")
private String jobName;
private Long teacherId;
@Trans(trans = "teacherId", key = "name", using = TeacherTransRepository.class)
private String teacherName;
@Trans(trans = "teacherId", key = "subjectId", using = TeacherTransRepository.class)
private Long subjectId;
@Trans(using = SubjectTransRepository.class, trans = "subjectId", key = "name")
private String subjectName;
// constructor omitted for brevity
}Implement the translation repositories:
public class TeacherTransRepository implements TransRepository {
@Override
public Map
getTransValueMap(List
transValues, Annotation transAnno) {
return getTeachers().stream()
.filter(t -> transValues.contains(t.getId()))
.collect(Collectors.toMap(TeacherDto::getId, t -> t));
}
public List
getTeachers() {
List
list = new ArrayList<>();
list.add(new TeacherDto(1L, "老师1", 1L));
list.add(new TeacherDto(2L, "老师2", 2L));
list.add(new TeacherDto(3L, "老师3", 3L));
list.add(new TeacherDto(4L, "老师4", 4L));
return list;
}
} public class SubjectTransRepository implements TransRepository {
@Override
public Map
getTransValueMap(List
transValues, Annotation transAnno) {
return getSubjects().stream()
.filter(s -> transValues.contains(s.getId()))
.collect(Collectors.toMap(SubjectDto::getId, s -> s));
}
public List
getSubjects() {
List
list = new ArrayList<>();
list.add(new SubjectDto(1L, "语文"));
list.add(new SubjectDto(2L, "数学"));
list.add(new SubjectDto(3L, "英语"));
list.add(new SubjectDto(4L, "物理"));
return list;
}
}Register repositories:
TransRepositoryFactory.register(new TeacherTransRepository());
TransRepositoryFactory.register(new SubjectTransRepository());
TransRepositoryFactory.register(new DictTransRepository(new DictLoader() {
@Override
public Map
loadDict(String dictGroup) {
return dictMap().getOrDefault(dictGroup, new HashMap<>());
}
private Map
> dictMap() {
Map
> map = new HashMap<>();
map.put("sexDict", new HashMap<>());
map.put("jobDict", new HashMap<>());
map.get("sexDict").put("1", "男");
map.get("sexDict").put("2", "女");
map.get("jobDict").put("1", "学习委员");
map.get("jobDict").put("2", "生活委员");
map.get("jobDict").put("3", "宣传委员");
map.get("jobDict").put("4", "班长");
map.get("jobDict").put("5", "团支书");
map.get("jobDict").put("6", "团长");
return map;
}
}));Test the translation service with single objects and collections; the console output shows fields like sexName , jobName , teacherName , and subjectName populated correctly.
5. Advanced Features
5.1 Custom Annotations
By annotating a custom annotation with @Trans , you can reuse translation logic. Example:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Trans(using = TeacherTransRepository.class)
public @interface TeacherTrans {
String trans() default "";
String key() default "";
}5.2 Value Extraction
Dictionary translation can use #val as the key to match the original field value.
5.3 Wrapper Class Translation
For wrapper objects like Result<T> , implement TransObjResolver to extract the actual data object for translation.
public class ResultResolver implements TransObjResolver {
@Override
public boolean support(Object obj) {
return obj instanceof Result;
}
@Override
public Object resolveTransObj(Object obj) {
return ((Result
) obj).getData();
}
}Register the resolver with TransObjResolverFactory.register(new ResultResolver()); .
5.4 Spring Boot Integration
Add the starter dependency:
<dependency>
<groupId>io.github.orangewest</groupId>
<artifactId>easy-trans-spring-start</artifactId>
<version>0.0.3</version>
</dependency>Mark translation repositories with @Component and use @AutoTrans on controller methods to trigger automatic translation.
5.5 ORM Integration (MyBatis‑Plus example)
Define a @DbTrans annotation that specifies the target entity class, implement DbTransRepository using a TransDriver that queries the database by IDs, and plug it into the framework.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Trans(using = DbTransRepository.class)
public @interface DbTrans {
String trans();
String key() default "";
Class
entity() default BaseEntity.class;
}The driver uses MyBatis SqlSession to fetch entities in batch.
6. Open‑Source Repository
https://gitee.com/orangewest/easy-trans
Author: 一只小闪闪 (Juejin post: https://juejin.cn/post/7361468008529412148 )
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.