Javaddict
Javaddict

Reputation: 251

JPA - How to query using Specification and @EmbeddedId?

I'm using a JPA query that uses a specification to retrieve entities. When I execute the query, I'm getting the error:

org.springframework.data.mapping.PropertyReferenceException: No property name found for type Task!

I've looked at the answers to similar questions that have been asked on this site previously & tried to model my code to follow the patterns that were recommended but the code is still failing.

When I step through the code with a debugger, the expanded path in the criteria builder is returning the embedded ID class, but when the specification is actually used in the query it looks like the attribute is being applied to the base entity class.

Am I missing something obvious?

Here is the entity class:

@Entity
@Table(name = "TASKS")
public class Task implements Serializable {

    @EmbeddedId
    private TaskId id;

    ...more attributes, getters and setters
}

Here is the embedded ID entity class:

@Embeddable
public class TaskId implements Serializable {

    @Column(name = "NAME", length = 100)
    private String name;

    ...more attributes, getters and setters
}

Here is the specification builder that matches on the embedded id 'name' attribute:

public class HasTaskNameSpec {

    private HasTaskNameSpec() {
    }

    public static Specification<Task> equals(String name) {
        return (root, query, criteriaBuilder) -> {
            return criteriaBuilder.equal(root.get("id").get("name"), taskName);
        };
    }

}

The query is executed on the repository as follows:

List<Task> results = taskRepository.findAll(HasTaskNameSpec.equals("foo"));

The repository itself is very simple:

public interface TaskRepository extends JpaRepository<Task, TaskId>, JpaSpecificationExecutor<Task> {
   List<Task> findByIdName(String name);
   Page<Task> findByIdName(String name, Pageable page);    
}

** EDIT added methods to repository as was suggested below **

Upvotes: 6

Views: 12250

Answers (3)

Hari Krishna
Hari Krishna

Reputation: 3548

root.get({embeddedIdName}).get({subPropertyName}) is used to query on embeddedId using specification.

@Embeddable
public class ProjectId implements Serializable{

    private static final long serialVersionUID = 1L;

    @Column(name = "PROJECT_NAME")
    private String projectName;

    @Column(name = "ORGANIZATION")
    private String organization;

    ......
    ......

}


@Entity
@Table(name = "projects")
public class Project {

    @EmbeddedId
    private ProjectId projectId;

    @Column(name = "STARTED_TIME")
    private Timestamp startedTime;

    @Column(name = "ACTIVE")
    private String active;

    @Column(name = "DESCRIPTION")
    private String description;

    ......
    ......
}

In the above snippet, ProjectId is an embedded id. To query on projectName, we should use below snippet.

expression = root.get("projectId").get("projectName");

Upvotes: 5

Javaddict
Javaddict

Reputation: 251

Ahh, the root cause was totally in our codebase. There was a sort order being specified on the page that didn't include the embedded "id" attribute. The above code works.

Upvotes: 4

AdamPillingTech
AdamPillingTech

Reputation: 466

Take a look at this link which has a similar query.

EmbbededId Lookup

The final answer suggests that you can add a method to your TaskRepository thus.

public interface TaskRepository extends JpaRepository<Task, TaskId>, JpaSpecificationExecutor<Task> {
   public List<Task> findByIdName(String name);
}

Upvotes: 3

Related Questions