4

I am trying to use Spring JPA's projection to filter out unnecessary data from query result. However, I have multiple projections that will need to be used on the same interface method.

The problem is, I am trying to query data from the same method with a different returning object but java doesn't allowed this.

The query are auto generated by JPA based on method name, so I cannot make changes to method name.

Is there a alternative, other than creating a new interface, since I think it's a hassle and unnecessary

here is a sample code, of what I am trying to do.

Auto-Generated Query

public interface UserRepository extends CrudRepository<UserAccount, Long> {

    AuthenticateProjection getByUsername(String username);

    UserDetailsProjection getByUsername(String username);

}

Projections

public interface AuthenticateProjection {

    @Value("#{target.username}")
    String getUsername();

    @Value("#{target.credentail.token}")
    String getHashPassword();
}

public interface UserDetailsProjection {

    @Value("#{target.username}")
    String getUsername();

    @Value("#{target.firstname}")
    String getFirstName();

    @Value("#{target.lastname}")
    String getLastName();
}
XPLOT1ON
  • 2,994
  • 2
  • 20
  • 36

2 Answers2

7

So I've managed to figure out how to use multiple projections with a single query.

<T> T getByUsername(String username, Class<T> projection)

This allows the method caller to specified the type of projection to be applied to the query.

To further improve this so it is less prone to error, I made a blank interface that the projection will have to extend in order to be able to insert class into the parameter.

public interface JPAProjection {
}

public interface UserRepository extends CrudRepository<UserAccount, Long> {
    <T extends JPAProjection > T getByUsername(String username, Class<? extends JPAProjection> projection);
}

Projection Interface

public interface UserDetailsProjection extends JPAProjection{
    @Value("#{target.username}")
    String getUsername();

    @Value("#{target.firstname}")
    String getFirstname();

    @Value("#{target.lastname}")
    String getLastname();
}

Then I can call the query method by

getByUsername("...", UserDetailsProjection.class)
XPLOT1ON
  • 2,994
  • 2
  • 20
  • 36
  • I tried to use your same solution, but it does not work for me. In the Eclipse console I see an error: `Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: from near line 1, column 9 [select from com.app.company.domain.organization.Organization as generatedAlias0 where generatedAlias0.deletedAt is null]`. Look at the "select" statement: there is not any column. Maybe did you have the same problem? It seems that the repository does not receive the correct projection type.....any idea? Thanks a lot! – Andrea Bevilacqua Feb 08 '18 at 14:27
  • hmm this is odd. I did not have any problem. Would you mind attaching your impl? Pastebin maybe? @AndreaBevilacqua – XPLOT1ON Feb 08 '18 at 15:02
3

Just add something between get (or e.g. find) and By starting with an upper case character. It is ignored in the query generation.

public interface UserRepository extends CrudRepository<UserAccount, Long> {

   AuthenticateProjection getByUsername(String username);

   UserDetailsProjection getAnotherByUsername(String username);

}
user1056903
  • 921
  • 9
  • 26
  • Best answer ! No code to write, you may only write the "mode" of the data you want to retrieve, like : getByUsername, getFullByUsername, getLightByUsername. – Frédéric Nobre May 17 '22 at 14:13