1

I am using Spring Boot (2.1.1) to automatically create an HAL REST API of my JpaRepository interfaces.

For most cases these interfaces are empty, for example:

public interface ProjectRepository extends JpaRepository<Project, Long> {}

public interface ProtocolRepository extends JpaRepository<Protocol, Long> {}

A Project-entity contains many Protocol-entities. And a Protocol-entity has a back-link to its Project-entity.

When I visit http://localhost:8080/admin/protocols/4711 I get the link to its project:

...
"project": {
  "href": "http://localhost:8080/admin/protocols/4711/project"
}
...

But when I follow that link all further links are generated faulty:

  ...
  "_links": {
    "self": {
      "href": "http://localhost:8080/admin/project/1"
    },
    "project": {
      "href": "http://localhost:8080/admin/project/1"
    }
  ...
  }
  ...

The error in the link is that the singular noun project is used, instead of the plural form projects.

As these links are automatically generated, it is not obvious how this behavior can be changed.

gillesB
  • 1,061
  • 1
  • 14
  • 30

1 Answers1

1

While debugging the Spring internals I figured out that PersistentEntityResourceAssembler uses an instance of DefaultSelfLinkProvider to create the self-links. When I debugged that class I realized that it does not work correctly when a self link is generated for an object which is proxied by Hibernate.

Therefore I tried to replace the DefaultSelfLinkProvider by my own implementation of the SelfLinkProvider-interface.

This can be done with an BeanPostProcessor:

  @Bean
  public BeanPostProcessor entityManagerBeanPostProcessor()
  {
    return new BeanPostProcessor()
    {
      @Override
      public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
      {
        return bean;
      }

      @Override
      public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
      {
        if (bean instanceof SelfLinkProvider)
        { return new HibernateSelfLinkProvider((SelfLinkProvider) bean); }
        return bean;
      }
    };
  }

And the HibernateSelfLinkProvider is a simple wrapper around a SelfLinkProvider:

public class HibernateSelfLinkProvider implements SelfLinkProvider
{
  private final SelfLinkProvider selfLinkProvider;

  public HibernateSelfLinkProvider(SelfLinkProvider selfLinkProvider)
  {
    this.selfLinkProvider = selfLinkProvider;
  }

  @Override
  public Link createSelfLinkFor(Object instance)
  {
    instance = Hibernate.unproxy(instance);
    return selfLinkProvider.createSelfLinkFor(instance);
  }
}

The nice thing about Hibernate.unproxy() is that it leaves the given object unchanged, if it is not a proxied object.

With this addition I get the correct link: "http://localhost:8080/admin/projects/1". But I am not sure if this is the best place to modify the behavior.

gillesB
  • 1,061
  • 1
  • 14
  • 30