hibernate的Criteria的一个bug

本文探讨了在使用Hibernate框架时,利用Criteria API查询具有OneToMany关联关系的数据出现的问题。通过对两种不同查询方式(HQL与Criteria)的对比,揭示了Criteria在特定配置下存在的潜在问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个bug是在使用hibernate annotation过程中发现的,开始以为是hibenrate annotation的bug,后来又使用xml文件来配置,还是存在同样的问题。
问题场景:我需要使用省份与城市,他们的关系是:一个省有个多城市,很简单的OneToMany关系,在改变province中的cities的lazy特性时,遇到问题
由于我采用把所有的资源都是放在一个表中的策略,所有这里有一个基类Resource

基类

@Entity
@Inheritance(strategy
=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name
="CATEGORY_ID",discriminatorType=DiscriminatorType.STRING)
@Table(name
="T_RESOURCE")
publicclassResourceimplementsSerializable...{

privateStringid;

privateStringname;

privateResourceCategoryresourceCategory;

@Id
@GenericGenerator(name
="generator",strategy="uuid")
@GeneratedValue(generator
="generator")
@Column(name
="ID")
publicStringgetId()...{
returnid;
}


@Column(name
="NAME")
publicStringgetName()...{
returnname;
}


@ManyToOne(fetch
=FetchType.LAZY)
@JoinColumn(name
="CATEGORY_ID",updatable=false,insertable=false)
publicResourceCategorygetResourceCategory()...{
returnresourceCategory;
}


@Override
publicbooleanequals(finalObjectother)...{
if(!(otherinstanceofResource))
returnfalse;
ResourcecastOther
=(Resource)other;
returnnewEqualsBuilder().append(id,castOther.id).isEquals();
}


@Override
publicinthashCode()...{
returnnewHashCodeBuilder().append(id).toHashCode();
}

}

Province子类

@Entity
@DiscriminatorValue(
"province")
publicclassProvinceextendsResource...{

privateSet<City>cities=newHashSet<City>();

@OneToMany(cascade
=CascadeType.ALL,fetch=FetchType.EAGER,mappedBy="province")
publicSet<City>getCities()...{
returncities;
}

}

子类City

@Entity
@DiscriminatorValue(
"city")
publicclassCityextendsResource...{
privateProvinceprovince;

publicCity(Stringname,ResourceCategoryresourceCategory)...{
super(name,resourceCategory);
}


@ManyToOne
@JoinColumn(name
="PARENT_ID")
publicProvincegetProvince()...{
returnprovince;
}

}

hibernate.cfg.xml

<!--
<mappingresource="ResourceCategory.hbm.xml"/>
<mappingresource="Resource.hbm.xml"/>
-->
<mappingclass="ResourceCategory"/>
<mappingclass="Resource"/>

hibernate助手类

publicclassHibernateUtil...{

privatestaticSessionFactorysessionFactory;

static...{
//Configurationconfiguration=newConfiguration();
AnnotationConfigurationconfiguration=newAnnotationConfiguration();
configuration.configure();

sessionFactory
=configuration.buildSessionFactory();
}


publicstaticSessionFactorygetSessionFactory()...{
returnsessionFactory;
}


publicSessiongetSession()...{
returngetSessionFactory().openSession();
}

}

单元测试类

publicclassHibnerateTestextendsTestCase...{

publicvoidtestListProvinceByHQL()...{
SessionFactorysessionFactory
=HibernateUtil.getSessionFactory();
Sessionsession
=sessionFactory.openSession();
Transactionctx
=session.beginTransaction();

Queryquery
=session.createQuery("fromProvince");
List
<Province>provinces=query.list();

for(Provinceprovince:provinces)...{
System.out.println(province.getName());
}


ctx.commit();
}


publicvoidtestListProvinceByCriteria()...{
SessionFactorysessionFactory
=HibernateUtil.getSessionFactory();
Sessionsession
=sessionFactory.openSession();
Transactionctx
=session.beginTransaction();

Criteriacriteria
=session.createCriteria(Province.class);

List
<Province>provinces=criteria.list();

for(Provinceprovince:provinces)...{
System.out.println(province.getName());
}


ctx.commit();
}

}

