Hibernate Criteria查询:多对一忽略列及关联实体字段屏蔽
Alright, let's break down these two Hibernate Criteria questions step by step—they’re super common when you want to avoid pulling in sensitive or unnecessary data!
问题1:在Hibernate Criteria多对一查询场景下,检索数据时如何忽略指定列?
When dealing with a many-to-one association and needing to exclude specific columns from the associated entity, your core goal is to control which fields Hibernate fetches (since Criteria defaults to loading all columns of mapped entities). Here are three reliable approaches:
1. Use Projections to Explicitly List Needed Fields
If you don’t need full entity objects (e.g., just a subset of data), use Projections to define exactly which columns to retrieve—including fields from the associated entity, omitting the ones you don’t want.
Example (say Order has a many-to-one relationship with User, and we want to skip User.password):
Criteria criteria = session.createCriteria(Order.class, "order"); criteria.createAlias("order.user", "user"); // Join the associated User entity // Define the projection list: include only what you need ProjectionList projList = Projections.projectionList() .add(Projections.property("order.id"), "orderId") .add(Projections.property("order.orderNumber"), "orderNumber") .add(Projections.property("user.id"), "userId") .add(Projections.property("user.username"), "username"); // Notice we don't add user.password here // Map results to a DTO for clean access criteria.setProjection(projList); criteria.setResultTransformer(Transformers.aliasToBean(OrderUserDTO.class)); List<OrderUserDTO> results = criteria.list();
2. Fetch + Projection for Partial Entity Loading
If you need the full main entity but only a subset of the associated entity’s fields, combine fetching with projections. Note: This leaves the associated entity partially initialized, so use it only for read-only scenarios.
Criteria criteria = session.createCriteria(Order.class, "order"); // Ensure the associated User is fetched via join criteria.setFetchMode("order.user", FetchMode.JOIN); // Define projections for both entities, excluding the unwanted column criteria.createAlias("order.user", "user") .setProjection(Projections.projectionList() // Include all Order fields (list them explicitly) .add(Projections.property("order.id"), "id") .add(Projections.property("order.orderNumber"), "orderNumber") // Include User fields except password .add(Projections.property("user.id"), "user.id") .add(Projections.property("user.username"), "user.username") ); // Transform results back to Order entities (partial User inside) criteria.setResultTransformer(new AliasToEntityMapResultTransformer()); List<Order> results = criteria.list();
3. Named Entity Graphs (Hibernate 5+)
For cleaner, type-safe partial loading (and full entity objects), use @NamedEntityGraph to define which fields to load, then reference it in your Criteria query.
First, add the entity graph to your associated entity (e.g., User):
@Entity @NamedEntityGraph(name = "UserWithoutPassword", attributeNodes = { @NamedAttributeNode("id"), @NamedAttributeNode("username") }) public class User { // ... entity fields including password }
Then use it in your Criteria query:
CriteriaBuilder cb = session.getCriteriaBuilder(); CriteriaQuery<Order> cq = cb.createQuery(Order.class); Root<Order> orderRoot = cq.from(Order.class); // Fetch User and apply the entity graph to exclude password orderRoot.fetch("user", JoinType.INNER) .fetchGraph(session.getEntityGraph("UserWithoutPassword")); cq.select(orderRoot); List<Order> results = session.createQuery(cq).getResultList();
问题2:检索单条Ticket记录,关联返回的Employee数据中不包含password字段,如何编写Criteria查询?
Given your Employee -> Ticket one-to-many relationship (so Ticket has a many-to-one employee field), here are two tailored solutions:
方法1:Projections for Precise Field Control
Use this if you want to explicitly define every field to retrieve, either mapping to a DTO or a partial Ticket entity:
Session session = sessionFactory.getCurrentSession(); Criteria criteria = session.createCriteria(Ticket.class, "ticket"); // Join the associated Employee entity criteria.createAlias("ticket.employee", "emp"); // Build projection list: include all Ticket fields, plus Employee fields except password ProjectionList projList = Projections.projectionList(); // Add all Ticket fields (list them explicitly) projList.add(Projections.property("ticket.id"), "id"); projList.add(Projections.property("ticket.title"), "title"); projList.add(Projections.property("ticket.content"), "content"); // ... add other Ticket fields as needed // Add Employee fields, skipping password projList.add(Projections.property("emp.id"), "employee.id"); projList.add(Projections.property("emp.email"), "employee.email"); projList.add(Projections.property("emp.name"), "employee.name"); // ... add other Employee fields except emp.password // Apply projection and transform to Ticket entity (read-only!) criteria.setProjection(projList); criteria.setResultTransformer(Transformers.aliasToBean(Ticket.class)); // Fetch the single Ticket record Ticket ticket = (Ticket) criteria.uniqueResult();
方法2:Named Entity Graph (Cleaner, Full Ticket Entity)
This is the most maintainable approach if you’re on Hibernate 5+. First, update your Employee entity with a named graph excluding password:
@Entity @NamedQuery(name = "getUserByEmail", query = "from Employee where email = :email") @NamedEntityGraph(name = "EmployeeWithoutPassword", attributeNodes = { @NamedAttributeNode("id"), @NamedAttributeNode("email"), @NamedAttributeNode("name") // List all Employee fields you need; omit password }) public class Employee { @Id private Long id; private String email; private String name; private String password; @OneToMany(mappedBy = "employee") private List<Ticket> tickets; // Getters and setters }
Then write the Criteria query to fetch the Ticket with the filtered Employee data:
Session session = sessionFactory.getCurrentSession(); CriteriaBuilder cb = session.getCriteriaBuilder(); CriteriaQuery<Ticket> cq = cb.createQuery(Ticket.class); Root<Ticket> ticketRoot = cq.from(Ticket.class); // Fetch Employee and apply the entity graph to exclude password Fetch<Ticket, Employee> employeeFetch = ticketRoot.fetch("employee", JoinType.INNER); employeeFetch.fetchGraph(session.getEntityGraph("EmployeeWithoutPassword")); // Add condition to fetch the specific Ticket (e.g., by ID) cq.where(cb.equal(ticketRoot.get("id"), yourTicketId)); // Execute the query to get the single Ticket Ticket ticket = session.createQuery(cq).uniqueResult();
Quick Notes:
- If you’re stuck on Hibernate 3.x, skip the entity graph approach and use projections instead.
- Partial entities (from projections) should be used for read operations only—don’t update them, as unloaded fields will overwrite data with nulls.
- While
@Basic(fetch = FetchType.LAZY)onpasswordseems like an option, it’s unreliable (some JDBC drivers don’t support single-field lazy loading). Entity graphs or projections are far more consistent.
内容的提问来源于stack exchange,提问作者Hardeek sharma

