I am implementing server side sorting with Spring Data REST and Spring Data JPA. The JPA entity I'd like to sort the results by many to one entiry budgetPool.name
@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "TYPE", discriminatorType =DiscriminatorType.STRING)
@Table(name = "PS_TRANSACTION")
@ActiveBudgetPoolValidation
public abstract class Transaction extends Auditable {
@Id
@Column(name = "ID", nullable = false, insertable = false, updatable = false)
@SequenceGenerator(name = "TransactionSeq", sequenceName = "SEQ_TRANSACTION", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TransactionSeq")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
protected Long id;
@NotNull
@JsonProperty(access = READ_WRITE)
@ManyToOne(optional = false)
@JoinColumn(name = "BUDGET_POOL_ID", referencedColumnName = "ID", updatable = false)
protected BudgetPool budgetPool;
I display budgetPool.name on UI with the option to sort the results by budget pool name. The following URL to sort budget pool by id work:
http://host:port/finance-service/transactions/search/approvals?page=0&size=10&sort=budgetPool,desc
But the following url to sort by budget pool name throws java.sql.SQLSyntaxErrorException: invalid ORDER BY expression:
http://host:port/finance-service/transactions/search/approvals?page=0&size=10&sort=budgetPool.name,desc
My Repository has a custom query to return paged transactions based on the search parameters passed.
@RepositoryRestResource(excerptProjection = TransactionWithInlineProductControlAction.class, collectionResourceRel = "transactions")
public interface TransactionRepository<T extends Transaction> extends JpaRepository<T, Long>, JpaSpecificationExecutor<T> {
@RestResource(path = "approvals", rel = "approvals", description = @Description("Show approval transactions for the given status, orderId and budgetPoolId (paged)"))
@Query("SELECT DISTINCT t " +
"FROM Transaction t " +
"JOIN t.budgetPool bp " +
"LEFT JOIN bp.budgetApprovers ba " +
"LEFT JOIN bp.technicalApprovers ta " +
"WHERE (t.status = :status OR :status IS NULL) " +
"AND (t.autoApproved = false OR t.autoApproved IS NULL) " +
"AND (t.orderId LIKE %:orderId% OR :orderId IS NULL) " +
"AND (bp.id=:budgetPoolId OR :budgetPoolId IS NULL) " +
"AND (" +
"ta.email LIKE ?#{hasRole('ROLE_ADMIN') || hasRole('ROLE_SERVICE_ACCOUNT') ? '%' : principal.username} " +
"OR ba.email LIKE ?#{hasRole('ROLE_ADMIN') || hasRole('ROLE_SERVICE_ACCOUNT') ? '%' : principal.username} " +
"OR 1=?#{hasRole('ROLE_ADMIN') || hasRole('ROLE_COMMERCIAL_MANAGEMENT') || hasRole('ROLE_PRODUCT_MANAGEMENT') ? 1 : 0}" +
")"
)
Page<T> findByStatusAndBudgetPool(
@Param("status") TransactionStatus status,
@Param("orderId") String orderId,
@Param("budgetPoolId") Long budgetPoolId,
Pageable p
);
}
If I remove DISTINCT the sorting by bugetPool.name work fine but I get duplicate results in my query.
Here is the underlying query JPA/Hibernate makes:
select distinct transactio0_.ID as ID2_10_, transactio0_.LAST_MODIFIED_DATE as LAST_MOD3_10_, transactio0_.VERSION as VERSION4_10_, transactio0_.COMMENTS as COMMENTS5_10_,
transactio0_.CREATED_BY as CREATED_6_10_, transactio0_.CREATED_DATE as CREATED_7_10_, transactio0_.LAST_MODIFIED_BY as LAST_MOD8_10_, transactio0_.APPROVED as APPROVED9_10_,
transactio0_.AUTO_APPROVED as AUTO_AP10_10_, transactio0_.BUDGET_POOL_ID as BUDGET_23_10_, transactio0_.CLARITY_ID as CLARITY11_10_, transactio0_.CLARITY_PROJECT_NAME as CLARITY12_10_,
transactio0_.PRODUCT_CONTROL_ACTION_ID as PRODUCT24_10_, transactio0_.AMOUNT as AMOUNT13_10_, transactio0_.DESCRIPTION as DESCRIP14_10_, transactio0_.INITIAL_COST as INITIAL15_10_,
transactio0_.NAR_INSTANCE_ID as NAR_INS16_10_, transactio0_.NAR_INSTANCE_NAME as NAR_INS17_10_, transactio0_.NEXT_YEAR_COST as NEXT_YE18_10_, transactio0_.ORDER_ID as ORDER_I19_10_,
transactio0_.REFERENCE_LINK as REFEREN20_10_, transactio0_.REQUESTOR as REQUEST21_10_, transactio0_.STATUS as STATUS22_10_, transactio0_.TYPE as TYPE1_10_
from PS_TRANSACTION transactio0_
inner join PS_BUDGET_POOL budgetpool1_ on transactio0_.BUDGET_POOL_ID=budgetpool1_.ID
left outer join PS_PARTICIPANT budgetappr2_ on budgetpool1_.ID=budgetappr2_.BUDGET_POOL_ID
and ( budgetappr2_.TYPE = 'BUDGET' AND budgetappr2_.ACTIVE=1)
and budgetappr2_.TYPE='BUDGET'
left outer join PS_PARTICIPANT technicala3_ on budgetpool1_.ID=technicala3_.BUDGET_POOL_ID
and ( technicala3_.TYPE = 'TECHNICAL' AND technicala3_.ACTIVE=1)
and technicala3_.TYPE='TECHNICAL' where 1=1 and 1=1 and 1=1
and (transactio0_.AUTO_APPROVED is null or transactio0_.AUTO_APPROVED=false)
and (budgetappr2_.EMAIL like ? or technicala3_.EMAIL like ? or 1=1)
order by budgetpool1_.NAME desc limit ?
Above query works fine by adding budgetpool1_.NAME in the select statement. Perhaps the solution could be telling JPA/Hibernate to include budget pool name in the top level entity but I couldn't succeed in doing that.
I have searched through different forums but could not find the answer. I appreciate your help in advance.
I appreciate your help in advance.