2
votes

I am using spring boot 1.3.3 with starter-data-jpa + I am using specification-arg-resolver for combine multiple filters in JPA Repo

I have a filed call status attribute in entity which is matching to the database status id, nevertheless, we want search by status name, I defined all the status name in hashMap of the entity it is like following code

However when I try to filter by status name I got

 Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: Unable to locate Attribute  with the the given name [statusName] on this ManagedType [api.domain.Inscription]; nested exception is java.lang.IllegalArgumentException: Unable to locate Attribute  with the the given name [statusName] on this ManagedType [api.domain.Inscription]] with root cause

java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [statusName] on this ManagedType [api.domain.Inscription]

Entity class:

@Entity
@Table(name="INSCRIPTIONS")
public class Inscription {
    private static final Map<Byte, String> STATUS = new HashMap<Byte, String>(){{
       put((byte) 1, "sale");
       put((byte) 3, "rent");
    }};

    private byte status;

    @Transient
    private String statusName;

    public byte getStatus() {
        return this.status;
    }

    public void setStatus(byte status) {
        this.status = status;
    }

    public String getStatusName() {
        return STATUS.get(this.status);
    }

    public void setStatusName(byte status) {
        this.statusName = STATUS.get(status);
    }
}

Controller Class:

 public class InscriptionController
 {
     @Autowired
     private InscriptionRepository inscriptionRepository;

     @RequestMapping(method = RequestMethod.GET)
     public Page<Inscription> findAll(
     final @RequestParam(defaultValue = "0", required = false) int offset,
     final @RequestParam(defaultValue = "20", required = false) int limit,
     @Spec(params = "status",  path = "statusName", spec = In.class) Specification<Inscription> spec
 )
 {
     Page<Inscription> inscriptions = inscriptionRepository.findAll(spec, new PageRequest(offset, limit));
     return inscriptions;
 }

Repo Class:

public interface InscriptionRepository extends PagingAndSortingRepository<Inscription, String>, JpaSpecificationExecutor<Inscription> { }
2
Why do you put @Transient on it when you don't want it? - mh-dev
@mh-dev Because statusName is not exist in table, status is exist but it is the id of status, I would like to filter by the status name. - zt1983811

2 Answers

2
votes

@Transient annotation is used to indicate that a field is not to be persisted in the database, which means it has no equivalent in the database and you can't query over it.

And since statutName is related to status which is not transiet , you can use status instead.

2
votes

Thanks for @Rafik BELDI I did it with specification, hope this can help anyone have the same problem a little bit.

Spec class

public class InscriptionSpecification
{
     public static Specification<Inscription> statusNameIn(String[] statusNames) {
        if (statusNames == null || statusNames.length == 0) {
            return null;
        }
        return (root, query, cb) -> {
            Inscription inscription = new Inscription();
            List<Byte> statusIds = new ArrayList<Byte>();
            for (String oneStatusName : statusNames) {
                statusIds.add(inscription.getStatusIdByName(oneStatusName));
            }
            return cb.isTrue(root.<String>get("status").in(statusIds));
        };
    }
}

Controller class

   @RequestMapping(method = RequestMethod.GET)
   public Page<Inscription> findAll(
       final @RequestParam(defaultValue = "0", required = false) int offset,
       final @RequestParam(defaultValue = "20", required = false) int limit,
       final @RequestParam(required = false) String[] status
   )
   {
       Specification<Inscription> specStatusNameIn = InscriptionSpecification.statusNameIn(status);
       inscriptionRepository.findAll(specStatusNameIn, new PageRequest(offset, limit));
       return inscriptions;
   }
}