-
Notifications
You must be signed in to change notification settings - Fork 619
Description
When using the @DynamicLabels annotation, objects are getting instantiated as the incorrect type if there are values added to the dynamic labels set. This issue has been tested and confirmed on as late as SDN version 7.5.2. This issue also seems similar to this ticket from last year:
#2886.
Here is an example that demonstrates the issue:
Entities
@Getter
@Setter
@NoArgsConstructor
@Node(primaryLabel = "Vehicle")
public class Vehicle {
@Id
public String id;
@Property(name = "name")
public String name;
@JsonIgnore
@DynamicLabels
public Set<String> labels = Set.of();
}
@Getter
@Setter
@NoArgsConstructor
@Node(primaryLabel = "Car")
public class Car extends Vehicle {
}
@Getter
@Setter
@NoArgsConstructor
@Node(primaryLabel = "Sedan")
public class Sedan extends Car {
}
Repository
@Repository
public interface VehicleRepository extends Neo4jRepository<Vehicle, String> {
@Query("MATCH (vehicle:Vehicle) RETURN vehicle")
List<Vehicle> findAllVehicles();
}
Test
@DataNeo4jTest
class VehicleRepositoryTest (
@Autowired val vehicleRepository: VehicleRepository,
) {
@Test
fun `dynamic labels cause incorrect sub-types`() {
val vehicleWithDynamicLabel = Vehicle().apply {
name = "Vehicle with dynamic label"
labels = setOf("Random Label")
}
val vehicleWithTwoDynamicLabels = Vehicle().apply {
name = "Vehicle with dynamic label"
labels = setOf("Random Label", "Random Label 2")
}
val vehicleWithoutDynamicLabel = Vehicle().apply {
name = "Vehicle without dynamic label"
}
vehicleRepository.saveAll(listOf(vehicleWithDynamicLabel, vehicleWithoutDynamicLabel, vehicleWithTwoDynamicLabels))
val vehicles = vehicleRepository.findAllVehicles();
assertThat(vehicles.filterIsInstance<Sedan>()).isEmpty()
assertThat(vehicles.filterIsInstance<Car>()).isEmpty()
}
}
The above test fails, because vehicleWithDynamicLabel and vehicleWithTwoDynamicLabels get mapped to Car objects when retrieved from the database, when they are nodes of the parent type Vehicle. vehicleWithoutDynamicLabel is correctly mapped to a Vehicle object because it does not have dynamic labels. With one or two dynamic labels, both Vehicle nodes still mapped to Car objects, neither were instantiated as the grandchild type Sedan.
We can see in the following picture, that two of the three Vehicle nodes get mapped to Car because they have dynamic labels:
