ORM Framework для реактивных приложений

Я искал Spring-data-r2dbc для реактивного доступа к БД. Но, похоже, он не обеспечивает поддержку ORM, как указано на странице проекта, это не платформа ORM. Какие варианты существуют или планируются для поддержки ORM в реактивных приложениях? На сегодняшний день, каков наилучший способ указать имя столбца для поля (@Column в JPA), использовать иерархии классов (@MappedSuperclass в JPA) и, что самое важное, объединить при использовании spring-data-r2dbc?

1 ответ

Вы правы, что spring-data-r2dbc не является ORM. Он не отображает отношения в вашей базе данных. Что он может сделать, так это сопоставить строки вашей базы данных с объектами. Следующий образец фрагмента сопоставляется со строкой базы данных ниже:

Пример класса в Котлине:

@Table("song")
class SongRow(
    @Id val id: Long, 
    val name: String, 
    val artist: Long
)

Строка:

create table song(
    id integer identity primary key,
    artist integer references artist(id),
    name varchar(100) NOT NULL,
);

Чтобы автоматически сопоставить столбцы с вашей моделью, см. https://docs.spring.io/spring-data/r2dbc/docs/1.0.0.M1/reference/html/.

В ссылке выше также указано, что вы можете использовать @Column.

Что касается одного из ваших вопросов:

Какие варианты существуют или планируются для поддержки ORM в реактивных приложениях? Я понятия не имею

Что касается:

включается при использовании spring-data-r2dbc

С помощью аннотации @Query вы можете выполнять объединения. Если вы хотите отобразить данные, полученные с помощью объединений, лучше использовать DatabseClient (см. https://docs.spring.io/spring-data/r2dbc/docs/1.0.0.M1/reference/html/)

Spring Data R2dbc можно рассматривать как облегченную структуру ORM.

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

@Data
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(value = "posts")
public class Post {

    @Id
    @Column("id")
    private UUID id;

    @Column("title")
    private String title;

    @Column("content")
    private String content;

    @JsonSerialize(using = PgJsonObjectSerializer.class)
    @JsonDeserialize(using = PgJsonObjectDeserializer.class)
    @Column("metadata")
    private Json metadata;

    @Column("status")
    private Status status;

    @Column("created_at")
    @CreatedDate
    private LocalDateTime createdAt;

    @Column("updated_at")
    @LastModifiedDate
    private LocalDateTime updatedAt;

    @Column("version")
    @Version
    private Long version;

    enum Status {
        DRAFT, PENDING_MODERATION, PUBLISHED;
    }

}

В @Column и @Table взяты из проекта spring-data-relational, который используется совместно с spring-data-r2dbc и spring-data-jdbc.

В Id из Spring-data-commons.

Spring Data R2dbc предоставляет R2dbcEntityTemplate(аналогично JdbcTemplate) и R2dbcRepository(специфичный Repositoryдля R2dbc). И Spring 5.3, переработанный DatabaseClient предоставлен.

Посмотрите мой обновленный пример для R2dbc в Spring 5.3 и Spring Data R2dbc.

Но, к сожалению, в настоящее время Spring data r2dbc не поддерживает наследование и защиту.

Если вы придерживаетесь JPA/Hibernate, проверьте Hibernate Rx, реактивную реализацию Hibernate.

Может быть ЖХ-пружинные-данных r2dbc может сделать работу, но вы не найдете все функции JPA. Это может помочь в ожидании выпуска Hibernate Reactive и интеграции в Spring Data.

Например, вы можете объявить такие ссылки:

@Table
public class TableWithForeignKey {
  ...
  @ForeignKey(optional = false)
  private LinkedTable myLink;
  ...
}

@Table
public class LinkedTable {
  ...
  @ForeignTable(joinkey = "myLink")
  private List<TableWithForeignKey> links;
  ...
}

Если вы хотите, чтобы ссылки были сопоставлены, вы можете использовать либо отложенную загрузку, либо выбрать с объединением.

Если вы используете методы findBy...(findById, findAll...) репозитория Spring будет загружена только таблица репозитория. В этом случае можно использовать отложенную загрузку. Для этого вам нужно объявить методы с телом по умолчанию, и ваш метод будет автоматически реализован:

  public Flux<TableWithForeignKey> lazyGetLinks() {
    return null; // will be implemented
  }

Другой способ - делать соединения прямо в запросе. В настоящее время нет поддержки автоматического объединения в репозиторий (например, @EntitiGraph в JPA), но вы можете реализовать свои методы следующим образом:

public interface MyRepository extends LcR2dbcRepository<LinkedTable, Long> {
  
  default Flux<LinkedTable> findAllAndJoin() {
    SelectQuery.from(LinkedTable.class, "root") // SELECT FROM LinkedTable AS root
      .join("root", "links", "link")            // JOIN root.links AS link
      .execute(getLcClient());                  // execute the select and map entities
  }

}

Результатом будут все экземпляры LinkedTable со списком ссылок, загруженным вместе из базы данных.

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