Spring Пользовательский ресурс пользовательского ресурса REST данных работает для строки, но не длинный

У меня есть модель:

public class MyModel {
    @Id private Long id;
    private Long externalId;
    // Getters, setters
}

Я хотел бы использовать externalId как свой идентификатор ресурса:

@Configuration
static class RepositoryEntityLookupConfig extends RepositoryRestConfigurerAdapter {
    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration configuration) {
        configuration
                .withEntityLookup()
                    .forRepository(MyRepository.class, MyModel::getExternalId, MyRepository::findByExternalId);
    }
}

Если externalId является String, это отлично работает. Но поскольку это число (Long)

public interface MyRepository extends JpaRepository<MyModel, Long> {
    Optional<MyModel> findByExternalId(@Param("externalId") Long externalId);
}

при вызове: /myModels/1 Я получаю:

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Long
    at org.springframework.data.rest.core.config.EntityLookupConfiguration$RepositoriesEntityLookup.lookupEntity(EntityLookupConfiguration.java:213) ~[spring-data-rest-core-2.6.4.RELEASE.jar:na]
    at org.springframework.data.rest.core.support.UnwrappingRepositoryInvokerFactory$UnwrappingRepositoryInvoker.invokeFindOne(UnwrappingRepositoryInvokerFactory.java:130) ~[spring-data-rest-core-2.6.4.RELEASE.jar:na]
    at org.springframework.data.rest.webmvc.RepositoryEntityController.getItemResource(RepositoryEntityController.java:524) ~[spring-data-rest-webmvc-2.6.4.RELEASE.jar:na]
    at org.springframework.data.rest.webmvc.RepositoryEntityController.getItemResource(RepositoryEntityController.java:335) ~[spring-data-rest-webmvc-2.6.4.RELEASE.jar:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_111]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_111]
    ...

Работает отдельный пользовательский класс EntityLookupSupport<MyModel>.

Я пропустил что-то, чтобы заставить его работать на Long с помощью ссылок на методы в моем RepositoryRestConfigurerAdapter?

Ответ 1

Попробуйте добавить это в свой класс RepositoryEntityLookupConfig:

@Override
public void configureConversionService(ConfigurableConversionService conversionService) {
    conversionService.addConverter(String.class, Long.class, Long::parseLong);
    super.configureConversionService(conversionService);
}

Ответ 2

Вам действительно нужно настроить конфигурацию самостоятельно? Вы можете попытаться использовать автоматическую конфигурацию spring -boot, добавив аннотацию @RepositoryRestResource

@RepositoryRestResource(collectionResourceRel = "myModels", path = "myModels")
public interface MyRepository extends JpaRepository<MyModel, Long> {
        Optional<MyModel> findByExternalId(@Param("externalId") Long externalId);
}

Также добавьте @Entity в класс модели

@Entity
public class MyModel {
    @Id 
    private Long id;
    @Column(name = "EXTERNAL_ID")
    // Column annotation is not required if you respect case-sensitive
    private Long externalId;
    // Getters, setters
}

Ответ 3

Подпись метода, который вы пытаетесь вызвать, выглядит следующим образом:

forRepository(Class<R> type, Converter<T,ID> identifierMapping, 
         EntityLookupRegistrar.LookupRegistrar.Lookup<R,ID> lookup)

Я не очень хорошо знаком с Java 8, но я не вижу, как MyModel::getExternalId может выполнить необходимое преобразование.

Я бы попробовал что-то вроде следующего, которое, без сомнения, имеет "упрощенную" альтернативную конструкцию Java 8.

@Configuration
static class RepositoryEntityLookupConfig extends RepositoryRestConfigurerAdapter {
    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration configuration) {

    Converter<String, Long> converter = new Converter(){
        public Long convert(String source){
            return Long.parseLong(source);
        }
    };

    configuration
                .withEntityLookup()
                    .forRepository(MyRepository.class, converter, MyRepository::findByExternalId);
    }
}