Проблема с проекцией при использовании @Lob и @Query - PullRequest
0 голосов
/ 29 января 2019

Объект:

@Entity
public class Item {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private Integer price;

    @Lob
    private String description;
}

Интерфейс для проецирования:

public interface NameAndDesc {

    String getAlias();
    String getDesc();
}

Репозиторий:

public interface ItemRepository extends JpaRepository<Item, Long> {

    @Query(value = "SELECT NAME AS ALIAS, DESCRIPTION AS DESC FROM ITEM WHERE ID IS :#{#id}",nativeQuery = true)
    NameAndDesc findNameAndDesc(@Param("id") Long id);
}

Когда я пытаюсь вызвать .getDesc() в запросе выше, Я получаю это исключение:

java.lang.IllegalArgumentException: Projection type must be an interface!

at org.springframework.util.Assert.isTrue(Assert.java:118)
at org.springframework.data.projection.ProxyProjectionFactory.createProjection(ProxyProjectionFactory.java:100)
at org.springframework.data.projection.SpelAwareProxyProjectionFactory.createProjection(SpelAwareProxyProjectionFactory.java:45)
at org.springframework.data.projection.ProjectingMethodInterceptor.getProjection(ProjectingMethodInterceptor.java:131)
at org.springframework.data.projection.ProjectingMethodInterceptor.invoke(ProjectingMethodInterceptor.java:80)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.projection.ProxyProjectionFactory$TargetAwareMethodInterceptor.invoke(ProxyProjectionFactory.java:245)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy105.getDesc(Unknown Source)
at com.example.demo.DemoApplicationTests.contextLoads(DemoApplicationTests.java:18)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
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.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
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:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Когда я удаляю аннотацию "@Lob" из "description", проекция работает без каких-либо проблем.Кажется, проблема в том, что CLOB возвращается из БД.Когда я изменяю метод интерфейса проецирования на «java.sql.Clob getDesc();», кажется, что он снова начинает работать, но это не лучшее решение.

Правильное ли поведение при использовании проекций, как этот?

Я обнаружил несколько похожую проблему, когда она была ошибкой в ​​ProxyProjectionFactory: Проблема с проекцией в атрибуте SpringDataRest и @Lob

Ответы [ 2 ]

0 голосов
/ 30 января 2019

Одним из способов решения этой проблемы является использование проекта сообщества Spring Content .Этот проект позволяет связать контент с сущностями Spring Data.Контент управляется отдельно, оставляя только «управляемые» метаданные, связанные с контентом, на объекте.Это не испортит ваши прогнозы.Подумайте, Spring Spring, но для контента (или неструктурированных данных).

Это довольно легко добавить в существующие проекты.Я не уверен, используете ли вы Spring Boot или нет.Я приведу пример загрузки без пружины:

pom.xml

   <!-- Java API -->
   <dependency>
      <groupId>com.github.paulcwarren</groupId>
      <artifactId>spring-content-jpa</artifactId>
      <version>0.5.0</version>
   </dependency>
   <!-- REST API (if desired)-->
   <dependency>
      <groupId>com.github.paulcwarren</groupId>
      <artifactId>spring-content-rest</artifactId>
      <version>0.5.0</version>
   </dependency>

Конфигурация

@Configuration
@EnableJpaStores
@Import("org.springframework.content.rest.config.RestConfiguration.class")
public class ContentConfig {

    // schema management
    // 
    @Value("/org/springframework/content/jpa/schema-drop-mysql.sql")
    private Resource dropContentTables;

    @Value("/org/springframework/content/jpa/schema-mysql.sql")
    private Resource createContentTables;

    @Bean
    DataSourceInitializer datasourceInitializer() {
        ResourceDatabasePopulator databasePopulator =
                new ResourceDatabasePopulator();

        databasePopulator.addScript(dropContentTables);
        databasePopulator.addScript(createContentTables);
        databasePopulator.setIgnoreFailedDrops(true);

        DataSourceInitializer initializer = new DataSourceInitializer();
        initializer.setDataSource(dataSource());
        initializer.setDatabasePopulator(databasePopulator);

        return initializer;
    }
}

Чтобы связать контент, добавьте аннотации Spring Content к своей учетной записи.

Item.java

@Entity
public class Item {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private Integer price;

    // replace @Lob field with
    @ContentId
    private String contentId;

    @ContentLength
    private long contentLength = 0L;

    // if you have rest endpoints
    @MimeType
    private String mimeType = "text/plain";
}

Создайте «хранилище»:

ItemContentStore.java

@StoreRestResource(path="itemsContent)
public interface ItemContentStore extends ContentStore<Item, String> {
}

Это все, что вам нужно для создания конечных точек REST @ /itemsContent.Когда ваше приложение запускается, Spring Content проверит ваши зависимости (см. Spring Content JPA / REST), взглянет на ваш ItemContentStore интерфейс и внедрит реализацию этого интерфейса для JPA.Он также внедрит @Controller, который перенаправляет http-запросы к этой реализации.Это избавляет вас от необходимости реализовывать все это самостоятельно, что, я думаю, то, что вы ищете.

Итак ...

Чтобы получить доступ к контенту через API Java, автоматически подключите ItemContentStore и используйте его методы.

Или для доступа к контенту через REST API:

curl -X POST /itemsContent/{itemId}

с запросом multipart / form-data сохранит изображение в базе данных и свяжет его сучетная запись, чей идентификатор itemId.

curl /itemsContent/{itemId}

будет извлекать ее снова и так далее ... поддерживает полный CRUD.

Есть несколько вариантов полученияРуководства здесь .Справочное руководство здесь .И здесь есть обучающее видео здесь .Кодовый бит начинается примерно на полпути.

HTH

0 голосов
/ 29 января 2019

Идея, лежащая в основе проекции, заключается в ограничении возвращаемых столбцов и (в идеале запрашиваемых) из базы данных.Встроенной поддержки конверсии не так много, потому что она обычно обрабатывается JPA, но этого не происходит, потому что вы используете собственный запрос.

Поэтому я вижу два варианта решения проблемы:

  1. Конвертировать большой объект в VARCHAR2 или аналогичный в базе данных.Как это сделать, зависит от вашей базы данных. Этот ответ, кажется, работает для SQL Server .Я уверен, что вы найдете альтернативу для любой базы данных, которую вы используете.

  2. Верните JPA в игру, используя запрос JPQL.Это должно быть независимо от базы данных, но я предполагаю, что у вас была причина для использования собственного запроса, для начала.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...