Как смоделировать отношения «многие ко многим» в модели программирования ABAP для Fiori? - PullRequest
0 голосов
/ 20 декабря 2018

Как добиться отношений «многие ко многим», используя CDS / BOPF и ассоциации?

Например, рассмотрим, есть ли у вас таблицы для фильмов и жанров, в которых мы хотим назначать жанры для фильмов.

define table zmv_movies {
  key client   : abap.clnt not null;
  key id       : snwd_node_key not null;
  name         : abap.string(0);
  release_date : abap.dats;
  plot         : abap.string(0);

}

define table zmv_genres {
  key client : abap.clnt not null;
  key id     : snwd_node_key not null;
  name       : abap.string(0);

}

Хороший способ отобразить отношения многие ко многим (n: m) - это соединительные таблицы, поэтому я создал:

define table zmv_moviesgenres {
  @AbapCatalog.foreignKey.screenCheck : false
  key movie_id           : snwd_node_key not null
    with foreign key [0..*,1] zmv_movies
      where client = syst.mandt
        and id = zmv_moviesgenres.movie_id;
  @AbapCatalog.foreignKey.screenCheck : false
  key genre_id           : snwd_node_key not null
    with foreign key [0..*,1] zmv_genres
      where client = syst.mandt
        and id = zmv_moviesgenres.genre_id;
}

Как создать транзакционные представления, которые правильно обрабатывают это?

Мои попытки до сих пор

Я создал рабочий пример, но он не работает по согласованности данных.

Я изменил zmv_moviesgenres, чтобы иметь собственный первичный ключ:

define table zmv_moviesgenres {
  key movie_genre_id : snwd_node_key not null;
  @AbapCatalog.foreignKey.screenCheck : false
  movie_id           : snwd_node_key not null
    with foreign key [0..*,1] zmv_movies
      where client = syst.mandt
        and id = zmv_moviesgenres.movie_id;
  @AbapCatalog.foreignKey.screenCheck : false
  genre_id           : snwd_node_key not null
    with foreign key [0..*,1] zmv_genres
      where client = syst.mandt
        and id = zmv_moviesgenres.genre_id;

}

Затем я создал несколько базовых представлений, а затем транзакционных представлений:

MOVIES_TP:

@AbapCatalog.sqlViewName: 'ZMVIMOVIESTP'
//@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Movies transactional view'

@VDM.viewType: #TRANSACTIONAL
@ObjectModel: {
  modelCategory: #BUSINESS_OBJECT,
  createEnabled: true,
  updateEnabled: true,
  deleteEnabled: true,

  compositionRoot: true,
  transactionalProcessingEnabled: true,
  writeActivePersistence: 'ZMV_MOVIES',

  draftEnabled: true,
  writeDraftPersistence: 'ZMVIDMOVIESTP',

  semanticKey: 'id'
}

define view ZMVI_Movies_TP
  as select from ZMVI_Movies
  association [0..*] to ZMVI_Movies_Genres_TP as _movies_genres on $projection.id = _movies_genres.movie_id
{
      @ObjectModel.readOnly: true
      key id,

      @ObjectModel.association.type: [ #TO_COMPOSITION_CHILD ]
      _movies_genres,
      name,
      release_date,
      plot
}

MOVIES_GENRES_TP:

@AbapCatalog.sqlViewName: 'ZMVIMOVGENTP'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Movies Genres Transactional View'

@VDM.viewType: #TRANSACTIONAL

@ObjectModel: {
  createEnabled: true,
  updateEnabled: true,
  deleteEnabled: true,


  writeDraftPersistence: 'ZMVDMOVIESGENRES',
  writeActivePersistence: 'ZMV_MOVIESGENRES', 

  semanticKey: 'movie_genre_id',
  alternativeKey: [{id: 'movie_id'}]
} 

define view ZMVI_Movies_Genres_TP as select from ZMVI_Movies_Genres
    association [1..1] to ZMVI_Movies_TP as _movies on $projection.movie_id = _movies.id
    association [1..1] to ZMVI_GENRES_TP as _genres on $projection.genre_id = _genres.id
{

    key movie_genre_id,
    genre_id,
    movie_id,

    @ObjectModel.association.type: [ #TO_COMPOSITION_PARENT, #TO_COMPOSITION_ROOT ]
    _movies,
    _genres
}

GENRES_TP:

@AbapCatalog.sqlViewName: 'ZMVIGENRESTP'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Genres transactional view'
@VDM.viewType: #TRANSACTIONAL
@ObjectModel: {
  modelCategory: #BUSINESS_OBJECT, 
  createEnabled: true,
  updateEnabled: true,
  deleteEnabled: true,

  compositionRoot: true,
  transactionalProcessingEnabled: true,
  writeActivePersistence: 'ZMV_GENRES',

  semanticKey: 'id',

  alternativeKey: [{element: ['_movies_genres']}]

}

define view ZMVI_GENRES_TP as select from ZMVI_Genres
  association [0..*] to ZMVI_Movies_Genres_TP as _movies_genres on $projection.id = _movies_genres.genre_id {
  @ObjectModel.readOnly: true
  key id,

  _movies_genres,
  name
}

Это работает.Он связывает фильмы с жанрами.Но если я удаляю жанр, он не проверяет, используется ли жанр в фильме.Я должен был создать определение / подтверждение для этого, чтобы проверить вручную.

Кроме того, он позволяет повторные назначения (например, две строки ["Звездные войны | Научно-фантастические", "Звездные войны | Научно-фантастические"]])

Я использую систему S / 4 1809, компоненты SAP_ABA 75D, SAP_BASIS 753.

Есть ли другой способ сделать это?

...