2

以下に示すように、グラフ プロパティのカスタム コンバーターを作成しました。

エンティティ クラス

@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 にアップグレードしたときに壊れ始めました。

4

1 に答える 1

3

AttributeConverterこの場合、実際にインスタンス を構築するのは Spring ではないため、これが発生しています。AttributeConverter基礎となるオブジェクトグラフマッパー (OGM) に由来し、これは設計上、Spring を認識しないため、管理するクラスの Spring アノテーションを無視します。

ただし、クラスの代わりにターゲット タイプを指定@Convertしてフィールドのアノテーションを変更すると、 Spring のアノテーションを代わりに使用できます。必要な Spring コンバーターを に登録すると、フレームワークはこれを変換に使用する必要があります。PersonAttributeConverterConversionServiceMetaDataDrivenConversionService

メタデータ駆動型の変換サービスは、次のNeo4jConfigurationようにサブクラスで構築できます。

@Bean
public ConversionService springConversionService() {
   return new MetaDataDrivenConversionService(getSessionFactory().metaData());
}
于 2015-09-15T09:04:10.073 に答える