@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
中的错误时,我发现了一个类似的问题:
Issue with projection in SpringDataRest and @Lob attribute
答案 0 :(得分:0)
投影背后的想法是限制返回的列(最好是从数据库请求的列)。 内置的转换支持不多,因为这通常由JPA处理,但这不会发生,因为您使用的是本机查询。
因此,我看到了两种解决方法:
在数据库中将LOB转换为VARCHAR2或类似内容。 如何完成此操作取决于您的数据库。 This answer seems to work for SQL Server。 我确定您会为正在使用的任何数据库找到替代方法。
通过使用JPQL查询在游戏中重新获得JPA。 那应该是独立于数据库的,但是我认为您有一个使用本机查询的理由。
答案 1 :(得分:0)
解决此问题的一种方法是使用Spring Content社区项目。该项目使您可以将内容与Spring Data实体相关联。内容是单独管理的,仅在实体上保留与“托管”内容相关的元数据。这不会弄乱您的预测。考虑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> {
}
这就是在/itemsContent
上创建REST端点所需的全部。当您的应用程序启动时,Spring Content将查看您的依赖项(请参阅Spring Content JPA / REST),查看您的ItemContentStore
接口,并为JPA注入该接口的实现。它还将注入一个@Controller
来将http请求转发到该实现。这省去了您自己实现的任何麻烦,而我想这就是您的追求。
所以...
要通过Java API访问内容,请自动连接ItemContentStore
并使用其方法。
或通过REST API访问内容:
curl -X POST /itemsContent/{itemId}
会将图像存储在数据库中,并将其与ID为itemId
的帐户实体相关联。
curl /itemsContent/{itemId}
将再次获取它,依此类推...支持完整的CRUD。
有两个入门指南here。参考指南为here。还有一个教程视频here。编码位大约从1/2开始。
HTH