以下に示すように、グラフ プロパティのカスタム コンバーターを作成しました。
エンティティ クラス
@NodeEntity(label = "Person")
public class Person extends AbstractEntity {
@Property(name = "accessCount")
private Long accessCount;
@Property(name = "lastAccessDate")
@Convert(LocalDateTimeConverter.class)
private LocalDateTime lastAccessDate;
public Long getAccessCount() {
return accessCount;
}
public void setAccessCount(final Long accessCount) {
this.accessCount = accessCount;
}
public LocalDateTime getLastAccessDate() {
return lastAccessDate;
}
public void setLastAccessDate(final LocalDateTime lastAccessDate) {
this.lastAccessDate = lastAccessDate;
}
}
コンバータ
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.apache.commons.lang3.StringUtils;
import org.neo4j.ogm.typeconversion.AttributeConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class LocalDateTimeConverter implements AttributeConverter<LocalDateTime, String> {
private static final Logger LOG = LoggerFactory.getLogger(LocalDateTimeConverter.class);
@Value("${neo4j.dateTime.format:yyyy-MM-dd HH:mm:ss.SSS}")
private String dateTimeFormat;
@Override
public String toGraphProperty(final LocalDateTime value) {
LOG.debug("Converting local date time: {} to string ...", value);
if (value == null) {
return "";
}
return String.valueOf(value.format(getDateTimeFormatter()));
}
@Override
public LocalDateTime toEntityAttribute(final String value) {
LOG.debug("Converting string: {} to local date time ...", value);
if (StringUtils.isBlank(value)) {
return null;
}
return LocalDateTime.parse(value, getDateTimeFormatter());
}
public DateTimeFormatter getDateTimeFormatter() {
return DateTimeFormatter.ofPattern(dateTimeFormat);
}
}
単体テストに合格です
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestContextConfiguration.class)
@DirtiesContext
@TestExecutionListeners(inheritListeners = false, listeners = { DataSourceDependencyInjectionTestExecutionListener.class })
public class LocalDateTimeConverterTest {
public static final String DATE_TIME_VALUE = "2015-06-22 13:05:04.546";
@Autowired
protected LocalDateTimeConverter localDateTimeConverter;
@Test
public void should_get_date_time_formatter() {
final DateTimeFormatter dateTimeFormatter = localDateTimeConverter.getDateTimeFormatter();
assertNotNull(dateTimeFormatter);
}
@Test
public void should_convert_local_date_time_property_from_graph_property_string_for_database() throws Exception {
final LocalDateTime localDateTime = LocalDateTime.of(2015, Month.JUNE, 22, 13, 5, 4, 546000000);
final String actual = localDateTimeConverter.toGraphProperty(localDateTime);
final String expected = localDateTime.format(localDateTimeConverter.getDateTimeFormatter());
assertEquals(expected, actual);
}
@Test
public void should_convert_string_from_database_to_local_date_time() throws Exception {
final LocalDateTime localDateTime = localDateTimeConverter.toEntityAttribute(DATE_TIME_VALUE);
assertNotNull(localDateTime);
assertThat(localDateTime.getDayOfMonth(), equalTo(22));
assertThat(localDateTime.getMonthValue(), equalTo(6));
assertThat(localDateTime.getYear(), equalTo(2015));
assertThat(localDateTime.getHour(), equalTo(13));
assertThat(localDateTime.getMinute(), equalTo(5));
assertThat(localDateTime.getSecond(), equalTo(4));
assertThat(localDateTime.getNano(), equalTo(546000000));
}
}
ただし、以下に示すように、リポジトリから使用しようとすると。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestContextConfiguration.class)
@DirtiesContext
@TestExecutionListeners(inheritListeners = false, listeners = { DataSourceDependencyInjectionTestExecutionListener.class })
public class PersonRepositoryTest extends AbstractRepositoryTest<CypherFilesPopulator> {
private static final Logger LOG = LoggerFactory.getLogger(PersonRepositoryTest.class);
public static final String CQL_DATASET_FILE = "src/test/resources/dataset/person-repository-dataset.cql";
@Autowired
PersonRepository personRepository;
@Test
public void should_find_all_persons() {
LOG.debug("Test: Finding all persons ...");
final Iterable<Person> persons = personRepository.findAll();
persons.forEach(person -> {LOG.debug("Person: {}", person);});
}
@Override
public CypherFilesPopulator assignDatabasePopulator() {
return DatabasePopulatorUtil.createCypherFilesPopulator(Collections.singletonList(CQL_DATASET_FILE));
}
}
値の注入が行われていないため、単体テストが失敗します。
org.neo4j.ogm.metadata.MappingException: Error mapping GraphModel to instance of com.example.model.node.Person
at org.neo4j.ogm.mapper.GraphEntityMapper.mapEntities(GraphEntityMapper.java:97)
at org.neo4j.ogm.mapper.GraphEntityMapper.map(GraphEntityMapper.java:69)
at org.neo4j.ogm.session.response.SessionResponseHandler.loadAll(SessionResponseHandler.java:181)
at org.neo4j.ogm.session.delegates.LoadByTypeDelegate.loadAll(LoadByTypeDelegate.java:69)
at org.neo4j.ogm.session.delegates.LoadByTypeDelegate.loadAll(LoadByTypeDelegate.java:99)
at org.neo4j.ogm.session.Neo4jSession.loadAll(Neo4jSession.java:119)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy110.loadAll(Unknown Source)
at org.springframework.data.neo4j.repository.GraphRepositoryImpl.findAll(GraphRepositoryImpl.java:123)
at org.springframework.data.neo4j.repository.GraphRepositoryImpl.findAll(GraphRepositoryImpl.java:118)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:475)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:460)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:432)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy124.findAll(Unknown Source)
at com.example.repository.PersonRepositoryTest.should_find_all_persons(PersonRepositoryTest.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:73)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:224)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.lang.NullPointerException: pattern
at java.util.Objects.requireNonNull(Objects.java:228)
at java.time.format.DateTimeFormatterBuilder.appendPattern(DateTimeFormatterBuilder.java:1571)
at java.time.format.DateTimeFormatter.ofPattern(DateTimeFormatter.java:534)
at com.example.converter.LocalDateTimeConverter.getDateTimeFormatter(LocalDateTimeConverter.java:41)
at com.example.converter.LocalDateTimeConverter.toEntityAttribute(LocalDateTimeConverter.java:37)
at com.example.converter.LocalDateTimeConverter.toEntityAttribute(LocalDateTimeConverter.java:14)
at org.neo4j.ogm.entityaccess.FieldWriter.write(FieldWriter.java:64)
at org.neo4j.ogm.mapper.GraphEntityMapper.writeProperty(GraphEntityMapper.java:164)
at org.neo4j.ogm.mapper.GraphEntityMapper.setProperties(GraphEntityMapper.java:129)
at org.neo4j.ogm.mapper.GraphEntityMapper.mapNodes(GraphEntityMapper.java:110)
at org.neo4j.ogm.mapper.GraphEntityMapper.mapEntities(GraphEntityMapper.java:94)
... 74 more
コンバーター オブジェクトが SDN4 によってどのようにインスタンス化されるのか疑問に思っています。ここで何が間違っているのかわかりません。SDN 3.4 では、同様のアプローチが使用されていました。SDN 4 にアップグレードしたときに壊れ始めました。