Как применить аннотации компонента DynamoDB к объектам POJO другого проекта с помощью дженериков?

Я пытаюсь настроить в своем приложении микросхему для устранения дублирования кода (принцип «СУХОЙ»). В частности, мне нужно использовать аннотации из моего проекта в сочетании с несколькими классами POJO, которые находятся в другом проекте (это ниже) и которые не разделяют мои зависимости.

Итак, я начал с этого:

      public abstract class DynamoRepo<T> {

    @DynamoDbBean
    public static class Entity<T> {
        private Number id;
        private T bean;
    
        @DynamoDbPartitionKey
        @DynamoDbConvertedBy(IdConverter.class)
        public Number getId() {
            return id;
        }
    
        public void setId(Number id) {
            this.id = id;
        }
    
        @DynamoDbFlatten
        public T getBean() {
            return bean;
        }
    
        public void setBean(T bean) {
            this.bean = bean;
        }
        
        ...

... аннотации требуются во время выполнения для кода ниже

          private DynamoDbTable<Entity<?>> table;

... с небольшой настройкой в ​​конструкторе ...

          table = enhancedClient.table(tableName, TableSchema.fromBean(new Entity<? extends T>() {}.getClass()));

... Я бы хотел принять <T> что не требует, чтобы звонящий знал о Entity:

      public void putItem(T item) {
    table.putItem(new Entity<T>(item));
}

Увы, этот код не компилируется. Здесь есть несколько проблем, и любые небольшие изменения приводят к другой ошибке компиляции.

Как я могу сделать эту работу, не добавляя @DynamoDb аннотации к базовым POJO (которые не входят в мой проект и не имеют DynamoDB в качестве зависимости) или должны вручную создавать подклассы, которые расширяют каждый из POJO (и выкидывают DRY из окна, создавая целую иерархию параллельных объектов)?

Этот вопрос касается ясности кода. Пожалуйста, не беспокойтесь о накладных расходах.

1 ответ

Недавно я реализовал абстрактный DAO (репозиторий) в стиле Spring Data и реактивным способом с помощью WebFlux для AWS DynamoDB. Не идеально, но отлично работает и, как я предполагаю, подходит для вашего случая:

Первая часть — это абстрактный DAO. Общий интерфейс выглядит так:

      public interface DynamoDbRepository<T> {

  /** Constant prefix for logging purposes */
  String SN = "[DynamoDbRepository]";

  Mono<PutItemEnhancedResponse<T>> insert(final T object);

  Mono<T> getById(final String id);

  Flux<T> getAll();
}

Класс реализации логики, расширяющий DynamoDbRepository :

      @Log4j2
@Repository
public abstract class EntityDynamoDbRepository<T> implements DynamoDbRepository<T> {

  private final Class<T> clazz;
  private DynamoDbAsyncTable<T> dynamoDbAsyncTable;

  @Autowired
  @SneakyThrows
  public final void setAsyncClient(DynamoDbEnhancedAsyncClient asyncClient) {
    DynamoDbTable tableName = AnnotationUtils.getAnnotation(clazz, DynamoDbTable.class);
    this.dynamoDbAsyncTable =
        asyncClient.table(tableName.value(), TableSchema.fromBean(this.clazz));
  }

  @SuppressWarnings("unchecked")
  @SneakyThrows
  public EntityDynamoDbRepository() {
    clazz =
        (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), DynamoDbRepository.class);
    if (clazz == null) {
      throw new Exception("Not possible to resolve generic type");
    }
  }

  @Override
  public Mono<PutItemEnhancedResponse<T>> insert(final T object) {
    final PutItemEnhancedRequest<T> putItemEnhancedRequest =
        PutItemEnhancedRequest.builder(this.clazz)
            .item(object)
            .build();
    return Mono.fromFuture(dynamoDbAsyncTable.putItemWithResponse(putItemEnhancedRequest));
  }

  public Mono<T> getById(final String id) {
    return Mono.fromFuture(dynamoDbAsyncTable.getItem(getKeyBuild(id)));
  }

  public Flux<T> getAll() {
    return Flux.from(dynamoDbAsyncTable.scan().items());
  }

  private Key getKeyBuild(final String id) {
    return Key.builder().partitionValue(id).build();
  }
}

И, наконец, реализовать конкретный репозиторий:

      @Repository
@Log4j2
public class ConcreteRepository extends EntityDynamoDbRepository<ConcreteEntity> {}

Вы могли заметить метод setAsyncClient , который определяет DynamoDbAsyncTable . Из-за того, что @DynamoDbBean использует имя класса сущностей в качестве имени таблицы DynamoDB, я помещаю пользовательскую аннотацию, чтобы назначить там имя пользовательской таблицы:

      @Retention(RetentionPolicy.RUNTIME)
@DynamoDbBean
public @interface DynamoDbTable {
  String value();
}

Отвечая на ваш вопрос: вы можете добавить/вызвать все, что хотите, внутри конструктора EntityDynamoDbRepository , чтобы обернуть ваш <POJO> любой возможной логикой.

ps: я не вставлял bean-компонент DynamoDbEnhancedAsyncClient, но на GitHub есть много примеров

Другие вопросы по тегам