如何在JPA EclipseLink中调用无主键实体类并实现非ID字段查询?
Hey there! Let's work through this query problem you're facing with your Header and LineItem entities. It's super frustrating when you're stuck only being able to fetch LineItems via Header ID—let's get you set up to query by other fields like name or customer account.
First, let's make sure your entity mappings are solid (sometimes wonky mappings can block proper queries). Here's a quick example of how your entities should look with correct OneToMany/ManyToOne associations:
Example Entity Mappings
// Header.java @Entity public class Header { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String customerAccount; // The customer account field you want to query by @OneToMany(mappedBy = "header", fetch = FetchType.LAZY, cascade = CascadeType.ALL) private List<LineItem> lineItems = new ArrayList<>(); // Getters, setters, constructors } // LineItem.java @Entity public class LineItem { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; // The name field you want to query by @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "header_id") // Make sure this matches your DB foreign key column private Header header; // Getters, setters, constructors }
Now, let's cover the most common ways to run your desired queries:
1. JPQL Queries (Most Straightforward)
JPQL lets you write queries using your entity class names and property names (not database column names—this is a common gotcha!).
Query LineItem by Name
EntityManager em = // Get your EntityManager instance String jpql = "SELECT li FROM LineItem li WHERE li.name = :lineItemName"; TypedQuery<LineItem> query = em.createQuery(jpql, LineItem.class); query.setParameter("lineItemName", "Your Target Name"); List<LineItem> results = query.getResultList();
Query LineItem by Header's Customer Account
Since customer account lives on Header, you'll join the two entities in your JPQL:
String jpql = "SELECT li FROM LineItem li JOIN li.header h WHERE h.customerAccount = :accountNumber"; TypedQuery<LineItem> query = em.createQuery(jpql, LineItem.class); query.setParameter("accountNumber", "CUST-12345"); List<LineItem> results = query.getResultList();
2. Criteria API (Great for Dynamic Queries)
If you need to build queries on the fly (e.g., optional filters for name AND/OR customer account), the Criteria API is perfect:
Example: Dynamic Filter for Name or Customer Account
EntityManager em = // Get your EntityManager instance CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<LineItem> cq = cb.createQuery(LineItem.class); Root<LineItem> lineItemRoot = cq.from(LineItem.class); // Build predicates for your filters List<Predicate> predicates = new ArrayList<>(); // Add filter for name if needed String targetName = "Widget"; if (targetName != null) { predicates.add(cb.equal(lineItemRoot.get("name"), targetName)); } // Add filter for customer account if needed String targetAccount = "CUST-12345"; if (targetAccount != null) { Join<LineItem, Header> headerJoin = lineItemRoot.join("header"); predicates.add(cb.equal(headerJoin.get("customerAccount"), targetAccount)); } cq.where(cb.and(predicates.toArray(new Predicate[0]))); List<LineItem> results = em.createQuery(cq).getResultList();
3. Spring Data JPA Method Name Queries (If You're Using Spring)
If your project uses Spring Data JPA, you can define query methods directly in your repository interface—no need to write JPQL:
public interface LineItemRepository extends JpaRepository<LineItem, Long> { // Find LineItems by name List<LineItem> findByName(String name); // Find LineItems by Header's customer account List<LineItem> findByHeaderCustomerAccount(String customerAccount); // Combine filters (e.g., name AND customer account) List<LineItem> findByNameAndHeaderCustomerAccount(String name, String customerAccount); }
Quick Troubleshooting Tips
- Double-check that you're using entity property names (not database column names) in your queries. For example, if your LineItem's name field is called
itemNamein the entity, use that instead ofitem_name. - If you're getting lazy loading exceptions when accessing Header fields, make sure your query joins the Header entity (like in the JPQL example above) or use
FetchType.EAGER(though EAGER fetching isn't always ideal—join fetch is better for performance). - Verify that your EntityManager is properly configured and that you're committing transactions if needed (for write operations, but queries usually don't require it unless you're using extended persistence contexts).
内容的提问来源于stack exchange,提问作者user9454796

