cancel
Showing results for 
Search instead for 
Did you mean: 

CDS OData Java service com.sap.cds.NonUniqueResultException

0 Kudos

Hello,

I am new in this forum, and new to SAP Cloud Foundry/CAP. I can not find a solution to the below problem anywhere.

I have created a Odata Java service using the CAP Java Maven Archetype as mentioned here: https://cap.cloud.sap/docs/java/getting-started

Other than splitting entities and services in two files, eventually I have combined them into one cds file:

service service {
	entity Case {
		key ID : Integer;
		caseNumber : Integer;
		description : String;
		phase  : String;
	}
}

Next, I have created a CaseImpl, which extends HashMap<String, Object> and it implements the generated 'Case' interface. All four properties are provided in the CaseImpl, like this:

	public Integer getId() {
		return (Integer) get(ID);
	}

	public void setId(final Integer id) {
		put(ID, id);
	}

So, basically, CaseImpl is a HashMap containing all necessary String key / Object value combinations.

Then I have added a custom event handler, as mentioned here: https://developers.sap.com/tutorials/cp-cap-java-custom-handler.html

It contains this method:

	@On(event = CdsService.EVENT_READ, entity = "service.Case")
	public void onReadCases(final CdsReadEventContext context) {

		context.setResult(caseDao.getCases());
	}

... the getCases() method returns a collection of four CaseImpl instances that are identified by ID= 1, 2, 3 and 4.

And the entire collection is shown perfectly when calling /odata/v4/service/Case

However, when calling /odata/v4/service/Case(1) I expect to see only Case with ID=1, but I get:

com.sap.cds.NonUniqueResultException: Result contains more than one row
	at com.sap.cds.impl.ResultImpl.single(ResultImpl.java:90) ~[cds4j-core-1.8.0.jar:na]
	at com.sap.cds.adapter.odata.v4.processors.impl.ResultSetProcessor.toEntity(ResultSetProcessor.java:113) ~[cds-adapter-odata-v4-1.4.0.jar:na]
	at com.sap.cds.adapter.odata.v4.processors.impl.DataProvider.lambda$processReadRequest$6(DataProvider.java:291) ~[cds-adapter-odata-v4-1.4.0.jar:na]
	at com.sap.cds.services.impl.runtime.CdsRuntimeImpl.runInRequestContext(CdsRuntimeImpl.java:158) ~[cds-services-impl-1.4.0.jar:na]
	at com.sap.cds.adapter.odata.v4.processors.impl.DataProvider.processReadRequest(DataProvider.java:276) ~[cds-adapter-odata-v4-1.4.0.jar:na]
	at com.sap.cds.adapter.odata.v4.processors.impl.DataProvider.processRequestsBasedOnMethods(DataProvider.java:110) ~[cds-adapter-odata-v4-1.4.0.jar:na]
	at com.sap.cds.adapter.odata.v4.processors.impl.DataProvider.lambda$processChangesetRequest$0(DataProvider.java:88) ~[cds-adapter-odata-v4-1.4.0.jar:na]
	at com.sap.cds.adapter.odata.v4.handlers.ODataProcessorHandler.lambda$runInReqContextIfNecessary$2(ODataProcessorHandler.java:812) ~[cds-adapter-odata-v4-1.4.0.jar:na]
	at com.sap.cds.services.impl.runtime.CdsRuntimeImpl.runInChangeSetContext(CdsRuntimeImpl.java:167) ~[cds-services-impl-1.4.0.jar:na]
	at com.sap.cds.adapter.odata.v4.handlers.ODataProcessorHandler.runInReqContextIfNecessary(ODataProcessorHandler.java:811) ~[cds-adapter-odata-v4-1.4.0.jar:na]
	at com.sap.cds.adapter.odata.v4.processors.impl.DataProvider.processChangesetRequest(DataProvider.java:94) ~[cds-adapter-odata-v4-1.4.0.jar:na]
	at com.sap.cds.adapter.odata.v4.processors.impl.GenericODataProcessor.readEntityInternal(GenericODataProcessor.java:410) ~[cds-adapter-odata-v4-1.4.0.jar:na]
	at com.sap.cds.adapter.odata.v4.processors.impl.GenericODataProcessor.readEntity(GenericODataProcessor.java:398) ~[cds-adapter-odata-v4-1.4.0.jar:na]
	at com.sap.cds.adapter.odata.v4.processors.impl.GenericODataProcessor.readEntity(GenericODataProcessor.java:130) ~[cds-adapter-odata-v4-1.4.0.jar:na]

(etc., no cause)

Also, when calling /odata/v4/service/Case?$top=2, all four cases are shown. As if the filter is ignored.

What am I doing wrong? The CDS version is 1.4.0.

Thanks in advance,

Kind regards,

Kjeld

vfweiss
Participant

gregorw & iinside do you guys maybe recognise anything like this?

Accepted Solutions (0)

Answers (1)

Answers (1)

0 Kudos

Well, boy meets world, I understand what we did wrong now.

When implemeting the @On handle, apparently CDS expects you to provide exactly what is asked for. I assumed we could provide the entire collection of entities (Case) and that CDS would do any fitering for us.

It appears CDS does exactly that, as long as it can retrieve the data from the database.

The thing is, we need to be able to provide the lastest Cases to the client, which we retrieve from a backend. Not those cached in a database.

We solved our problem by deleting the @On handle, and creating a @Before handle where we delete all Cases from the database, and insert the latest versions. Then CDS picks up those cases and it does any necessary filtering for us.

	@Before(event = CdsService.EVENT_READ, entity = "service.Case")
	public void beforeReadCases(final CdsReadEventContext context) {

		final CdsService service = context.getService();

		// Delete all cases from the database
		final CqnDelete delete = Delete.from("service.Case");
		service.run(delete);

		// Put new cases in the database
		final CqnInsert insert = Insert.into("service.Case").entries(caseDao.getCases());
		service.run(insert);
	}