jpenninkhof / odata-boilerplate

OpenUI5 boilerplate based on OLingo, JPA and Spring Boot
MIT License
82 stars 41 forks source link

Support $batch operations #1

Open lasterra opened 8 years ago

lasterra commented 8 years ago

In the SevletRegister you need to configure multipart to support batch operations.

Just include this line

odataServletRegistrationBean.setMultipartConfig(new MultipartConfigElement(""));

Thanks for your example, it was very helpful.

mjza commented 4 years ago

For supporting batch this is not enough, however I am not sure that can even help. For me batch is working as soon as we have only one changeset. As soon as we want to send multiple changes sets in one batch like this:

--batch_ffec-4d8c-af56
Content-Type: multipart/mixed; boundary=changeset_aec6-3a75-e9ea

--changeset_aec6-3a75-e9ea
Content-Type: application/http
Content-Transfer-Encoding: binary

MERGE QuestionSet(6) HTTP/1.1
sap-contextid-accept: header
Accept: application/json
Accept-Language: en-US
DataServiceVersion: 2.0
MaxDataServiceVersion: 2.0
Content-Type: application/json
Content-Length: 141

{"Deleted":false,"FormId":2,"Id":6,"Mandatory":true,"QtypeId":"QT2","Question":"Do you like your manager?","RandomWeight":"1.0","Sequence":1}
--changeset_aec6-3a75-e9ea--

--batch_ffec-4d8c-af56
Content-Type: multipart/mixed; boundary=changeset_4289-845e-d51d

--changeset_4289-845e-d51d
Content-Type: application/http
Content-Transfer-Encoding: binary

MERGE QuestionSet(5) HTTP/1.1
sap-contextid-accept: header
Accept: application/json
Accept-Language: en-US
DataServiceVersion: 2.0
MaxDataServiceVersion: 2.0
Content-Type: application/json
Content-Length: 157

{"Deleted":false,"FormId":2,"Id":5,"Mandatory":false,"QtypeId":"QT1","Question":"How do you like working with him or her?","RandomWeight":"1.0","Sequence":1}
--changeset_4289-845e-d51d--

--batch_ffec-4d8c-af56
Content-Type: multipart/mixed; boundary=changeset_714c-f47d-1abd

--changeset_714c-f47d-1abd
Content-Type: application/http
Content-Transfer-Encoding: binary

MERGE QuestionSet(20) HTTP/1.1
sap-contextid-accept: header
Accept: application/json
Accept-Language: en-US
DataServiceVersion: 2.0
MaxDataServiceVersion: 2.0
Content-Type: application/json
Content-Length: 187

{"Deleted":false,"FormId":2,"Id":20,"Mandatory":false,"QtypeId":"QT6","Question":"What are the things you like about your manager’s leadership style?","RandomWeight":"1.0","Sequence":3}
--changeset_714c-f47d-1abd--

--batch_ffec-4d8c-af56--

then it will show the following error:

<?xml version='1.0' encoding='UTF-8'?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
    <code/>
    <message xml:lang="en">Error on processing request content:Cannot begin Transaction on closed Session/EntityManager</message>
</error>
mjza commented 4 years ago

The solution is overriding the executeChangeSet of ODataJPADefaultProcessor class. The problem comes from a commiting function that will close the session:

@Override
public ODataResponse executeBatch(final BatchHandler handler, final String contentType, final InputStream content)
        throws ODataException {
    try {
        ODataResponse batchResponse;
        List<BatchResponsePart> batchResponseParts = new ArrayList<BatchResponsePart>();
        PathInfo pathInfo = getContext().getPathInfo();
        EntityProviderBatchProperties batchProperties = EntityProviderBatchProperties.init().pathInfo(pathInfo)
                .build();
        List<BatchRequestPart> batchParts = EntityProvider.parseBatchRequest(contentType, content, batchProperties);

        for (BatchRequestPart batchPart : batchParts) {
            batchResponseParts.add(handler.handleBatchPart(batchPart));
        }
        batchResponse = EntityProvider.writeBatchResponse(batchResponseParts);
        return batchResponse;
    } finally {
        close(true);
    }
}

@Override
  public BatchResponsePart executeChangeSet(final BatchHandler handler, final List<ODataRequest> requests)
      throws ODataException {
    List<ODataResponse> responses = new ArrayList<ODataResponse>();
    try {
      //oDataJPAContext.getODataJPATransaction().begin();

      for (ODataRequest request : requests) {
        //oDataJPAContext.setODataContext(getContext());
        ODataResponse response = handler.handleRequest(request);
        if (response.getStatus().getStatusCode() >= HttpStatusCodes.BAD_REQUEST.getStatusCode()) {
          // Roll Back
          oDataJPAContext.getODataJPATransaction().rollback();
          List<ODataResponse> errorResponses = new ArrayList<ODataResponse>(1);
          errorResponses.add(response);
          return BatchResponsePart.responses(errorResponses).changeSet(false).build();
        }
        responses.add(response);
      }
      //oDataJPAContext.getODataJPATransaction().commit();
      return BatchResponsePart.responses(responses).changeSet(true).build();
    } catch (Exception e) {
      throw new ODataException("Error on processing request content:" + e.getMessage(), e);
    } finally {
      close(true);
    }
  }

I have commented the two problematic lines!

Nihar41 commented 1 year ago

Hey you have mentioned that you have commented 2 lines but in the above code 3 lines seem to be committed. Can you confirm is it required to comment oDataJPAContext.setODataContext(getContext()); also. For me its working fine without commenting this line and commenting the other 2 lines. Thanks in advance.