Spring Boot + JPA: Querying Specific Columns of a Composite Primary Key (@EmbeddedId)
When working with Spring Boot and JPA, you may encounter situations where a
composite primary key (Composite Key) is required. In particular, when using
@EmbeddedId
, you might need to query specific columns
of the composite key. This post provides a detailed explanation of how to
achieve this with practical examples and code.
1. Composite Primary Key and @EmbeddedId
A composite primary key consists of two or more columns that together
uniquely identify a record in a table. In JPA, composite primary keys can be
mapped using @EmbeddedId
or
@IdClass
. This article focuses on the
@EmbeddedId
approach.
Defining the Composite Key Class
To represent a composite key, you must define a class annotated with
@Embeddable
. This class should implement the
Serializable
interface.
import jakarta.persistence.Embeddable;
import java.io.Serializable;
import java.util.Objects;
@Embeddable
public class CompositeKey implements Serializable {
private String convsId;
private String anotherId;
// Default constructor
public CompositeKey() {}
// Constructor
public CompositeKey(String convsId, String anotherId) {
this.convsId = convsId;
this.anotherId = anotherId;
}
// Getters and Setters
public String getConvsId() {
return convsId;
}
public void setConvsId(String convsId) {
this.convsId = convsId;
}
public String getAnotherId() {
return anotherId;
}
public void setAnotherId(String anotherId) {
this.anotherId = anotherId;
}
// equals & hashCode methods
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CompositeKey that = (CompositeKey) o;
return Objects.equals(convsId, that.convsId) && Objects.equals(anotherId, that.anotherId);
}
@Override
public int hashCode() {
return Objects.hash(convsId, anotherId);
}
}
Defining the Entity Class
The entity class that uses the composite key should reference the key class
using the @EmbeddedId
annotation.
import jakarta.persistence.*;
@Entity
public class MyEntity {
@EmbeddedId
private CompositeKey id;
private String someData;
// Default constructor
public MyEntity() {}
// Constructor
public MyEntity(CompositeKey id, String someData) {
this.id = id;
this.someData = someData;
}
// Getters and Setters
public CompositeKey getId() {
return id;
}
public void setId(CompositeKey id) {
this.id = id;
}
public String getSomeData() {
return someData;
}
public void setSomeData(String someData) {
this.someData = someData;
}
}
2. Querying Specific Columns of the Composite Key
When using @EmbeddedId
, querying a specific column
of the composite key can be achieved through the following methods.
Method 1: Using JPQL
In JPQL, you can access specific fields of the composite key using path navigation.
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface MyEntityRepository extends CrudRepository<MyEntity, CompositeKey> {
@Query("SELECT e FROM MyEntity e WHERE e.id.convsId = :convsId")
List<MyEntity> findByConvsId(String convsId);
}
Example Usage
@Autowired
private MyEntityRepository myEntityRepository;
public void testQuery() {
String convsId = "12345";
List<MyEntity> results = myEntityRepository.findByConvsId(convsId);
results.forEach(System.out::println);
}
Method 2: Using Criteria API
The Criteria API provides a dynamic way to build queries programmatically.
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.criteria.*;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class MyEntityCriteriaRepository {
@PersistenceContext
private EntityManager entityManager;
public List<MyEntity> findByConvsId(String convsId) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<MyEntity> query = cb.createQuery(MyEntity.class);
Root<MyEntity> root = query.from(MyEntity.class);
// Adding the condition on convsId
Predicate condition = cb.equal(root.get("id").get("convsId"), convsId);
query.where(condition);
return entityManager.createQuery(query).getResultList();
}
}
Example Usage
@Autowired
private MyEntityCriteriaRepository criteriaRepository;
public void testCriteria() {
String convsId = "12345";
List<MyEntity> results = criteriaRepository.findByConvsId(convsId);
results.forEach(System.out::println);
}
Method 3: Using Example Matcher
Spring Data JPA’s Example
matcher simplifies
querying for specific conditions.
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.repository.CrudRepository;
public interface MyEntityRepository extends CrudRepository<MyEntity, CompositeKey> {
List<MyEntity> findAll(Example<MyEntity> example);
}
Example Usage
@Autowired
private MyEntityRepository myEntityRepository;
public void testExample() {
CompositeKey key = new CompositeKey();
key.setConvsId("12345");
MyEntity probe = new MyEntity();
probe.setId(key);
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("id.convsId", ExampleMatcher.GenericPropertyMatchers.exact());
Example<MyEntity> example = Example.of(probe, matcher);
List<MyEntity> results = myEntityRepository.findAll(example);
results.forEach(System.out::println);
}
3. Choosing the Right Method
- For simple queries: Use JPQL for clarity and simplicity.
- For dynamic queries: The Criteria API is best suited for scenarios requiring multiple or flexible conditions.
-
For example-based queries: Use
Example
objects for straightforward use cases with sample data.
Summary
-
When working with
@EmbeddedId
, querying specific columns of a composite key is achievable through JPQL, Criteria API, or Example objects. - Choose the most suitable approach based on your query’s complexity and requirements.
I hope this guide helps you effectively manage composite keys in your JPA projects. Feel free to leave comments for further clarifications!
Comments
Post a Comment