Dopo la creazione di una entity sono necessari i seguenti passaggi:
- Aggiunta configurazione code nell’application.yml
- Creazione resource aggiuntive per la ripubblicazione di alcuni eventi
- Creazione delle entity aggregate
- Creazione scodatori per popolare gli aggregate
- Modifica del puntamento delle resource di lettura dalle entity alle aggregate
Inoltre va configurato nell’application.yml l’exchange nel quale scrivere
cloud: stream: bindings: ... createUpdateBuyer-Output: destination: createUpdateBuyerExchange contentType: application/json deleteBuyer-Output: destination: deleteBuyerExchange contentType: application/json
3. Metodi aggiuntivi nei metodi resource per la ripubblicazione di alcuni eventi
Potrebbe essere necessaro ripubblicare gli eventi di createUpdate in modo che eventuali listner possano ri-aggiornare i propri valori, per far questo si prevede una risorsa REST per fare questo, accessibile solo da admin.
Nella resource della entity aggiungere il seguente metodo:
@GetMapping("/buyers/publish") public ResponseEntity<String> publish(BuyerCriteria criteria, Pageable pageable) { log.debug("REST request to republish by criteria: {} and page: {}", criteria, pageable); // Access Roule UcpPermission.verifyPermission("c6e.admin"); // Execution int republishedQuantity = buyerBusinessService.republishCreateUpdate(criteria, pageable); return new ResponseEntity<String>("" + republishedQuantity, HttpStatus.OK); }
4. Creazione delle entity aggregate
Le entity aggregate sono uno strumento pensato per vari fattori: velocizzare la lettura, ricerche approfondite, orientare le letture verso no-sql.
Per creare gli aggregate è necessario iniziare duplicando ogni entity esistente ma anteponendo il termine Aggregate al nome della entity, ed anteponendo agg_ al nome della tabella. ad esempio:
entity Buyer (a_buyer) { } entity AggregateBuyer (agg_a_buyer) { }
Successivamente per ogni campo dell’entity va anteposto il nome dell’entity (facendo attenzione a rispettare sempre il formato camelBack) ad esempio:
entity Buyer (a_buyer) { eopooCode String required, confirmation Boolean required, statusNote String, } entity AggregateBuyer (agg_a_buyer) { buyerEopooCode String, buyerConfirmation Boolean, buyerStatusNote String, }
Nell’aggregate va inoltre aggiunto un campo iniziale nel quale verrà inserito l’id dell’entity originale, che si chiamerà con nome entity ed in aggiunta ID come ad esempio: buyerID Integer required
Il campo dell’id dell’entity principale sarà l’unico ad essere required!
A questo punto vanno aggiunti tutti i campi delle entity parent sempre anteponendo al nome del campo il nome dell’entity di riferimento, come di seguito:
entity AggregateBuyer (agg_a_buyer) { buyerID Integer required, buyerEopooCode String, buyerConfirmation Boolean, buyerStatusNote String, sellerID Integer, sellerEopooCode String, sellerName String, }
Una vota aggiunte le entity aggregate nel JDL princuipale nel branch entity_creation generare il codice come indicato nell’apposita guida.
Come ultimo passaggio di preparazione degli aggregate vanno creati i mapper, tra entity ed aggregate, queste classi mapper useranno MapStruct e dovranno essere create nel pacchetto aggregate.mapper come nel seguente esempio:
import org.mapstruct.Mapper; import org.mapstruct.Mapping; import com.eneasys.apigest.contexts_apps.commerce.service.dto.AggregateBuyerDTO; import com.eneasys.apigest.contexts_apps.commerce.service.dto.BuyerDTO; @Mapper(componentModel = "spring", uses = {}) public interface BuyerDTOMapper { @Mapping(source = "buyerID", target = "id") @Mapping(source = "buyerConfirmation", target = "confirmation") @Mapping(source = "buyerStatusNote", target = "statusNote") @Mapping(source = "buyerEopooCode", target = "eopooCode") @Mapping(source = "sellerID", target = "sellerId") @Mapping(source = "sellerID", target = "seller.id") @Mapping(source = "sellerName", target = "seller.name") @Mapping(source = "sellerEopooCode", target = "seller.eopooCode") BuyerDTO toDto(AggregateBuyerDTO aggregateBuyerDTO); @Mapping(source = "id", target = "buyerID") @Mapping(source = "confirmation", target = "buyerConfirmation") @Mapping(source = "statusNote", target = "buyerStatusNote") @Mapping(source = "eopooCode", target = "buyerEopooCode") @Mapping(source = "sellerId", target = "sellerID") @Mapping(source = "seller.name", target = "sellerName") @Mapping(source = "seller.eopooCode", target = "sellerEopooCode") AggregateBuyerDTO toAggregate(BuyerDTO buyerDTO); }
5. Creazione scodatori per popolare gli aggregate
Va inoltre configurato nel file application.yml a quali Exchange collegare i listner e come chiamare le code
cloud: stream: bindings: ... createUpdateBuyer-createUpdateBuyerAggregate-Input: destination: createUpdateBuyerExchange group: createUpdateBuyerAggregateQueue contentType: application/json createUpdateSeller-createUpdateBuyerAggregate-Input: destination: createUpdateSellerExchange group: createUpdateBuyerAggregateQueue contentType: application/json deleteBuyer-deleteBuyerAggregate-Input: destination: deleteBuyerExchange group: deleteBuyerAggregateQueue contentType: application/json
La seconda classe da creare per la gestione dell’evento è la seguente:
Questa classe necessita di vari metodi nel layer di entity degli aggregate da implementare nel service come nel seguente esempio:
public interface AggregateBuyerService { ... public Optional<AggregateBuyerDTO> findByBuyerId(Integer buyerId); public void updateBuyer(AggregateBuyerDTO aggregateBuyerDTO); public Optional<List<AggregateBuyerDTO>> findBySellerId(Integer sellerId); public void updateSeller(AggregateBuyerDTO aggregateBuyerDTO); }
public class AggregateBuyerServiceImpl implements AggregateBuyerService { ... @Override @Transactional(readOnly = true) public Optional<AggregateBuyerDTO> findByBuyerId(Integer buyerId) { log.debug("Request to get all AggregateBuyers by buyer: {}", buyerId); return aggregateBuyerRepository.findByBuyerID(buyerId) .map(aggregateBuyerMapper::toDto); } @Override public void updateBuyer(AggregateBuyerDTO aggregateBuyerDTO) { log.debug("Request to updateBuyer in AggregateBuyer : {}", aggregateBuyerDTO); aggregateBuyerRepository.updateBuyer(aggregateBuyerDTO.getBuyerID(), aggregateBuyerDTO.getBuyerEopooCode(), aggregateBuyerDTO.getBuyerStatusNote(), aggregateBuyerDTO.isBuyerConfirmation(), aggregateBuyerDTO.getSellerID(), aggregateBuyerDTO.getSellerEopooCode(), aggregateBuyerDTO.getSellerName()); } @Override @Transactional(readOnly = true) public Optional<List<AggregateBuyerDTO>> findBySellerId(Integer sellerId) { log.debug("Request to get all AggregateBuyers by seller: {}", sellerId); return aggregateBuyerRepository.findBySellerID(sellerId) .map(aggregateBuyerMapper::toDto); } @Override public void updateSeller(AggregateBuyerDTO aggregateBuyerDTO) { log.debug("Request to updateSeller in AggregateBuyer: {}", aggregateBuyerDTO); aggregateBuyerRepository.updateSeller(aggregateBuyerDTO.getSellerID(), aggregateBuyerDTO.getSellerEopooCode(), aggregateBuyerDTO.getSellerName()); } }
A sua volta il layer di entity necessita modifiche nel layer di repository come di seguito:
public interface AggregateBuyerRepository extends JpaRepository<AggregateBuyer, Long>, JpaSpecificationExecutor<AggregateBuyer> { public Optional<AggregateBuyer> findByBuyerID(Integer buyerId); @Modifying @Query("update AggregateBuyer u " + " set u.buyerEopooCode = :buyerEopooCode, " + " u.buyerStatusNote = :buyerStatusNote, " + " u.buyerConfirmation = :buyerConfirmation," + " u.sellerID = :sellerID," + " u.sellerEopooCode = :sellerEopooCode," + " u.sellerName = :sellerName" + " where u.buyerID = :buyerID") public void updateBuyer(@Param(value = "buyerID") Integer buyerID, @Param(value = "buyerEopooCode") String buyerEopooCode, @Param(value = "buyerStatusNote") String buyerStatusNote, @Param(value = "buyerConfirmation") Boolean buyerConfirmation, @Param(value = "sellerID") Integer sellerID, @Param(value = "sellerEopooCode") String sellerEopooCode, @Param(value = "sellerName") String sellerName); public Optional<List<AggregateBuyer>> findBySellerID(Integer sellerId); @Modifying @Query("update AggregateBuyer u " + " set u.sellerEopooCode = :sellerEopooCode, " + " u.sellerName = :sellerName " + " where u.sellerID = :sellerID") public void updateSeller(@Param(value = "sellerID") Integer sellerID, @Param(value = "sellerEopooCode") String sellerEopooCode, @Param(value = "sellerName") String sellerName); }
Una volta fatto questo quando l’appicativo riceve un messaggio di createUpdate o di delete di un entità tale azione verrà eseguita anche sull’aggregate corrispondente.
6. Modifica del puntamento delle resource di lettura dalle entity alle aggregate
Quando i dati delle entity saranno anche inserite sulle ripettive aggregate si potrà far leggere le informazioni dall’aggregate in modo da velocizzare la lettura ed estendere le funzionalità di filtro a molti più campi.
Per poter fare questo bisognerà cambiare il puntamento nel layer di resource REST in modo da far leggere dagli aggregate e non dalle entity, lo si fa come di seguito in esempio:
public class BuyerResource { ... @GetMapping("/buyers") public ResponseEntity<List<BuyerDTO>> getAllBuyers(AggregateBuyerCriteria criteria, Pageable pageable) { log.debug("REST request to get Buyers by criteria: {}", criteria); // Access Roule UcpPermission.verifyPermission("c6e.admin"); // Execution Page<AggregateBuyerDTO> pageAggregateBuyerDTO = aggregateBuyerQueryService.findByCriteria(criteria, pageable); // Trasformation Roule Page<BuyerDTO> pageBuyerDTO = pageAggregateBuyerDTO.map(buyerDTOMapper::toDto); // Return HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), pageBuyerDTO); return ResponseEntity.ok().headers(headers).body(pageBuyerDTO.getContent()); } ... @GetMapping("/buyers/count") public ResponseEntity<Long> countBuyers(AggregateBuyerCriteria criteria) { log.debug("REST request to count Buyers by criteria: {}", criteria); // Access Roule UcpPermission.verifyPermission("c6e.admin"); // Execution return ResponseEntity.ok().body(aggregateBuyerQueryService.countByCriteria(criteria)); } ... @GetMapping("/buyers/{id}") public ResponseEntity<BuyerDTO> getBuyer(@PathVariable Long id) { log.debug("REST request to get Buyer: {}", id); // Access Roule UcpPermission.verifyPermission("c6e.admin"); // Execution Optional<BuyerDTO> optionalBuyerDTO = aggregateBuyerService.findByBuyerId(new Integer(id.intValue())).map(buyerDTOMapper::toDto); // Return return ResponseUtil.wrapOrNotFound(optionalBuyerDTO); } ... }