简体   繁体   中英

Write Spring Specification with multiple inner join & other conditions

I am working on a project Spring and Java, generated using JHipster. I encounter a problem with the following items of Spring: Specification, Criteria, and filtering a list of items.

I have a functioning SQL query, than I want to "translate" as a Spring Specification. But, I don't find how to do it, as the query works on 3 tables, and has 2 conditions.

Basically, the work concerns 3 tables: contract, transfer & entry. A contract can be inside one or several transfers, and a transfer contains 1 to several contracts. An entry is the link between these two tables (an entry contains a contract_id and a transfer_id).

The needs is to use the specification to get a list of contracts which are linked to a not received transfer.

How can i write this?

I have already looked at several stackoverflow posts and questions, but I found answers for a join between only two tables, or how to write specifications on an entity.

Here, the query I want to translate:

SELECT c.* 
FROM contract AS c
LEFT JOIN entry AS e ON e.contract_id = c.id 
INNER JOIN transfer AS t ON t.id = e.transfer_id
AND t.status != 'RECEIVED'

Here, an example of the existing Contract Specification created by JHipster Here you can see how the JHipster specification are used as filter. I want to add the new specification inside the already existing ones

private Specification<Contract> createSpecification(ContractCriteria criteria) {
        Specification<Contract> specification = Specification.where(null);

        if (criteria == null) {
            return specification;
        }

        return specification.and(buildStringSpecification(criteria.getContractNumber(), Contract_.contractNumber))
            .and(buildSpecification(criteria.getStatus(), Contract_.status))
            .and(buildSpecification(
                criteria.getStoreCode(),
                root -> root.join(Contract_.store, JoinType.LEFT).get(Store_.code)));

Okay, so I think I understood more or less how your entities are designed. I created a quick project using the following JDL:

entity Contract {contractNumber String, status String}
entity Transfer {status String}
entity Entry {}

relationship OneToMany {
    Transfer{entries} to Entry{transfer},
    Contract{entries} to Entry{contract}
}

service all with serviceClass
filter all

This is not how I would have designed the entities, but this is how you have them on your project and also as succinct as I could manage.

After importing this JDL in a fresh jhipster project your requirement is to filter contracts by transfer status.

The first thing we need to do is create a new StringFilter in your ContractCriteria.java (my status is just a String for simplicity, if yours is an Enum then you need you create the corresponding enum filter).

ContractCriteria.java

public class ContractCriteria implements Serializable, Criteria {
// ...
    private StringFilter transferStatus;

    public ContractCriteria(ContractCriteria other){
        // ...
        this.transferStatus = other.transferStatus == null ? null : other.transferStatus.copy();
    }
// ...
    public StringFilter getTransferStatus() {
        return transferStatus;
    }

    public void setTransferStatus(StringFilter transferStatus) {
        this.transferStatus = transferStatus;
    }
// ...

Remember to add your new filter to the hashCode() and equals() too. Once the new filter is implemented you just have to use it in your query service.

ContractQueryService.java

    protected Specification<Contract> createSpecification(ContractCriteria criteria) {
        Specification<Contract> specification = Specification.where(null);
        if (criteria != null) {
            // ...
            if (criteria.getTransferStatus() != null) {
                specification = specification.and(buildSpecification(criteria.getTransferStatus(),
                    root -> root.join(Contract_.entries, JoinType.LEFT)
                                .join(Entry_.transfer, JoinType.INNER)
                                .get(Transfer_.status)));
            }
        }
        return specification;
    }

The three relevant entities are as follow:

  • Contract has a property: Set<Entry> entries
  • Entry has properties: Transfer transfer and Contract contract
  • Transfer has a property: Set<Entry> entries

For quick development, jhipster comes with Swagger so you can test all your APIs live (/admin/docs with admin privileges). I leave the client side to you :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM