I am developing a Jakarta EE Batch using EclipseLink and Wildfly. I am stuck at the ItemWriter level. The Writer basically creates a file and should write some records into it. Each file is associated with a Delivery which is my entity. So one file per delivery. The delivery numbers should increase ascendingly.
Here is the relevant part of the entity that im using:
@Entity
@Table(name = "DELIVERY")
public class Delivery {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "lieferung_id_generator")
@SequenceGenerator(name = "lieferung_id_generator", sequenceName = "lieferung_id_seq", allocationSize = 1)
@Column(name = "DELIVERY_NO", nullable = false)
private Long deliveryNo;
@Column(name = "DELIVERY_DATE", nullable = false)
private LocalDate deliveryDate;
@Column(name = "FILENAME", nullable = false)
private String deliveryFile;
Here is the writer:
@Named
@Dependent
public class MyItemWriter extends TypedItemWriter<List<String>, Serializable> {
@PersistenceContext(unitName = MyPersistenceUnit.NAME)
private EntityManager entityManager;
private FileWriter fileWriter;
private Delivery newDelivery;
private Integer writtenRecords;
@Override
protected void doOpen(Serializable checkpoint) {
if (checkpoint == null) {
File deliveryFile = new File(erzeugeDateiName());
newDelivery = persistNewLieferung(dateiZurLieferung);
writtenRecords = 0;
try {
boolean isFileCreated = deliveryFile.createNewFile();
if (!isFileCreated) {
throw new BatchRuntimeException("File already exists.");
}
fileWriter = new FileWriter(deliveryFile);
} catch (IOException e) {
throw new BatchRuntimeException("Error upon file creation: {}", e);
}
// Initial Filewriting without Records from `ItemProcessor`
// [...]
} else {
writtenRecords = (Integer)checkpoint;
}
}
@Override
protected void doWriteItems(List<List<String>> items) {
for (List<String> records: items) {
records.forEach(this::writeToFile);
}
}
@Override
protected Serializable doCheckpointInfo() {
return writtenRecords;
}
@Override
public void close() throws IOException {
Delivery previousDelivery = getPreviousDelivery();
writeToFile(createRecord(newDelivery.getDeliveryNo().toString(),
writtenRecords,
previousDelivery.getDeliveryDate(),
previousDelivery.getDeliveryNo().toString()));
fileWriter.close();
}
- In
doOpen()I create a newDeliveryand persist it. The delivery numberdeliveryNois the primary key and gets assigned by the database when I persist the new delivery. - Afterwards all the records coming from my
ItemProcessorget written to the file. - In
close()I need to write one more record thats different from the others. In this record I need information about the new and the previous delivery (to be precise, I need theirdeliveryNoanddeliveryDate).
The problem
When an exception occurs during the batch job (e.g. IOException, the new delivery is a failure but still...
- it gets persisted
- the sequence that manages the
deliveryNos is therefore increased nonetheless. - the content of the special record that im writing in
close()points to a failed delivery which is a critical error state and must be prevented. Ideally, there should only be successful deliveries in the database.
Now my question is: How can I prevent the failed delivery to be persisted and the deliveryNo counter to be increased when the batch job fails? Is there an easy / non-hacky way to "revert" everything so the database is basically in the state from before the batch job's execution? Is this even possible?