Skip to content

Commit 89c7ab0

Browse files
Pass concrete enum type to MetaDataDrivenConversionService.
`Enum` properties always use an `AttributeConverter` and this is always passed to the parent conversion service. The parent conversion service however creates an adapter on the passed source type. Originally we passed `java.lang.Enum` here, leading to a scenario in which the parent decided to not be able to convert a concrete enum instance (and rightfully so). The solution in this commit is to pass the concrete field type if it is an enum. The conversion failed only for constructor calls. This fixes #2213.
1 parent b067d9f commit 89c7ab0

File tree

8 files changed

+262
-11
lines changed

8 files changed

+262
-11
lines changed

spring-data-neo4j/src/main/java/org/springframework/data/neo4j/conversion/MetaDataDrivenConversionService.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import org.neo4j.ogm.metadata.ClassInfo;
2121
import org.neo4j.ogm.metadata.FieldInfo;
2222
import org.neo4j.ogm.metadata.MetaData;
23+
import org.neo4j.ogm.support.ClassUtils;
2324
import org.neo4j.ogm.typeconversion.AttributeConverter;
25+
import org.neo4j.ogm.typeconversion.EnumStringConverter;
2426
import org.neo4j.ogm.typeconversion.ProxyAttributeConverter;
2527
import org.slf4j.Logger;
2628
import org.slf4j.LoggerFactory;
@@ -52,20 +54,20 @@ public MetaDataDrivenConversionService(MetaData metaData) {
5254
for (ClassInfo classInfo : metaData.persistentEntities()) {
5355
for (FieldInfo fieldInfo : classInfo.propertyFields()) {
5456
if (fieldInfo.hasPropertyConverter()) {
55-
addWrappedConverter(fieldInfo.getPropertyConverter());
57+
addWrappedConverter(fieldInfo.getField().getType(), fieldInfo.getPropertyConverter());
5658
}
5759
}
5860
}
5961
}
6062

