问题
I'm looking for an elegant solution for persisting localized data in MongoDB in my Spring application.
In the example below there is a basic solution for persisting localized data for the field description
.
As already suggested in how-to-do-i18n-with-mongodb the schema for localized data is
"description": [{
"locale": "es",
"value": "someESvalue"
}, {
"locale": "en",
"value": "someENvalue"
}]
Given that, the entity looks like this:
@Document(collection = "foo")
public class Foo implements Serializable {
@Id
private String id;
@Field("description")
private List<LocalizedValue> description;
// Getters and Setters are omitted in all classes
LocalizedValue holds both locale and the value
public class LocalizedValue {
private Locale locale;
private String value;
And the DTO looks like this:
public class FooDTO {
private String id;
private String description;
In the service save
and the update (or customUpdate
) are treated differently
@Service
public class FooServiceImpl implements FooService {
public FooDTO save(FooDTO dto) {
// String is converted to List<LocalizedValue> in this step
Foo foo = entityMapper.toEntity(dto);
if (StringUtils.isBlank(foo.getId())) {
// save and update must be treated differently
foo = fooRepository.save(foo);
} else {
foo = fooRepository.customUpdate(foo);
}
Please note, that the mapper handles the conversion from String description
field in the DTO to the List<LocalizedValue> description
in the entity.
At this point, the update or insertion of the LocalizedValue
in the description
array becomes an issue. There is a custom repository method for that.
public class FooRepositoryImpl implements FooRepositoryCustom {
private final MongoTemplate mongoTemplate;
public FooRepositoryImpl(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
public Foo customUpdate(Foo foo) {
Query query = new Query();
query.addCriteria(Criteria.where("id").is(foo.getId()).andOperator(
Criteria.where("description.locale").is(foo.getDescription().get(0).getLocale().toString())));
List<Foo> foos = mongoTemplate.find(query, Foo.class);
Update update = new Update();
UpdateResult writeResult;
// insert a new LocalizedValue or update an existing one
if (CollectionUtils.isEmpty(foos)) {
query = new Query();
query.addCriteria(Criteria.where("id").is(foo.getId()));
update.addToSet("description", foo.getDescription().get(0));
writeResult = mongoTemplate.upsert(query, update, Foo.class);
} else {
update.set("description.$.value", foo.getDescription().get(0).getValue());
writeResult = mongoTemplate.updateFirst(query, update, Foo.class);
}
// Here you may need to update manually any other fields Foo may have !
This example kind of works, but in my opinion it's a rather ugly solution. You still need to create a custom repository for every one of your entities and write (and execute) couple of queries. Even if you're able to further optimize the queries (maybe using the Aggregation Framework) there is still a lot of unnecesary steps involved for every field of every entity you may want to localize.
The main issue is the management of this array:
"description": [{
"locale": "es",
"value": "someESvalue"
}, {
"locale": "en",
"value": "someENvalue"
}]
If locale
does not exist you must insert the new element, if locale
exists, you should update its value
.
I'm thinking about extending MongoRepository
and treating these operations in a generic way.
What are your thoughts about i18n with MongoDB? Do you know a better solution?
Thanks in advance.
来源:https://stackoverflow.com/questions/60956356/implementing-mongodb-i18n-with-spring-data