Search code examples
enumsspring-data-jpaspring-el

Spring Data JPA, Query class with enum attributes


public class RedeemCodeQuery {
private String code;
private RedeemCode.CodeStatus status;
private String exportStatus;
public static enum CodeStatus {
    INIT,USED
}

public static enum ExportStatus {
    DEFAULT,EXPORTED
}
//getter setter...
}

 String sqlByQuery = """
         select rc from RedeemCode rc 
        where (:#{#query.code} is null or rc.code = :#{#query.code} )
        and ( :status is null or rc.status = :status )
        and ( :#{#query.exportStatus} is null or rc.exportStatus = :#{#query.exportStatus} )
        """;
@Query(sqlByQuery)
Page<RedeemCode> pageBy(@Param("query") RedeemCodeQuery query, @Param("status") CodeStatus status, Pageable pageable);

rc.code = :#{#query.code}: working , code is type of String and inside the RedeemCodeQuery class;

rc.status = :status : working, status is type of enum.

rc.exportStatus = :#{#query.exportStatus}: error , exportStatus is type of enum and inside the RedeemCodeQuery class.

Spring SpEL may change the :#{#query.exportStatus} to some other type. Is possible to put an enum inside a class for query parameter? How to do that?


Solution

  • You have two options here:

    1. either use Enum.name() to get the String representation of rc.exportStatus field for comparison with String rc.exportStatus parameter:
    String sqlByQuery = """
             select rc from RedeemCode rc 
            where (:#{#query.code} is null or rc.code = :#{#query.code} )
            and ( :status is null or rc.status = :status )
            and ( :#{#query.exportStatus} is null or rc.exportStatus.name() = :#{#query.exportStatus} )
            """;
    @Query(sqlByQuery)
    Page<RedeemCode> pageBy(@Param("query") RedeemCodeQuery query, @Param("status") CodeStatus status, Pageable pageable);
    
    1. or use special T() operator with Enum.valueOf() for conversion of String rc.exportStatus field into member of ExportStatus enum:
    String sqlByQuery = """
             select rc from RedeemCode rc 
            where (:#{#query.code} is null or rc.code = :#{#query.code} )
            and ( :status is null or rc.status = :status )
            and ( :#{#query.exportStatus} is null or rc.exportStatus = :#{T(full.path.to.ExportStatus).valueOf(#query.exportStatus)})
            """;
    @Query(sqlByQuery)
    Page<RedeemCode> pageBy(@Param("query") RedeemCodeQuery query, @Param("status") CodeStatus status, Pageable pageable);
    
    1. or use the dirty hack: SpEL expression #query.exportStatus assumes there's a getter for exportStatus field, so you could modify it to return ExportStatus instead of String keeping your initial query:
    public class RedeemCodeQuery {
    private String code;
    private RedeemCode.CodeStatus status;
    private String exportStatus;
    
    public static enum ExportStatus {
        DEFAULT,EXPORTED
    }
    
    public ExportStatus getExportStatus() {
      return ExportStatus.valueOf(exportStatus);
    }
    

    This would give you desirable type at runtime.