Cannot retrieve projection with findBy function when using String type node ID

When I try to retrieve a projection via a custom findBy function in the repository, this only works when I use ID of type Long. When I switch to String (as Id() is deprecated in Neo4j) the same function doesn't return a value.

Any Idea?

Here is the test setup:

Entity and projection:

@Data 
@Node(labels={"Test"})
public class TestEntity  {
    @Id
    @GeneratedValue
    private Long ID;

    private String name;

    private String key;
}

@Data 
public class TestEntityProjection {
    @Id
    @GeneratedValue
    private Long ID;

    private String name;
}

Repository:

public interface TestRepository extends Neo4jRepository<TestEntity, Long> {
    Optional<TestEntityProjection> findTestEntityProjectionByID(Long elementID);
}

Test function:

public void projectionTest() {

    // create and save test extends
	TestEntity testEntity = new TestEntity();
	testEntity.setName("Test 1");
	testEntity = testRepository.save(testEntity);

	// retrieve entity by regular findByID => works with ID is both String and Long
	TestEntity entity = null;
	Optional<TestEntity> result1 = testRepository.findById(testEntity.getID());
	if (result1.isPresent()) {
		testfindById = result1.get();
	}

	// retrieve projection by special findByID => works only when ID is Long
	TestEntityProjection testfindProjectionById = null;
	Optional<TestEntityProjection> result2 = testRepository.findTestEntityProjectionByID(testEntity.getID());
	if (result2.isPresent()) {
		testfindProjectionById = result2.get();
	}
}

Based on your repository definition, your ID attribute is a long value. As such, your search parameters must be a long. What is it you are trying to accomplish?

BTW, the projection class is just a DTO. It doesn’t need the SDN entity annotations. You just need to include the entity attributes you want to project.

https://docs.spring.io/spring-data/neo4j/reference/repositories/projections.html#projections.dtos

Hi Gary,

Based on your repository definition, your ID attribute is a long value. As such, your search parameters must be a long. What is it you are trying to accomplish?

My problem is that when I switch the type of the ID property of both Entity and Projection class from Long to String the findTestEntityProjectionByID function returns an empty result whereas the findByID returns the correct object. Of course I will also change the Repository definition from Long to String. It wouldn't run at all If I wouldn't.

BTW, the projection class is just a DTO. It doesn’t need the SDN entity annotations. You just need to include the entity attributes you want to project.

Thanks for reminding me. But this doesn't have an impact on the problem.

So, if you replace Long with String in these three places, it stops working?

Do you delete the existing entities and create new ones with String IDs?

So, if you replace Long with String in these three places, it stops working?

Exactly

Do you delete the existing entities and create new ones with String IDs?

I've tried both approaches. Create with Long and Query with String and using String in both cases.

As I understand it the variable annotated with @Id will be mapped to Neo4J's "id" if it's Long and to "elementId" if it's String. So I would assume it should work in both scenarios as always both ID fields are populated.

I'm one step further to understand the problem (but still don't see the solution). I activated logging output and found that for findTestEntityProjectionByID() function call the following query is executed.

MATCH (testEntity:`Test`) WHERE id(testEntity) = $elementID RETURN testEntity{.name, __nodeLabels__: labels(testEntity), __elementId__: elementId(testEntity)}" {elementID="4:7d03d672-fff8-403e-85c9-25832a9c2428:303"}

So basically id(..) is used in the WHERE clause instead of elementId(..), which I did expect and which is used in the regular findById() function of the repository.

So maybe the solution lies somewhere in the naming of the repository function (something like findTestEntityProjectionByElementID()) but up to now I haven't figured out how.

You can try adding this bean to a configuration class, which your main class annotated with SpringBootApplication is.

 @Bean
    Configuration cypherDslConfiguration() {
        return Configuration.newConfig().withDialect(Dialect.NEO4J_5).build();
    }

I used the UUID sequence generator. I have not had such an issue.

    @GeneratedValue(UUIDStringGenerator.class)

Hi Gary,

@GeneratedValue(UUIDStringGenerator.class) did the job. Although I cannot spot any difference in the entities IDs in Neo4J now the projection object is present and populated.

Configuration of the NEO4J_5 dialect was already there.

Thank you very much for your help! I wouldn't have solved this by myself.

BR Thomas

1 Like

Glad it is working.

Interesting that yours worked, but my springboot application failed at startup when I removed the "UUIDStringGenerator.class" input to the GeneratedValue annotation. It complained as follows:

Caused by: java.lang.IllegalArgumentException: Cannot use internal id strategy with custom property id on entity class net.gslogix.xcrafter.orderService.entities.customerOrders.CustomerOrder

Anyways, we know how to do it properly.