hibernate的Criteria的一个bug

本文探讨了在使用Hibernate框架时,配置OneToMany关系中lazy属性为false时出现的问题。具体表现为使用Criteria查询时结果不正确,而HQL查询却能返回预期结果。通过对不同配置的对比和XML映射文件的应用,验证了这一现象的存在。

摘要生成于 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")
public class Resource implements Serializable {

    
private String id;

    
private String name;

    
private ResourceCategory resourceCategory;

    @Id
    @GenericGenerator(name 
= "generator", strategy = "uuid")
    @GeneratedValue(generator 
= "generator")
    @Column(name 
= "ID")
    
public String getId() {
        
return id;
    }


    @Column(name 
= "NAME")
    
public String getName() {
        
return name;
    }


    @ManyToOne(fetch 
= FetchType.LAZY)
    @JoinColumn(name 
= "CATEGORY_ID", updatable = false, insertable = false)
    
public ResourceCategory getResourceCategory() {
        
return resourceCategory;
    }


    @Override
    
public boolean equals(final Object other) {
        
if (!(other instanceof Resource))
            
return false;
        Resource castOther 
= (Resource) other;
        
return new EqualsBuilder().append(id, castOther.id).isEquals();
    }


    @Override
    
public int hashCode() {
        
return new HashCodeBuilder().append(id).toHashCode();
    }

}

Province子类

@Entity
@DiscriminatorValue(
"province")
public class Province extends Resource {

    
private Set<City> cities = new HashSet<City>();

    @OneToMany(cascade 
= CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "province")
    
public Set<City> getCities() {
        
return cities;
    }

}

 子类City

@Entity
@DiscriminatorValue(
"city")
public class City extends Resource {
    
private Province province;

    
public City(String name, ResourceCategory resourceCategory) {
        
super(name, resourceCategory);
    }


    @ManyToOne
    @JoinColumn(name 
= "PARENT_ID")
    
public Province getProvince() {
        
return province;
    }

}

 hibernate.cfg.xml

<!-- 
    <mapping resource="ResourceCategory.hbm.xml" />
    <mapping resource="Resource.hbm.xml" />
-->
<mapping class="ResourceCategory" />
<mapping class="Resource" />

hibernate助手类

public class HibernateUtil {

    
private static SessionFactory sessionFactory;

    
static {
        
// Configuration configuration = new Configuration();
        AnnotationConfiguration configuration = new AnnotationConfiguration();
        configuration.configure();

        sessionFactory 
= configuration.buildSessionFactory();
    }


    
public static SessionFactory getSessionFactory() {
        
return sessionFactory;
    }


    
public Session getSession() {
        
return getSessionFactory().openSession();
    }

}

单元测试类

public class HibnerateTest extends TestCase {

    
public void testListProvinceByHQL() {
        SessionFactory sessionFactory 
= HibernateUtil.getSessionFactory();
        Session session 
= sessionFactory.openSession();
        Transaction ctx 
= session.beginTransaction();

        Query query 
= session.createQuery("from Province");
        List
<Province> provinces = query.list();

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


        ctx.commit();
    }


    
public void testListProvinceByCriteria() {
        SessionFactory sessionFactory 
= HibernateUtil.getSessionFactory();
        Session session 
= sessionFactory.openSession();
        Transaction ctx 
= session.beginTransaction();

        Criteria criteria 
= session.createCriteria(Province.class);
        
        List
<Province> provinces = criteria.list();

        
for (Province province : 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

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>

<hibernate-mapping>
    
<class name="Resource"
        table
="T_RESOURCE">

        
<id name="id" type="string">
            
<column name="ID" />
        
</id>

        
<discriminator column="CATEGORY_ID" type="string" />

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

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

        
<subclass name="Province"
            discriminator-value
="province">
            
<set name="cities" lazy="false" fetch="join">
                
<key>
                    
<column name="PARENT_ID" />
                
</key>
                
<one-to-many class="City" />
            
</set>
        
</subclass>

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

    
</class>
</hibernate-mapping>

ResourceCategory.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    
<class name="ResourceCategory"
        table
="T_RESOURCE_CATEGORY">

        
<id name="id" type="string">
            
<column name="ID" />
        
</id>

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

    
</class>
</hibernate-mapping>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值