Spring Data Models - абстрактные базовые классы с помощью Lombok? - PullRequest
0 голосов
/ 24 января 2019

У меня есть вопрос о разработке моделей в Spring Data (и в моем конкретном случае Spring Data Mongo).

Мои требования:

  • Я хочу обеспечить, чтобы все агрегированные корнииспользуйте String как тип данных для ID.
  • Я хочу, чтобы идентификаторы, которые являются пустой строкой или пробелом, были маршалированы до нуля (поэтому mongo генерирует ключ для них)
  • Я хочу со временем добавить стандартные атрибуты аудита для каждого агрегатного корня.
  • Я хочу использовать Lombok для моделей, чтобы уменьшить шаблон.

Я видел несколько примеров использования абстрактного базового класса:

@SuperBuilder
public abstract class PersistentDocument implements Serializable {

    private static final long serialVersionUID = 1L;

    private String id;
    public void setId(String id) {
        if (!StringUtils.hasText(getId())) {
            this.id = null;
        } else {
            this.id = id;
        }
    }
    public String getId() {
        this.id;
    }
    .
    .
    auditing fields...etc.
}

Потомок:

@Data
@SuperBuilder
public class UnitOfMeasure extends PersistentDocument {

    private static final long serialVersionUID = 1L;
    private String name;
    private String description;
}

Есть две проблемы:

  • Это зависит от эксперимента "SuperBuilder"
  • Необходимо добавить конструктор вручную, чтобы включитьполя super, которые сводят на нет некоторые преимущества Lombok.

В качестве альтернативы я начал играть с использованием интерфейса для обеспечения соблюдения стандартов типов данных:

public interface PersistentDocument extends Serializable {

    void setId(String id);
    public String getId();

    .
    . Setters/Getters for audit fields
}

Это немного лучше, но каждый документ-потомок находится на крючке, чтобы правильнореализовать логику для преобразования пустого пространства в ноль и аннотировать поля аудита.

Мне действительно интересно посмотреть, что делают другие?

1 Ответ

0 голосов
/ 26 января 2019

Я хотел пойти дальше и ответить на свой вопрос:

Чтобы воспользоваться преимуществами Lombok для обработки стандартного кода, а также обеспечить, чтобы все совокупные корни следовали одному и тому же соглашению для атрибутов ID и аудита:

Я решил использовать интерфейс:

@Document
public interface PersistentDocument extends Serializable {

    void setId(String id);
    String getId();

    @CreatedDate
    @ReadOnlyProperty
    void setCreatedTimestamp(LocalDateTime timestamp);
    LocalDateTime getCreatedTimestamp();

    @LastModifiedDate
    void setLastModifiedTimestamp(LocalDateTime timestamp);
    LocalDateTime getLastModifiedTimestamp();

    @Version
    void setVersion(Long version);
    Long getVersion();
}

Действительно приятная, но тонкая вещь в интерфейсе состоит в том, что аннотации данных пружины могут применяться к интерфейсу, а не к моделям. Это не полностью удаляет аннотации из моделей (вам все равно нужно аннотировать поля модели, например, с помощью индексов).

Объекты модели просто определяют свойства и используют Ломбок:

@Data
@Builder
public class UnitOfMeasure implements PersistentDocument {

    private static final long serialVersionUID = 1L;

    private String id;

    @Indexed(unique=true)
    private String code;
    private String name;
    private String description;

    //Audit Columns
    private LocalDateTime createdTimestamp;
    private LocalDateTime lastModifiedTimestamp;
    private Long version;
}

Чтобы разрешить очистку постоянных идентификаторов пустой строки / пробела:

Мне удалось расширить SimpleMongoRepository с помощью моей собственной реализации:

public class PersistentDocumentMongoRepository<T extends PersistentDocument> extends SimpleMongoRepository <T, String> {

    public PersistentDocumentMongoRepository(MongoEntityInformation<T, String> metadata, MongoOperations mongoOperations) {
        super(metadata, mongoOperations);
    }

    @Override
    public <S extends T> S save(S entity) {
        sanitizeId(entity);
        return super.save(entity);
    }


    @Override
    public <S extends T> List<S> saveAll(Iterable<S> entities) {

        Assert.notNull(entities, "The given Iterable of entities not be null!");
        Streamable.of(entities).stream().forEach(this::sanitizeId);
        return super.saveAll(entities);
    }


    @Override
    public <S extends T> S insert(S entity) {
        sanitizeId(entity);
        return super.insert(entity);
    }

    @Override
    public <S extends T> List<S> insert(Iterable<S> entities) {
        Assert.notNull(entities, "The given Iterable of entities not be null!");
        Streamable.of(entities).stream().forEach(this::sanitizeId);
        return super.insert(entities);
    }

    private void sanitizeId(T entity) {
        if (!StringUtils.hasText(entity.getId())) {
            entity.setId(null);
        }
    }
}

И, наконец, говорю Spring Data использовать реализацию пользовательского репозитория с:

@EnableMongoAuditing
@EnableMongoRepositories(repositoryBaseClass=PersistentDocumentMongoRepository.class)

Надеюсь, это поможет другим, и мне бы очень хотелось посмотреть, как другие решают подобные проблемы.

...