I'm trying to map a model TradeValidationData to an entity TradeValidatioEntity using map() operation is the stream.
Here are the models:
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class TradeValidationData {
@JsonProperty("orderId")
public Integer orderId;
@JsonProperty("tradeValidations")
public List<TradeValidation> tradeValidations;
}
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class TradeValidation {
@JsonProperty("clientReferenceId")
public String clientReferenceId;
@JsonProperty("tradeValidationMessages")
public List<TradeValidationMessage> tradeValidationMessages;
}
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class TradeValidationMessage {
@JsonProperty("validationCode")
public Integer validationCode;
@JsonProperty("validationMessage")
public String validationMessage;
}
Here is the entity:
public class TradeValidationEntity implements Serializable {
private static final long serialVersionId = 1L;
@EmbeddedId
private TradeValidationCompositeKey tradeValidationCompositeKey;
@Column(name = "validation_message")
private String validationMessage;
}
public class TradeValidationCompositeKey implements Serializable {
private static final long serialVersionId = 1L;
@Column(name = "order_id")
private Integer orderId;
@Column(name = "client_reference_id")
private String clientReferenceId;
@Column(name = "function_type_id")
private Integer functionTypeId;
@Column(name = "validation_id")
private Integer validationId;
}
I have a model object TradeValidationData which in turn has a List<TradeValidation>. I need to transform each model TradeValidation into an entity TradeValidationEntity.
Each TradeValidation contains a list of messages List<TradeValidationMessage>.
In order to initialize TradeValidationEntity I need to create a composite key TradeValidationCompositeKey, which requires data from an instance of TradeValidation and a message.
Here's my previous code which makes use of the Stream.forEach() which is working fine. I hope it would help to convey my intention.
var tradeValidationEntities = new ArrayList<TradeValidationEntity>();
tradeValidationData.tradeValidations.stream().forEach(tradeValidation -> {
tradeValidation.tradeValidationMessages.stream().forEach(tradeValidationMessage -> {
var tradeValidationCompositeKey = new TradeValidationCompositeKey(orderId,
tradeValidation.clientReferenceId, functionTypeId, tradeValidationMessage.getValidationCode());
TradeValidationEntity tradeValidationEntity = new TradeValidationEntity();
tradeValidationEntity.setTradeValidationCompositeKey(tradeValidationCompositeKey);
tradeValidationEntity.setValidationMessage(tradeValidationMessage.validationMessage);
tradeValidationEntities.add(tradeValidationEntity);
});
});
And here's the current problematic attempt to map TradeValidationData to TradeValidatioEntity:
List<TradeValidationEntity> tradeValidationEntities = tradeValidationData.tradeValidations.stream()
.map(tradeValidation -> {
var tradeValidationEntity = new TradeValidationEntity();
tradeValidation.tradeValidationMessages.stream()
.map(tradeValidationMessage -> {
var tradeValidationCompositeKey = new TradeValidationCompositeKey();
tradeValidationCompositeKey.setOrderId(orderId);
tradeValidationCompositeKey.setClientReferenceId(tradeValidation.clientReferenceId);
tradeValidationCompositeKey.setFunctionTypeId(functionTypeId);
tradeValidationCompositeKey.setValidationId(tradeValidationMessage.validationId);
tradeValidationEntity.setTradeValidationCompositeKey(tradeValidationCompositeKey);
tradeValidationEntity.setValidationMessage(tradeValidationMessage.getValidationMessage());
return tradeValidationEntity;
});
return tradeValidationEntity;
})
.collect(Collectors.toList());
I think the issue is with the second return statement I have for tradeValidationEntity. But I'm not sure what to return instead.
CodePudding user response:
You need to transform each TradeValidation object in the stream into multiple TradeValidationEntity instances.
map() operation is not the right tool for that purpose, it's meant for one-to-one transformation. Its function take one object and produces only one object.
To perform one-to-many transformation, you can use flatMap() operation. It expects a function, which take an element and produces a stream of elements.
That's how it might be implemented.
List<TradeValidationEntity> tradeValidationEntities = tradeValidationData.tradeValidations.stream()
.flatMap(tradeValidation -> tradeValidation.tradeValidationMessages.stream()
.map(tradeValidationMessage -> {
var tradeValidationCompositeKey = new TradeValidationCompositeKey(orderId,
tradeValidation.clientReferenceId, functionTypeId, tradeValidationMessage.getValidationCode());
TradeValidationEntity tradeValidationEntity = new TradeValidationEntity();
tradeValidationEntity.setTradeValidationCompositeKey(tradeValidationCompositeKey);
tradeValidationEntity.setValidationMessage(tradeValidationMessage.validationMessage);
return tradeValidationEntity;
})
)
.toList(); // for Java 16 or collect(Collectors.toList())
Note:
map()- is an intermediate operation, it doesn't produce a result, it spawns another stream.- A stream which doesn't ends with a terminal operation will not be executed (unless it's consumed by another stream via some operations like
concat(), orflatMap()). - Multiline lambda expressions are difficult to read. Readability and conciseness is a main weapon of streams. I would advise considering extracting heavy logic from a function into a separate method with a self-explanatory name and replacing the multiline lambda with method reference.
