Inheritance of entities/repositories [DATAREST-344]
Benjamin M opened DATAREST-344 and commented
I'd like to use inheritance for my entities. And the only way of getting it kinda working was to do the following:
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="type")
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
public abstract class Message implements Identifiable<UUID> { ... }
@Entity
@DiscriminatorValue("TEXT")
@JsonTypeName("TEXT")
public class TextMessage extends Message { ... }
@Entity
@DiscriminatorValue("TODO")
@JsonTypeName("TODO")
public class TodoMessage extends Message { ... }
public interface MessageRepo extends JpaRepository<Message, UUID> { }
public interface TextMessageRepo extends JpaRepository<TextMessage, UUID> { }
public interface TodoMessageRepo extends JpaRepository<TodoMessage, UUID> { }
When I now call GET http://localhost:8080/webapp/messages I get the following output:
{
"_links": {
"self": { "href": "http://localhost:8080/webapp/messages{?page,size,sort}", "templated": true }
},
"_embedded": {
"textMessages": [
{ ... },
{ ... }
],
"todoMessages": [
{ ... }
]
},
"page": {
"size": 20, "totalElements": 3, "totalPages": 1, "number": 0
}
}
As you can see, there's textMessages and todoMessages within the _embedded object. So, I cannot really sort my messages.
Is there a way to get all my messages within a single array?
—
EDIT: I tried to get rid of my additional projections, but that won't work:
If I only have a @Projection for Message.class, it won't get applied when calling GET http://localhost:8080/webapp/messages?projection=summary,
BUT it does work, if it's an inherited projection: GET http://localhost:8080/webapp/messageInboxes/89cb89db-67c5-49b3-8f1f-00b63b74ca4a?projection=summary
with:
@Projection(name = "summary", types = MessageInbox.class)
public interface MessageInboxSummary {
MessageSummary getMessage();
}
@Projection(name = "summary", types = Message.class)
public interface MessageSummary {
String getSubject();
}
@Entity
public class MessageInbox extends Identifiable<UUID> {
@ManyToOne
@JoinColumn(name = "Message_id", nullable = false, updatable = false)
Message message;
public Message getMessage() { return message; }
}
—
EDIT 2: I now somehow fixed it / encountered a new bug...
If I put
@RepositoryRestResource(collectionResourceRel="messages", path="messages")
on all 3 repositories it behaves like I want it to: Every kind of message is available under GET http://localhost:8080/webapp/messages and all get displayed within the same array!
But now there are 2 issues:
- The index page displays:
{
"_links": {
"messages": [
{
"href": "http://localhost:8080/webapp/messages{?page,size,sort,projection}",
"templated": true
},
{
"href": "http://localhost:8080/webapp/messages{?page,size,sort,projection}",
"templated": true
},
{
"href": "http://localhost:8080/webapp/messages{?page,size,sort,projection}",
"templated": true
}
]
}
}
- if I add
itemResourceRel="message"to the@RepositoryRestResourceannotation on all 3 repositories, the URLGET http://localhost:8080/webapp/messageswill display randomly either theTextMessagesOR theTodoMessages, but never both
Affects: 2.1.1 (Dijkstra SR1)
9 votes, 16 watchers
Oliver Drotbohm commented
I think falling back to a repo per concrete type is not a good idea. You shouldn't need to tweak your repository setup that much to get the output you want.
Have you tried registering a RelProvider implementation that returns message/messages for all subtypes of Message?
Benjamin M commented
There's one situation when fallback would be pretty awesome:
Basically I did only want to use a single repository for all 3 entities:
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="type")
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
public abstract class Message implements Identifiable<UUID> {
@Column(name = "type", insertable = false, updatable = false)
String type;
public String getType() { return type; }
public void setType(String type) { this.type = type; }
}
@Entity
@DiscriminatorValue("TEXT")
@JsonTypeName("TEXT")
public class TextMessage extends Message { ... }
@Entity
@DiscriminatorValue("TODO")
@JsonTypeName("TODO")
public class TodoMessage extends Message { ... }
public interface MessageRepo extends JpaRepository<Message, UUID> { }
Jackson handles this pretty well, because I have:
@Configuration
public class RepositoryRestMvcConfig extends RepositoryRestMvcConfiguration {
@Override
protected void configureJacksonObjectMapper(ObjectMapper objectMapper) {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AnnotationTypeFilter(JsonTypeName.class));
for(BeanDefinition candidate : provider.findCandidateComponents(Application.class.getPackage().getName())) {
objectMapper.registerSubtypes(ClassUtils.resolveClassName(candidate.getBeanClassName(), ClassUtils.getDefaultClassLoader()));
}
}
}
(Maybe this would be a nice default for RepositoryRestMvcConfiguration ?)
So I can do:
GET http://localhost:8080/webapp/messages
{
"_links": { ... },
"_embedded": {
"messages": [
{
"type": "TEXT",
"_links": {
"self": {
"href": "http://localhost:8080/webapp/messages/b58a57bd-eb63-49e6-94c9-8ea92ffa193d{?projection}",
"templated": true
}
}
},
{
"type": "TODO",
"_links": {
"self": {
"href": "http://localhost:8080/webapp/messages/f50c48ee-3e40-462c-a2c2-49add268ead0{?projection}",
"templated": true
}
}
}
]
},
"page": { ... }
}
POST http://localhost:8080/webapp/messages
{
"type":"TEXT"
}
POSTing data this way, inserts it into the database, but the HTTP response fails for the same reason as the GET request:
2014-07-02 12:06:19,599 ERROR o.s.d.rest.webmvc.AbstractRepositoryRestController: 171 - Cannot create self link for class TextMessage! No persistent entity found!
java.lang.IllegalArgumentException: Cannot create self link for class TextMessage! No persistent entity found!
at org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler.getSelfLinkFor(PersistentEntityResourceAssembler.java:81) ~[PersistentEntityResourceAssembler.class:na]
at org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler.toResource(PersistentEntityResourceAssembler.java:64) ~[PersistentEntityResourceAssembler.class:na]
at org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler.toResource(PersistentEntityResourceAssembler.java:32) ~[PersistentEntityResourceAssembler.class:na]
at org.springframework.data.web.PagedResourcesAssembler.createResource(PagedResourcesAssembler.java:144) ~[PagedResourcesAssembler.class:na]
at org.springframework.data.web.PagedResourcesAssembler.toResource(PagedResourcesAssembler.java:96) ~[PagedResourcesAssembler.class:na]
at org.springframework.data.rest.webmvc.AbstractRepositoryRestController.entitiesToResources(AbstractRepositoryRestController.java:214) ~[AbstractRepositoryRestController.class:na]
at org.springframework.data.rest.webmvc.AbstractRepositoryRestController.resultToResources(AbstractRepositoryRestController.java:201) ~[AbstractRepositoryRestController.class:na]
at org.springframework.data.rest.webmvc.RepositoryEntityController.getCollectionResource(RepositoryEntityController.java:168) ~[RepositoryEntityController.class:na]
—
Now I'll try to find out, how this RelProvider stuff you mentioned works and where to register it. I'll give feedback later.
—
EDIT: Now figured out how to write my own RelProvider (which was as easy as possible):
new RelProvider() {
@Override public boolean supports(Class<?> arg0) {
return arg0.isAssignableFrom(Message.class);
}
@Override public String getItemResourceRelFor(Class<?> type) {
return "message";
}
@Override public String getCollectionResourceRelFor(Class<?> type) {
return "messages";
}
};
But I don't know how/where to register it. Inside RepositoryRestMvcConfiguration you have @Autowired(required = false) RelProvider relProvider which seems to be an instance of DelegatingRelProvider. The only way to add a RelProvider there, is to use the constructor. But when I use Open call Hierarchy in Eclipse, it finds nothing!
Search goes on...
—
Here ( https://github.com/spring-projects/spring-hateoas ) it's said "automatically picks up all RelProvider implementations in the ApplicationContext and bundles them into a DelegatingRelProvider available for autowiring."
@EnableHypermediaSupport is enabled, because I use Spring Boot with @Import(RepositoryRestMvcConfig.class) (I only customized the Jackson thing stated above).
I did the following:
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MessageRelProvider implements RelProvider {
@Override public boolean supports(Class<?> arg0) {
return arg0.isAssignableFrom(Message.class);
}
@Override public String getItemResourceRelFor(Class<?> type) {
return "message";
}
@Override public String getCollectionResourceRelFor(Class<?> type) {
return "messages";
}
}
But when I call GET http://localhost:8080/webapp/messages, it still has "_embedded": {
"textMessages": [... instead of "_embedded": {
"messages": [...
Pablo Santiago commented
Just adding my 2 cents here:
arg0.isAssignableFrom(Message.class)
Was returning false for me. I changed it to:
org.apache.commons.lang3.ClassUtils.isAssignable(arg0, Message.class);
And now I get the correct information
Manuel Sousa commented
I used the above suggestion and it fixed the _embedded so that it would show all subclasses.
However in the _links it still uses the subclassname instead of the value returned by getItemResourceRelFor. Did this also happen for you / did you manage to fix it?
In my case this is breaking access from the hateoas links so I needed to create one repository for each subclass again, still better than nothing being able to access by the superclass
David Riccitelli commented
I was able to solve setting the path on the subclass repositories, e.g:
@RepositoryRestResource(exported = false, path = "messages")
public interface TextMessageRepo extends JpaRepository<TextMessage, UUID> { }
(...)
Firoz Fazil commented
A dirty fix. In the above example make the subclass repository private so that the names can be changed to the parents and the endpoints will still be exposed by the repository of the parent.
@RepositoryRestResource(exported = false, path = "messages")
interface TextMessageRepo extends JpaRepository<TextMessage, UUID> {
}
Mathias Ewald commented
I just tried that (using the RelProvider above and non-exported Repo suggested just before my comment here) and I am seeing some strange behavior.
- I start the application and it all looks good:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.4.RELEASE)
2019-04-28 12:45:04.733 INFO 42556 --- [ main] c.e.s.SdrInheritanceApplication : Starting SdrInheritanceApplication on mewald.local with PID 42556 (/Users/mewald/Downloads/sdr-inheritance/target/classes started by mewald in /Users/mewald/Downloads/sdr-inheritance)2019-04-28 12:45:04.735 INFO 42556 --- [ main] c.e.s.SdrInheritanceApplication : No active profile set, falling back to default profiles: default2019-04-28 12:45:05.264 INFO 42556 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.2019-04-28 12:45:05.308 INFO 42556 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 39ms. Found 3 repository interfaces.2019-04-28 12:45:05.553 INFO 42556 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$3dae109f] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)2019-04-28 12:45:05.567 INFO 42556 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.hateoas.config.HateoasConfiguration' of type [org.springframework.hateoas.config.HateoasConfiguration$$EnhancerBySpringCGLIB$$bd2e5dd1] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)2019-04-28 12:45:05.829 INFO 42556 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)2019-04-28 12:45:05.846 INFO 42556 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]2019-04-28 12:45:05.846 INFO 42556 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.17]2019-04-28 12:45:05.905 INFO 42556 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext2019-04-28 12:45:05.906 INFO 42556 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1139 ms2019-04-28 12:45:06.061 INFO 42556 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...2019-04-28 12:45:06.124 INFO 42556 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.2019-04-28 12:45:06.166 INFO 42556 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [ name: default ...]2019-04-28 12:45:06.290 INFO 42556 --- [ main] org.hibernate.Version : HHH000412: Hibernate Core {5.3.9.Final}2019-04-28 12:45:06.290 INFO 42556 --- [ main] org.hibernate.cfg.Environment : HHH000206: hibernate.properties not found2019-04-28 12:45:06.360 INFO 42556 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.0.4.Final}2019-04-28 12:45:06.432 INFO 42556 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect2019-04-28 12:45:06.496 WARN 42556 --- [ main] org.hibernate.cfg.AnnotationBinder : HHH000457: Joined inheritance hierarchy [com.example.sdrinheritance.Feedback] defined explicit @DiscriminatorColumn. Legacy Hibernate behavior was to ignore the @DiscriminatorColumn. However, as part of issue HHH-6911 we now apply the explicit @DiscriminatorColumn. If you would prefer the legacy behavior, enable the `hibernate.discriminator.ignore_explicit_for_joined` setting (hibernate.discriminator.ignore_explicit_for_joined=true)2019-04-28 12:45:06.810 INFO 42556 --- [ main] o.h.t.schema.internal.SchemaCreatorImpl : HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@11ede87f'2019-04-28 12:45:06.812 INFO 42556 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'2019-04-28 12:45:07.492 INFO 42556 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'2019-04-28 12:45:07.521 WARN 42556 --- [ main] aWebConfiguration$JpaWebMvcConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning2019-04-28 12:45:07.784 INFO 42556 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''2019-04-28 12:45:07.787 INFO 42556 --- [ main] c.e.s.SdrInheritanceApplication : Started SdrInheritanceApplication in 3.296 seconds (JVM running for 3.835)2019-04-28 12:45:07.816 INFO 42556 --- [ main] com.example.sdrinheritance.DataLoader : Feedbacks saved
- I curl the root of the application and it still looks good:
$ curl http://localhost:8080
{
"_links" : {
"feedbacks" : {
"href" : "http://localhost:8080/feedbacks"
},
"profile" : {
"href" : "http://localhost:8080/profile"
}
}
}
- I curl the feedbacks endpoint I would expect to see one instance of "Feedback" but I get this:
curl -v http://localhost:8080/feedbacks
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /feedbacks HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 404
< Content-Type: application/hal+json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sun, 28 Apr 2019 10:47:17 GMT
<
* Connection #0 to host localhost left intact
{"timestamp":"2019-04-28T10:47:17.196+0000","status":404,"error":"Not Found","message":"No message available","path":"/feedbacks"}
These are the logs I can see switching to DEBUG mode:
[2m2019-04-28 12:49:16.599[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[l-1 housekeeper][0;39m [36mcom.zaxxer.hikari.pool.HikariPool [0;39m [2m:[0;39m HikariPool-1 - Pool stats (total=10, active=0, idle=10, waiting=0)
[2m2019-04-28 12:49:16.607[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[8080-Acceptor-0][0;39m [36mo.apache.tomcat.util.threads.LimitLatch [0;39m [2m:[0;39m Counting up[http-nio-8080-Acceptor-0] latch=1
[2m2019-04-28 12:49:16.608[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.a.tomcat.util.net.SocketWrapperBase [0;39m [2m:[0;39m Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@3de12d42:org.apache.tomcat.util.net.NioChannel@1ad6e3b1:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:52893]], Read from buffer: [0]
[2m2019-04-28 12:49:16.608[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36morg.apache.tomcat.util.net.NioEndpoint [0;39m [2m:[0;39m Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@3de12d42:org.apache.tomcat.util.net.NioChannel@1ad6e3b1:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:52893]], Read direct from socket: [87]
[2m2019-04-28 12:49:16.608[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.a.coyote.http11.Http11InputBuffer [0;39m [2m:[0;39m Received [GET /feedbacks HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.54.0
Accept: */*
]
[2m2019-04-28 12:49:16.609[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.a.c.authenticator.AuthenticatorBase [0;39m [2m:[0;39m Security checking request GET /feedbacks
[2m2019-04-28 12:49:16.609[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36morg.apache.catalina.realm.RealmBase [0;39m [2m:[0;39m No applicable constraints defined
[2m2019-04-28 12:49:16.609[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.a.c.authenticator.AuthenticatorBase [0;39m [2m:[0;39m Not subject to any constraint
[2m2019-04-28 12:49:16.609[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36morg.apache.tomcat.util.http.Parameters [0;39m [2m:[0;39m Set encoding to UTF-8
[2m2019-04-28 12:49:16.610[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.s.web.servlet.DispatcherServlet [0;39m [2m:[0;39m GET "/feedbacks", parameters={}
[2m2019-04-28 12:49:16.614[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.s.w.s.handler.SimpleUrlHandlerMapping [0;39m [2m:[0;39m Mapped to ResourceHttpRequestHandler ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/", "/"]
[2m2019-04-28 12:49:16.614[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.j.s.OpenEntityManagerInViewInterceptor[0;39m [2m:[0;39m Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
[2m2019-04-28 12:49:16.616[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.s.w.s.r.ResourceHttpRequestHandler [0;39m [2m:[0;39m Resource not found
[2m2019-04-28 12:49:16.616[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.j.s.OpenEntityManagerInViewInterceptor[0;39m [2m:[0;39m Closing JPA EntityManager in OpenEntityManagerInViewInterceptor
[2m2019-04-28 12:49:16.616[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.s.web.servlet.DispatcherServlet [0;39m [2m:[0;39m Completed 404 NOT_FOUND
[2m2019-04-28 12:49:16.616[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.a.c.c.C.[Tomcat].[localhost] [0;39m [2m:[0;39m Processing ErrorPage[errorCode=0, location=/error]
[2m2019-04-28 12:49:16.619[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.s.web.servlet.DispatcherServlet [0;39m [2m:[0;39m "ERROR" dispatch for GET "/error", parameters={}
[2m2019-04-28 12:49:16.620[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36ms.w.s.m.m.a.RequestMappingHandlerMapping[0;39m [2m:[0;39m Mapped to public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
[2m2019-04-28 12:49:16.620[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.j.s.OpenEntityManagerInViewInterceptor[0;39m [2m:[0;39m Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
[2m2019-04-28 12:49:16.625[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.s.w.s.m.m.a.HttpEntityMethodProcessor [0;39m [2m:[0;39m Using 'application/hal+json', given [*/*] and supported [application/hal+json]
[2m2019-04-28 12:49:16.627[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.s.w.s.m.m.a.HttpEntityMethodProcessor [0;39m [2m:[0;39m Writing [{timestamp=Sun Apr 28 12:49:16 CEST 2019, status=404, error=Not Found, message=No message available, (truncated)...]
[2m2019-04-28 12:49:16.641[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.j.s.OpenEntityManagerInViewInterceptor[0;39m [2m:[0;39m Closing JPA EntityManager in OpenEntityManagerInViewInterceptor
[2m2019-04-28 12:49:16.641[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.s.web.servlet.DispatcherServlet [0;39m [2m:[0;39m Exiting from "ERROR" dispatch, status 404
[2m2019-04-28 12:49:16.643[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.a.c.c.C.[.[.[/].[dispatcherServlet] [0;39m [2m:[0;39m Disabling the response for further output
[2m2019-04-28 12:49:16.644[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.a.tomcat.util.net.SocketWrapperBase [0;39m [2m:[0;39m Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@3de12d42:org.apache.tomcat.util.net.NioChannel@1ad6e3b1:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:52893]], Read from buffer: [0]
[2m2019-04-28 12:49:16.646[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.apache.coyote.http11.Http11Processor [0;39m [2m:[0;39m Error parsing HTTP request header
java.io.EOFException: null
at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1206) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.read(NioEndpoint.java:1140) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
at org.apache.coyote.http11.Http11InputBuffer.fill(Http11InputBuffer.java:731) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:352) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:294) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.17.jar:9.0.17]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834) [tomcat-embed-core-9.0.17.jar:9.0.17]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415) [tomcat-embed-core-9.0.17.jar:9.0.17]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.17.jar:9.0.17]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_181]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_181]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.17.jar:9.0.17]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_181]
[2m2019-04-28 12:49:16.646[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.apache.coyote.http11.Http11Processor [0;39m [2m:[0;39m Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@3de12d42:org.apache.tomcat.util.net.NioChannel@1ad6e3b1:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:52893]], Status in: [OPEN_READ], State out: [CLOSED]
[2m2019-04-28 12:49:16.648[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.apache.tomcat.util.threads.LimitLatch [0;39m [2m:[0;39m Counting down[http-nio-8080-exec-2] latch=1
[2m2019-04-28 12:49:16.648[0;39m [32mDEBUG[0;39m [35m42736[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36morg.apache.tomcat.util.net.NioEndpoint [0;39m [2m:[0;39m Socket: [org.apache.tomcat.util.net.NioChannel@1ad6e3b1:java.nio.channels.SocketChannel[closed]] closed
I made this demo code public here for easier reference: https://github.com/mathias-ewald/sdr-inheritance
Did this work for anyone?
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
It seems like RelProvider has been renamed to LinkRelationProvider: https://docs.spring.io/spring-hateoas/docs/current/api/org/springframework/hateoas/server/LinkRelationProvider.html
Adding a new, exported=false repo in my case leads to 404 errors, since I have a repo at the same path that I do want to access.
However, digging into this a bit more, the logic to calculate the name in the links array is here, and this provides a hint: https://github.com/spring-projects/spring-data-rest/blob/5c764464cfd682e1fef93769c209f65b917b757c/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/TypeBasedCollectionResourceMapping.java#L73-L77
We can also chose our path segment by annotating the JPA Entity with @RestResource, without needing to create a new Repository interface. So the workaround that works for me is:
@Entity
@DiscriminatorValue("TEXT")
@JsonTypeName("TEXT")
+@RestResource(rel = "message", path = "messages")
public class TextMessage extends Message { ... }
@Entity
@DiscriminatorValue("TODO")
@JsonTypeName("TODO")
+@RestResource(rel = "message", path = "messages")
public class TodoMessage extends Message { ... }