You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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) on password seems 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:09:42