分别执行单元测试方法,他们输出的sql语句是一样的,但是输出的结果是不一样的。
select this_.ID as ID1_1_, this_.NAME as NAME1_1_, this_.COMMENTS as COMMENTS1_1_, this_.CATEGORY_ID as CATEGORY2_1_1_, cities2_.PARENT_ID as PARENT5_3_, cities2_.ID as ID3_, cities2_.ID as ID1_0_, cities2_.NAME as NAME1_0_, cities2_.COMMENTS as COMMENTS1_0_, cities2_.CATEGORY_ID as CATEGORY2_1_0_, cities2_.PARENT_ID as PARENT5_1_0_ from T_RESOURCE this_ left outer join T_RESOURCE cities2_ on this_.ID=cities2_.PARENT_ID where this_.CATEGORY_ID='province'
testListProvinceByHQL方式输出的是
上海
上海
上海
上海
江苏
江苏
江苏
江苏

testListProvinceByCriteria方式输出的是
上海
江苏


若把public Set<City> getCities()的映射改为
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "province")
@Fetch(FetchMode.SELECT)
或者
@OneToMany(cascade = CascadeType.ALL, mappedBy = "province")
@LazyCollection(LazyCollectionOption.FALSE)

则两个方法输出sql语句是一样的,且结果也一样
SQL:
select this_.ID as ID1_0_, this_.COMMENTS as COMMENTS1_0_, this_.NAME as NAME1_0_, this_.CATEGORY_ID as CATEGORY1_1_0_ from T_RESOURCE this_ where this_.CATEGORY_ID='province'
select cities0_.PARENT_ID as PARENT5_1_, cities0_.ID as ID1_, cities0_.ID as ID1_0_, cities0_.COMMENTS as COMMENTS1_0_, cities0_.NAME as NAME1_0_, cities0_.CATEGORY_ID as CATEGORY1_1_0_, cities0_.PARENT_ID as PARENT5_1_0_ from T_RESOURCE cities0_ where cities0_.PARENT_ID=?
select cities0_.PARENT_ID as PARENT5_1_, cities0_.ID as ID1_, cities0_.ID as ID1_0_, cities0_.COMMENTS as COMMENTS1_0_, cities0_.NAME as NAME1_0_, cities0_.CATEGORY_ID as CATEGORY1_1_0_, cities0_.PARENT_ID as PARENT5_1_0_ from T_RESOURCE cities0_ where cities0_.PARENT_ID=?
输出:
上海
江苏

从上面的现象可是看出,不论配置文件怎么变化,通过hql语句获得结果一直不变,而Criteria会变化,这就证明了Criteria是有bug的。
据此我得出结论:hibernate的Criteria在把关联的lazy属性设置为"false",且把fetch设置为join时,查找数据就存在bug

后来我使用xml配置文件的方式,也存在同样的问题,也证明了我的这个结论,

Resource.hbm.xml

<?xmlversion="1.0"?>
<!DOCTYPEhibernate-mappingPUBLIC"-//Hibernate/HibernateMappingDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>

<hibernate-mapping>
<classname="Resource"
table
="T_RESOURCE">

<idname="id"type="string">
<columnname="ID"/>
</id>

<discriminatorcolumn="CATEGORY_ID"type="string"/>

<propertyname="name"type="string"column="NAME"/>

<many-to-onename="resourceCategory"insert="false"
update
="false"
class
="ResourceCategory"
column
="CATEGORY_ID"/>

<subclassname="Province"
discriminator-value
="province">
<setname="cities"lazy="false"fetch="join">
<key>
<columnname="PARENT_ID"/>
</key>
<one-to-manyclass="City"/>
</set>
</subclass>

<subclassname="City"
discriminator-value
="city">
<many-to-onename="province"
class
="Province"
column
="PARENT_ID"/>
</subclass>

</class>
</hibernate-mapping>

ResourceCategory.hbm.xml

<?xmlversion="1.0"?>
<!DOCTYPEhibernate-mappingPUBLIC"-//Hibernate/HibernateMappingDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
<classname="ResourceCategory"
table
="T_RESOURCE_CATEGORY">

<idname="id"type="string">
<columnname="ID"/>
</id>

<propertyname="name"type="string"column="NAME"/>

</class>
</hibernate-mapping>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值