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.