6163
@SuppressWarnings({ "unchecked", "rawtypes" })
62-
private void addWrappedConverter(final AttributeConverter attributeConverter) {
64+
private void addWrappedConverter(Class<?> type, final AttributeConverter attributeConverter) {
6365

6466
if (attributeConverter instanceof ProxyAttributeConverter) {
6567
return;
6668
}
6769

68-
EntityToGraphTypeMapping entityToGraphTypeMapping = getEntityToGraphTypeMapping(attributeConverter);
70+
EntityToGraphTypeMapping entityToGraphTypeMapping = getEntityToGraphTypeMapping(type, attributeConverter);
6971

7072
if (canConvert(entityToGraphTypeMapping.entityType, entityToGraphTypeMapping.graphType)
7173
&& canConvert(entityToGraphTypeMapping.graphType, entityToGraphTypeMapping.entityType)) {
@@ -92,7 +94,7 @@ private EntityToGraphTypeMapping(Class<?> entityType, Class<?> target) {
9294
}
9395
}
9496

95-
static EntityToGraphTypeMapping getEntityToGraphTypeMapping(AttributeConverter attributeConverter) {
97+
static EntityToGraphTypeMapping getEntityToGraphTypeMapping(Class<?> type, AttributeConverter attributeConverter) {
9698

9799
ResolvableType resolvableType = ResolvableType.forClass(AttributeConverter.class,
98100
attributeConverter.getClass());
@@ -103,7 +105,7 @@ static EntityToGraphTypeMapping getEntityToGraphTypeMapping(AttributeConverter a
103105
+ attributeConverter.getClass());
104106
}
105107

106-
Class<?> sourceType = nestedTypeOrType(resolvableType.getGeneric(0));
108+
Class<?> sourceType = ClassUtils.isEnum(type) ? type : nestedTypeOrType(resolvableType.getGeneric(0));
107109
Class<?> targetType = nestedTypeOrType(resolvableType.getGeneric(1));
108110

109111
return new EntityToGraphTypeMapping(sourceType, targetType);

spring-data-neo4j/src/test/java/org/springframework/data/neo4j/conversion/MetaDataDrivenConversionServiceTests.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import org.springframework.data.neo4j.conversion.support.ConvertedClass;
2222
import org.springframework.data.neo4j.conversion.support.Converters;
2323

24+
import java.util.List;
25+
2426
/**
2527
* @author Michael J. Simons
2628
* @soundtrack Murray Gold - Doctor Who Season 9
@@ -30,7 +32,7 @@ public class MetaDataDrivenConversionServiceTests {
3032
@Test
3133
public void shouldDetermineConvertersForClasses() {
3234
MetaDataDrivenConversionService.EntityToGraphTypeMapping entityToGraphTypeMapping = MetaDataDrivenConversionService
33-
.getEntityToGraphTypeMapping(new Converters.DoubleToStringConverter());
35+
.getEntityToGraphTypeMapping(Double.class, new Converters.DoubleToStringConverter());
3436

3537
assertThat(entityToGraphTypeMapping.entityType).isEqualTo(Double.class);
3638
assertThat(entityToGraphTypeMapping.graphType).isEqualTo(String.class);
@@ -39,7 +41,7 @@ public void shouldDetermineConvertersForClasses() {
3941
@Test
4042
public void shouldDetermineConvertersForTypedClasses() {
4143
MetaDataDrivenConversionService.EntityToGraphTypeMapping entityToGraphTypeMapping = MetaDataDrivenConversionService
42-
.getEntityToGraphTypeMapping(new Converters.ListToStringConverter());
44+
.getEntityToGraphTypeMapping(List.class, new Converters.ListToStringConverter());
4345

4446
assertThat(entityToGraphTypeMapping.entityType).isEqualTo(Double.class);
4547
assertThat(entityToGraphTypeMapping.graphType).isEqualTo(String.class);
@@ -48,7 +50,7 @@ public void shouldDetermineConvertersForTypedClasses() {
4850
@Test // DATAGRAPH-1131
4951
public void shouldWorkWithConvertersInvolvingAbstractBaseClasses() {
5052
MetaDataDrivenConversionService.EntityToGraphTypeMapping entityToGraphTypeMapping = MetaDataDrivenConversionService
51-
.getEntityToGraphTypeMapping(new Converters.ConvertedClassToStringConverter());
53+
.getEntityToGraphTypeMapping(ConvertedClass.class, new Converters.ConvertedClassToStringConverter());
5254

5355
assertThat(entityToGraphTypeMapping.entityType).isEqualTo(ConvertedClass.class);
5456
assertThat(entityToGraphTypeMapping.graphType).isEqualTo(String.class);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2011-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.conversion.gh2213;
17+
18+
/**
19+
* @author Michael J. Simons
20+
*/
21+
public enum DescribeType {
22+
A, B
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2011-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.conversion.gh2213;
17+
18+
import org.neo4j.ogm.annotation.EndNode;
19+
import org.neo4j.ogm.annotation.GeneratedValue;
20+
import org.neo4j.ogm.annotation.Id;
21+
import org.neo4j.ogm.annotation.RelationshipEntity;
22+
import org.neo4j.ogm.annotation.StartNode;
23+
24+
/**
25+
* @author Michael J. Simons
26+
*/
27+
@RelationshipEntity("DESCRIBES")
28+
public class Describes {
29+
30+
@Id @GeneratedValue
31+
private Long id;
32+
33+
private DescribeType something;
34+
35+
@StartNode
36+
private NodeA nodeA;
37+
38+
@EndNode
39+
private NodeB nodeB;
40+
41+
Describes() {
42+
}
43+
44+
public Describes(DescribeType something, NodeA nodeA, NodeB nodeB) {
45+
this.something = something;
46+
this.nodeA = nodeA;
47+
this.nodeB = nodeB;
48+
}
49+
50+
public Long getId() {
51+
return id;
52+
}
53+
54+
public DescribeType getSomething() {
55+
return something;
56+
}
57+
58+
public void setSomething(DescribeType something) {
59+
this.something = something;
60+
}
61+
62+
public NodeA getNodeA() {
63+
return nodeA;
64+
}
65+
66+
public void setNodeA(NodeA nodeA) {
67+
this.nodeA = nodeA;
68+
}
69+
70+
public NodeB getNodeB() {
71+
return nodeB;
72+
}
73+
74+
public void setNodeB(NodeB nodeB) {
75+
this.nodeB = nodeB;
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2011-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.conversion.gh2213;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
21+
import org.neo4j.ogm.annotation.GeneratedValue;
22+
import org.neo4j.ogm.annotation.Id;
23+
import org.neo4j.ogm.annotation.NodeEntity;
24+
import org.neo4j.ogm.annotation.Relationship;
25+
26+
/**
27+
* @author Michael J. Simons
28+
*/
29+
@NodeEntity
30+
public class NodeA {
31+
32+
@Id @GeneratedValue
33+
private Long id;
34+
35+
private DescribeType describeType;
36+
37+
@Relationship(type = "DESCRIBES")
38+
private List<Describes> describes = new ArrayList<>();
39+
40+
public NodeA(DescribeType describeType) {
41+
this.describeType = describeType;
42+
}
43+
44+
public Long getId() {
45+
return id;
46+
}
47+
48+
public DescribeType getDescribeType() {
49+
return describeType;
50+
}
51+
52+
public void setDescribeType(DescribeType describeType) {
53+
this.describeType = describeType;
54+
}
55+
56+
public List<Describes> getDescribes() {
57+
return describes;
58+
}
59+
60+
public void setDescribes(List<Describes> describes) {
61+
this.describes = describes;
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2011-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.conversion.gh2213;
17+
18+
import org.neo4j.ogm.annotation.GeneratedValue;
19+
import org.neo4j.ogm.annotation.Id;
20+
import org.neo4j.ogm.annotation.NodeEntity;
21+
22+
/**
23+
* @author Michael J. Simons
24+
*/
25+
@NodeEntity
26+
public class NodeB {
27+
28+
@Id @GeneratedValue
29+
private Long id;
30+
31+
public Long getId() {
32+
return id;
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2011-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.conversion.gh2213;
17+
18+
import org.springframework.data.neo4j.repository.Neo4jRepository;
19+
20+
/**
21+
* @author Michael J. Simons
22+
*/
23+
public interface RepositoryUnderTest extends Neo4jRepository<NodeA, Long> {
24+
}

spring-data-neo4j/src/test/java/org/springframework/data/neo4j/integration/conversion/ConversionServiceTests.java

+29-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.math.BigInteger;
2020
import java.math.RoundingMode;
2121
import java.util.Arrays;
22+
import java.util.Optional;
2223

2324
import org.junit.Before;
2425
import org.junit.Test;
@@ -29,12 +30,16 @@
2930
import org.neo4j.ogm.session.SessionFactory;
3031
import org.springframework.beans.factory.annotation.Autowired;
3132
import org.springframework.context.annotation.Bean;
32-
import org.springframework.context.annotation.ComponentScan;
3333
import org.springframework.context.annotation.Configuration;
3434
import org.springframework.core.convert.ConversionService;
3535
import org.springframework.core.convert.ConverterNotFoundException;
3636
import org.springframework.core.convert.support.DefaultConversionService;
3737
import org.springframework.data.neo4j.conversion.MetaDataDrivenConversionService;
38+
import org.springframework.data.neo4j.conversion.gh2213.DescribeType;
39+
import org.springframework.data.neo4j.conversion.gh2213.Describes;
40+
import org.springframework.data.neo4j.conversion.gh2213.NodeB;
41+
import org.springframework.data.neo4j.conversion.gh2213.RepositoryUnderTest;
42+
import org.springframework.data.neo4j.conversion.gh2213.NodeA;
3843
import org.springframework.data.neo4j.integration.conversion.domain.JavaElement;
3944
import org.springframework.data.neo4j.integration.conversion.domain.MonetaryAmount;
4045
import org.springframework.data.neo4j.integration.conversion.domain.PensionPlan;
@@ -65,6 +70,8 @@ public class ConversionServiceTests {
6570
@Autowired private PensionRepository pensionRepository;
6671
@Autowired private JavaElementRepository javaElementRepository;
6772
@Autowired private SiteMemberRepository siteMemberRepository;
73+
@Autowired private RepositoryUnderTest repositoryUnderTest;
74+
6875
// TODO See below, for the time being at least be explicit on which type of conversion service we're working on here
6976
// The only thing that is under test, is the instance of MetaDataDrivenConversionService which get's even
7077
// modified heavily be this test. This needs to be fixed in the near future.
@@ -251,9 +258,28 @@ public void shouldRecognizeJavaEnums() {
251258
assertThat(siteMember.getRoundingModes().contains(RoundingMode.FLOOR)).isTrue();
252259
}
253260

261+
@Test // GH-2213
262+
public void enumConvertersOnConstructorsShouldWork() {
263+
264+
long id = transactionTemplate.execute(tx -> {
265+
NodeA s = new NodeA(DescribeType.A);
266+
Describes d = new Describes(DescribeType.B, s, new NodeB());
267+
268+
s.setDescribes(Arrays.asList(d));
269+
return repositoryUnderTest.save(s).getId();
270+
});
271+
Optional<NodeA> optionalResult = repositoryUnderTest.findById(id);
272+
assertThat(optionalResult).hasValueSatisfying(nl -> {
273+
assertThat(nl.getDescribeType()).isEqualTo(DescribeType.A);
274+
assertThat(nl.getDescribes()).hasSize(1)
275+
.first().satisfies(dl -> assertThat(dl.getSomething()).isEqualTo(DescribeType.B));
276+
});
277+
278+
}
279+
254280
@Configuration
255-
@Neo4jIntegrationTest(domainPackages = "org.springframework.data.neo4j.integration.conversion.domain",
256-
repositoryPackages = "org.springframework.data.neo4j.integration.conversion")
281+
@Neo4jIntegrationTest(domainPackages = {"org.springframework.data.neo4j.integration.conversion.domain", "org.springframework.data.neo4j.conversion.gh2213"},
282+
repositoryPackages = {"org.springframework.data.neo4j.integration.conversion", "org.springframework.data.neo4j.conversion.gh2213"})
257283
static class ConversionServicePersistenceContext {
258284

259285
@Bean

0 commit comments

Comments
 (0)