The Coder
The Coder

Reputation: 4057

How to know the name of the resource from an Entity class, to build a Hateoas link to that resource?

Suppose I have two resources Person and Article

@Entity
@Table(name = "person")
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long person_id;
    private String firstName;
    private String lastName;

    @OneToMany(mappedBy="person", cascade=CascadeType.ALL)
    private List<Article> articles = new ArrayList<>();

}

@Entity
@Table(name="article")
public class Article {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    private String title;
    private String details;

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="person_id")
    private Person person;

}

I now want to add HATEOAS support to the response of the controller for which I am using org.springframework.hateoas.ResourceAssembler

public class PersonResourceAssembler implements ResourceAssembler<Person, Resource<Person>>  {

    private EntityLinks entityLinks;

    public UserJobResourceAssembler(EntityLinks entityLinks) {
        this.entityLinks = entityLinks;
    }

    @Override
    public Resource<Person> toResource(Person entity) {
        Resource<UserJob> resource = new Resource<>(entity);
        resource.add(
               entityLinks.linkFor(Person.class).withSelfRel()),
               entityLinks.linkFor(...logic...).withRel("articles")) //here I am hardcoding the relation link name i.e "article"
             );
        return resource;
        }
}

So, in above code the "article" is hardcoded for the link name, but I don't want to do it this way. I want it do in the way Spring-Data-REST handles it i.e for every relationship it auto detects the name of the variable used inside the Entity class e.g articles will be picked from Person and person will be picked from Article.

I have no idea how Spring-Data-REST handles it, but are there any readymade/custom solutions for this requirement?

Upvotes: 0

Views: 97

Answers (1)

Alan Hay
Alan Hay

Reputation: 23246

You can use the reflection API to introspect the entity and find associations. Something like:

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.persistence.OneToMany;

public class AssociationUtility {

    public static List<Field> getAssociatedFields(Object entity) {

        Stream<Field> fields = Arrays.stream(entity.getClass().getDeclaredFields());

        return fields.filter(field -> field.getAnnotation(OneToMany.class) 
                                  != null).collect(Collectors.toList());
    }

    public static void main(String[] args) {
        List<Field> fields = getAssociatedFields(new Customer());
        fields.stream().forEach(f -> System.out.println("Make a link for Class: "
                + ((ParameterizedType) f.getGenericType()).getActualTypeArguments()[0] 
                     + " with rel: " + f.getName()));

        System.exit(0);
    }
}

Upvotes: 1

Related Questions