Implementing MongoDB i18n with Spring Data

て烟熏妆下的殇ゞ 提交于 2021-02-11 17:44:47

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!