
在使用hibernate criteria api进行查询时,开发者通常可以通过getsession().createcriteria(entityclass.class, "alias")来为查询的根实体指定一个表别名。然而,在hibernate 3.6.10.final版本中,即使显式设置了别名,生成的sql语句中的根实体别名仍然是默认的this_,而非用户期望的自定义别名。
例如,当尝试将Vehicle实体的根别名设置为temp时:
Criteria cr = getSession().createCriteria(Vehicle.class, "temp"); // ... 其他Criteria配置
期望生成的SQL可能是:
select temp.vehicle_id as y0_, temp.vin as y1_, temp.initial_registration as y2_ from vehicle temp where temp.vin=?
但实际生成的SQL却是:
select this_.vehicle_id as y0_, this_.vin as y1_, this_.initial_registration as y2_ from vehicle this_ where this_.vin=?
这表明用户设置的"temp"别名并未生效。
以下是一个简化的Hibernate Criteria查询示例,用于说明上述问题:
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.transform.Transformers;
import org.hibernate.criterion.ProjectionList;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
// 假设这是一个通用的DAO方法
public abstract class BaseDao<T> {
protected Session getSession() {
// 实际应用中应从SessionFactory获取当前Session
throw new UnsupportedOperationException("Method not implemented: getSession()");
}
protected void begin() {
// 事务开始
}
protected void commit() {
// 事务提交
}
public List<T> findByProjectionCriteria() {
// 尝试设置根别名为 "temp"
Criteria cr = getSession().createCriteria(Vehicle.class, "temp");
cr.setResultTransformer(Transformers.aliasToBean(Vehicle.class));
ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.property("vehicleId"), "vehicleId");
projectionList.add(Projections.property("vin"), "vin");
projectionList.add(Projections.property("initialRegistration"), "initialRegistration");
cr.setProjection(projectionList);
cr.add(Restrictions.eq("vin", "WVW29343249702776"));
begin();
List<T> list = null;
try {
list = cr.list();
} catch (Exception e) {
e.printStackTrace();
}
if (list == null)
list = new ArrayList<>();
commit();
return list;
}
}
// 实体定义
@Entity
@Table(name = "vehicle")
public class Vehicle {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "vehicle_id", unique = true, nullable = false)
private int vehicleId;
@Column(nullable = false, length = 17)
private String vin;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "initial_registration")
private Date initialRegistration;
// Getter和Setter方法 (为简洁省略,但实际应存在)
public int getVehicleId() { return vehicleId; }
public void setVehicleId(int vehicleId) { this.vehicleId = vehicleId; }
public String getVin() { return vin; }
public void setVin(String vin) { this.vin = vin; }
public Date getInitialRegistration() { return initialRegistration; }
public void setInitialRegistration(Date initialRegistration) { this.initialRegistration = initialRegistration; }
}要理解为何自定义根别名无效,需要深入探究Hibernate 3.6内部处理Criteria查询的机制。问题根源在于CriteriaQueryTranslator类的构造函数及其调用的createCriteriaSQLAliasMap()方法。
当执行cr.list()方法时,Hibernate会创建一个CriteriaLoader对象,该对象进而会实例化CriteriaQueryTranslator。在CriteriaQueryTranslator的构造过程中,createCriteriaSQLAliasMap()方法负责设置SQL别名。其核心逻辑如下(简化自Hibernate 3.6源码):
private void createCriteriaSQLAliasMap() {
int i = 0;
// 遍历所有Criteria实例(包括根Criteria和关联Criteria)
Iterator criteriaIterator = criteriaEntityNames.entrySet().iterator();
while ( criteriaIterator.hasNext() ) {
Map.Entry me = ( Map.Entry ) criteriaIterator.next();
Criteria crit = ( Criteria ) me.getKey(); // 当前Criteria实例
String alias = crit.getAlias(); // 获取用户为当前Criteria设置的别名
if ( alias == null ) {
alias = ( String ) me.getValue(); // 如果没有显式别名,则使用实体名
}
// 为当前Criteria生成一个SQL别名并放入map
criteriaSQLAliasMap.put( crit, StringHelper.generateAlias( alias, i++ ) );
}
// 关键步骤:在循环结束后,再次为根Criteria设置SQL别名
// rootSQLAlias 通常默认为 "this_"
criteriaSQLAliasMap.put( rootCriteria, rootSQLAlias );
}从上述代码可以看出,createCriteriaSQLAliasMap()方法首先会遍历所有Criteria实例(包括根Criteria),并尝试根据crit.getAlias()或实体名生成一个SQL别名,将其存入criteriaSQLAliasMap。这意味着,即使我们通过createCriteria(Vehicle.class, "temp")设置了"temp",它可能在这个循环中被处理。
然而,关键在于循环结束后的一行:criteriaSQLAliasMap.put( rootCriteria, rootSQLAlias );。这一行会强制将rootCriteria的SQL别名设置为rootSQLAlias。在Hibernate 3.6中,rootSQLAlias的默认值通常就是"this_"。由于rootCriteria作为Map的键是唯一的,这个操作会覆盖之前在循环中可能为根Criteria设置的任何自定义别名。
因此,无论开发者在createCriteria()方法中为根实体指定了什么别名,最终都会被Hibernate内部的默认rootSQLAlias(即this_)所覆盖。
鉴于Hibernate 3.6的这一内部行为,直接通过Criteria API改变根实体的默认this_别名是不可行的。在面对此限制时,可以考虑以下策略:
接受默认别名: 对于大多数简单的查询,this_作为根实体的别名并不会影响查询的正确性或可读性。如果对别名没有强烈的自定义需求,接受默认行为是最直接的方式。
升级Hibernate版本: 较新的Hibernate版本可能已经改进了Criteria API对根别名的处理逻辑。如果项目条件允许,升级Hibernate是一个长期的解决方案,可能会解决此类内部行为问题并带来其他性能和功能上的改进。
使用HQL或JPQL: 如果对SQL别名有严格的控制要求,或者需要更复杂的查询逻辑,可以考虑使用Hibernate Query Language (HQL) 或 Java Persistence Query Language (JPQL)。HQL/JPQL提供了更灵活的语法来定义查询,包括自定义别名:
// HQL示例
String hql = "select v.vehicleId as vehicleId, v.vin as vin, v.initialRegistration as initialRegistration " +
"from Vehicle v where v.vin = :vin";
List<Vehicle> vehicles = getSession().createQuery(hql, Vehicle.class)
.setParameter("vin", "WVW29343249702776")
.list();在HQL中,您可以自由选择任何合法的标识符作为实体别名。
使用原生SQL: 对于极度复杂的查询或需要数据库特定功能的场景,原生SQL是最终的解决方案。原生SQL提供了对SQL语句的完全控制,包括所有别名的定义。
// 原生SQL示例
String sql = "select temp.vehicle_id as vehicleId, temp.vin as vin, temp.initial_registration as initialRegistration " +
"from vehicle temp where temp.vin = :vin";
List<Object[]> results = getSession().createNativeQuery(sql)
.setParameter("vin", "WVW29343249702776")
.list();
// 需要手动将Object[]映射到Vehicle对象Hibernate 3.6 Criteria API在处理根实体别名时存在一个内部限制,即用户通过createCriteria(Class, alias)设置的自定义别名会被CriteriaQueryTranslator强制覆盖为默认的this_。这是由于Hibernate内部在构建SQL别名映射时,会最后一步将根Criteria的别名设置为rootSQLAlias。
理解这一机制有助于开发者在遇到类似问题时,明确其行为而非误认为是API使用不当。对于需要严格控制SQL别名的场景,建议转向使用HQL、JPQL或原生SQL,或者考虑升级到更现代的Hibernate版本以获取潜在的改进和更灵活的功能。
以上就是深入理解Hibernate 3.6 Criteria API根别名行为及限制的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号