<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>绿色记忆 &#187; HQL</title>
	<atom:link href="https://blog.gmem.cc/tag/hql/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Mon, 13 Apr 2026 08:03:10 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>Hibernate知识集锦</title>
		<link>https://blog.gmem.cc/hibernate-faq</link>
		<comments>https://blog.gmem.cc/hibernate-faq#comments</comments>
		<pubDate>Wed, 11 Apr 2012 04:28:04 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[FAQ]]></category>
		<category><![CDATA[Hibernate]]></category>
		<category><![CDATA[HQL]]></category>
		<category><![CDATA[XRM]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=1233</guid>
		<description><![CDATA[<p>重要概念 get和load 在Hibernate 3.6.x中： get会返回实际的对象实例，如果不存在则返回null，session.get(Superclass.class,id)与session.get(Concreteclass,id)的返回值完全一致。 load则返回一个生成的代理（类名以类似_$$_javassist_*结尾），即使给定ID的对象不存在也可以得到代理实例，只有第一次访问其非ID属性时才会从数据库寻找实例，找不到抛出异常。代理的父类就是session.load(Class.class,id)中指定的类，如果在多态映射时不指定具体类型，则不能cast为实际类型。 指定ManyToOne关的FetchType为Lazy时，可能导致关联实体使用类似于load的方式加载（调用SessionImplementor.internalLoad方法），除非指定为其指定注解：@NotFound ( action = NotFoundAction.IGNORE )，相关的Hibernate代码： [crayon-69df5566f27a1243604545/] [crayon-69df5566f27a7768766772/] 因此使用多态的ManyToOne时，要确保关联对象的类型准确，要么禁止延迟加载，要么启用延迟加载的同时打开 NotFoundAction.IGNORE 实体的状态 状态 说明 New (Transient) 新创建的对象，并且从来没有和Hibernate会话（持久化上下文，persistence <a class="read-more" href="https://blog.gmem.cc/hibernate-faq">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/hibernate-faq">Hibernate知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2">重要概念</div>
<div class="blog_h3"><span class="graybg">get和load</span></div>
<p>在Hibernate 3.6.x中：</p>
<p>get会返回实际的对象实例，如果不存在则返回null，session.get(Superclass.class,id)与session.get(Concreteclass,id)的返回值完全一致。</p>
<p>load则返回一个生成的代理（类名以类似_$$_javassist_*结尾），即使给定ID的对象不存在也可以得到代理实例，只有第一次访问其非ID属性时才会从数据库寻找实例，找不到抛出异常。代理的父类就是session.load(Class.class,id)中指定的类，如果在<span style="background-color: #c0c0c0;">多态映射时不指定具体类型，则不能cast为实际类型</span>。</p>
<p>指定ManyToOne关的FetchType为Lazy时，可能导致关联实体使用类似于load的方式加载（调用SessionImplementor.internalLoad方法），除非指定为其指定注解：@NotFound ( action = NotFoundAction.IGNORE )，相关的Hibernate代码：</p>
<pre class="crayon-plain-tag">protected final Object resolveIdentifier(Serializable id, SessionImplementor session) throws HibernateException {
	boolean isProxyUnwrapEnabled = unwrapProxy &amp;&amp;
			session.getFactory()
					.getEntityPersister( getAssociatedEntityName() )
					.isInstrumented( session.getEntityMode() );
        //EntityType是ManyToOneType的父类，后者的isNullable() == ignoreNotFound，对应@NotFound注解
	Object proxyOrEntity = session.internalLoad(
			getAssociatedEntityName(),
			id,
			eager,
			isNullable() &amp;&amp; !isProxyUnwrapEnabled
	);

	if ( proxyOrEntity instanceof HibernateProxy ) {
		( ( HibernateProxy ) proxyOrEntity ).getHibernateLazyInitializer()
				.setUnwrap( isProxyUnwrapEnabled );
	}

	return proxyOrEntity;
}</pre><br />
<pre class="crayon-plain-tag">public Object internalLoad(String entityName, Serializable id, boolean eager, boolean nullable) throws HibernateException {
		// todo : remove
		LoadEventListener.LoadType type = nullable
                                //如果设置NotFoundAction.IGNORE，则走该分支，不创建代理
				? LoadEventListener.INTERNAL_LOAD_NULLABLE
				: eager
						? LoadEventListener.INTERNAL_LOAD_EAGER //不创建代理
						: LoadEventListener.INTERNAL_LOAD_LAZY; //只有这个会进行代理的创建
		LoadEvent event = new LoadEvent(id, entityName, true, this);
		fireLoad(event, type);
		if ( !nullable ) {
			UnresolvableObjectException.throwIfNull( event.getResult(), id, entityName );
		}
		return event.getResult();
	}</pre>
<p>因此使用多态的ManyToOne时，要确保关联对象的类型准确，要么禁止延迟加载，要么启用延迟加载的同时打开 NotFoundAction.IGNORE</p>
<div class="blog_h3"><span class="graybg">实体的状态</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">状态</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>New (Transient)</td>
<td>
<p>新创建的对象，并且从来没有和Hibernate会话（持久化上下文，persistence context）关联过，没有和数据库表的一行关联</p>
<p>要将瞬时对象变为持久化对象，你需要明确的调用持久化API，例如persist</p>
</td>
</tr>
<tr>
<td>Persistent (Managed)</td>
<td>已经关联到数据库表的一行，并且被当前Hibernate管理。对这类对象的<span style="background-color: #c0c0c0;">任何修改都被自动检测到，并自动在flush期间入库</span></td>
</tr>
<tr>
<td>Detached</td>
<td>
<p>如果当前Hibernate会话关闭，则先前Persistent对象变为Detached，之后对实体的更新不会被跟踪因而不会自动入库</p>
<p>要把Detached对象重新关联到Hibernate会话，你可以：</p>
<ol>
<li>Reattaching，通过session.update()方法。注意：在一个会话中，对于每个数据库行，只能有一个JVM对象与之关联</li>
<li>Merging，拷贝Detached对象的字段到一个Managed对象中，如果当前会话中对应Persistent尚不存在则从数据库中抓取。在此操作后，Detached对象的持久化状态不变</li>
</ol>
<p>对于配置了自动生成ID的实体类型，如果手工赋予了ID，会被看作是Detached，即使它是new出来的没有入库过的对象。对这种对象调用session.persist()会导致错误：detached entity passed to persist</p>
</td>
</tr>
<tr>
<td>Removed</td>
<td>
<p>实体对象被从数据库中删除，实际删除动作在flush时发生</p>
<p>JPA要求仅仅Managed对象才可以被删除，但是Hibernate允许调用session.delete删除Detached对象</p>
</td>
</tr>
</tbody>
</table>
<p>实体状态转换示意图：</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2012/04/hibernate-entity-state-trans.png"><img class="alignnone size-full wp-image-15172" src="https://blog.gmem.cc/wp-content/uploads/2012/04/hibernate-entity-state-trans.png" alt="hibernate-entity-state-trans" width="100%" /></a></p>
<div class="blog_h3"><span class="graybg">持久化API</span></div>
<p>本节以如下实体来描述常用持久化API的行为：</p>
<pre class="crayon-plain-tag">@Entity
public class Person {
    @Id
    @GeneratedValue
    private Long id;

    private String name;
 
}</pre>
<p>注意下表中的API都不会立即导致SQL操作，SQL操作仅仅在flush时执行。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">session.</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>persist</td>
<td>用于将瞬时（new）对象转换为受管状态，例如：<br />
<pre class="crayon-plain-tag">Person person = new Person();
person.setName("Alex");
session.persist(person);</pre></p>
<p>注意该方法的返回值为void，它是直接把入参对象纳入受管状态，而非进行对象状态的拷贝。它的语义严格遵守JSR-220规范：</p>
<ol>
<li>导致瞬时对象变为受管状态，并且对cascade=PERSIST|ALL的关联对象级联此操作</li>
<li>如果对象已经处于受管状态，调用此API无效果，但是级联操作仍然被执行</li>
<li>如果对象处于Detached状态，在此API调用后或者提交/flush时抛出异常</li>
<li>不保证id立即生成 —— <span style="background-color: #c0c0c0;">调用此方法后id可以为null</span>，无论id的生成策略。规范允许在提交/flush时生成id</li>
</ol>
</td>
</tr>
<tr>
<td>save</td>
<td>
<p>该方法是Hibernate原始的持久化接口，不遵守JPA规范。其行为类似于persist但是实现细节不同</p>
<p>该方法执行后，<span style="background-color: #c0c0c0;">立即返回为瞬时对象分配的id</span></p>
<p>如果对Detached对象执行调用save()，<span style="background-color: #c0c0c0;">会生成一个新的id</span></p>
</td>
</tr>
<tr>
<td>update</td>
<td>
<p>该方法是Hibernate原始的持久化接口：</p>
<ol>
<li>在入参对象上进行操作，返回值是void。将入参对象从detached转换为persistent</li>
<li>如果你传入transient对象，抛出异常</li>
</ol>
</td>
</tr>
<tr>
<td>merge</td>
<td>
<p>逻辑如下：</p>
<ol>
<li>根据id从数据库中加载实体</li>
<li>从入参对象拷贝字段到加载的实体</li>
<li>返回加载的实体，该对象与入参是两个对象</li>
</ol>
<p>JSR-220的语义被遵守：</p>
<ol>
<li>如果实体是detached，则拷贝字段到加载的受管实体上</li>
<li>如果实体是transient，则拷贝字段到新建的受管实体上</li>
<li>级联关联的cascade=MERGE|ALL对象</li>
<li>如果实体目前已经是受管的，不拷贝字段，但是级联操作仍然进行</li>
</ol>
</td>
</tr>
<tr>
<td>saveOrUpdate</td>
<td>
<p>Hibernate的特殊API，JPA中没有对应的接口</p>
<p>行为类似于update，可以被用来reattaching一个实例，不管它是transient还是detached</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">常见问题</span></div>
<div class="blog_h3"><span class="graybg">Hibernate3使用XRM（EntityMode=dom4j），修改query.list()的dom元素后报错</span></div>
<p>报错信息：<br /> org.springframework.orm.hibernate3.HibernateSystemException: identifier of an instance of *** was altered from 248178 to null; nested exception is org.hibernate.HibernateException: identifier of an instance of wnetJhInterface was altered from 248178 to null<br /> at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:690)<br /> 解决：查询完成后，调用sess.clear();</p>
<div class="blog_h3"><span class="graybg">Hibernate HQL is null问题</span></div>
<p>HQL应该写成：prop is null。不能用参数： prop is :p 并把值传进去</p>
<div class="blog_h3"><span class="graybg">Hibernate setParameter()报错</span></div>
<p>报错信息：Position beyond number of declared ordinal parameters. Remember that ordinal parameters are 1-based! Position: 2<br /> 报错原因：位置参数第一个设置为0，和JDBC不一样，JDBC的索引以1开始</p>
<div class="blog_h3"><span class="graybg">Hibernate setParameter()传递对象给位置参数?报错</span></div>
<p>报错信息：Expected positional parameter count: ?, actual parameters ...<br /> 报错原因：未知，可以使用命名参数（named parameters）解决</p>
<div class="blog_h3"><span class="graybg">如何为HQL命名参数传递多个值(IN子句)</span></div>
<pre class="crayon-plain-tag">String hql = "update user where id in (:id)";
sess = getSessionFactory().getCurrentSession();
Session msess = sess.getSession( EntityMode.MAP );
Query q = msess.createQuery( hql.toString() );
List&lt;Object&gt; params = new ArrayList&lt;Object&gt;();
for ( Map&lt;String, ?&gt; d : dataSet.getAllData() )
{
    params.add( d.get( "id") );
}
q.setParameterList( "id", params );</pre>
<div class="blog_h3"><span class="graybg">Hibernate序列化对象时出错</span></div>
<p>报错信息：org.hibernate.type.SerializationException: could not serialize</p>
<p>报错原因：某些时候Hibernate需要使用Java串行化机制来序列化对象，因此，必须在注解@Transient的同时，为字段添加transient关键字，这样Java串行化机制才能忽略该字段。</p>
<pre class="crayon-plain-tag">@Inject
@Transient
@JsonIgnore
private  transient ServiceHelper     service;</pre>
<div class="blog_h3"><span class="graybg">HQL报错：org.hibernate.QueryException: could not resolve property</span></div>
<ol>
<li>非实体属性不能存在于HQL的SELECT或者WHERE子句中</li>
<li>对于任意多态映射（@Any），即使关联的所有类型的实体均有属性A（通过@MappedSuperclass指定），A亦不能出现在SELECT或者WHERE子句中</li>
</ol>
<div class="blog_h3"><span class="graybg">@AnyMetaDef的@MetaValue的targetEntity不精确到具体子类时，无法保存元数据列的问题</span></div>
<p>在Hibernate 3.6.x中，使用任意多态映射时，如果其中某个关联目标是一个实体类层次，例如：</p>
<pre class="crayon-plain-tag">@AnyMetaDef (
    name = "adminMetaDef",
    idType = "integer",
    metaType = "integer",
    metaValues = {
        @MetaValue ( value = "1", targetEntity = Child.class ),
        //如果Org是一个抽象实体类，其下面有若干个具体实体子类，则在该配置下，任何子实体均无法关联上
        @MetaValue ( value = "2", targetEntity = Org.class )
    } 
)
@Any ( metaDef = "adminMetaDef", metaColumn = @Column ( name = "ADMIN_TYPE") )
@JoinColumn ( name = "ADMIN_ID" )
private Admin mainAdmin;</pre>
<p>则必须把所有子类的@MetaValue全部定义出来，否则在入库时元数据列ADMIN_TYPE保存为null，导致后续无法读取出mainAdmin属性。下面的补丁解决此问题：</p>
<pre class="crayon-plain-tag">public void nullSafeSet( PreparedStatement st, Object value, int index, SessionImplementor session )
        throws HibernateException, SQLException
{
    baseType.nullSafeSet( st, value == null ? null : getKey( value ), index, session );
}
private Object getSuperclass( Object clsName )
{
    try
    {
        Class&lt;?&gt; c = Class.forName( clsName.toString() );
        return c.getSuperclass().getName();
    }
    catch ( Exception e )
    {
        return null;
    }
}

private Object getKey( Object clsName )
{
    Object key = this.keys.get( clsName );
    if ( key == null )
    {
        return getKey( getSuperclass( clsName ) );
    }
    return key;
}</pre>
<p>应用上述补丁之后，加载对象后，发现mainAdmin属性的类型与其真实类型不对应，其类型是@metaValue中声明的Org类，下面的补丁解决此问题：</p>
<pre class="crayon-plain-tag">//修改此文件的130、210、216行，改为调用下面的方法：

private Object internalLoad( String entityName, Serializable id, SessionImplementor session )
{
    //这边第三个参数改为true，即禁止了延迟加载，避免了代理对象类型不正确的问题
    return session.internalLoad( entityName, id, true, false );
}
//上面的方法返回的对象不是代理，和原代码语义可能存在不一致（本人未验证）</pre>
<div class="blog_h2"><span class="graybg">配置示例</span></div>
<div class="blog_h3"><span class="graybg">Spring配置：Hibernate4</span></div>
<pre class="crayon-plain-tag">&lt;bean id="sessionFactory" p:dataSource-ref="dataSource" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"&gt;
        &lt;property name="packagesToScan"&gt;
            &lt;list&gt;
                &lt;value&gt;cc.gmem.mgr.*.model&lt;/value&gt;
            &lt;/list&gt;
        &lt;/property&gt;
        &lt;property name="hibernateProperties"&gt;
            &lt;value&gt;
                hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
                hibernate.jdbc.batch_size=20
                hibernate.show_sql=false
                hibernate.format_sql=false
                hibernate.generate_statistics=true
                hibernate.transaction.factory_class=org.hibernate.transaction.JDBCTransactionFactory
                hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext
            &lt;/value&gt;
        &lt;/property&gt;
    &lt;/bean&gt;
    &lt;bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"
        p:sessionFactory-ref="sessionFactory" /&gt;</pre>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/hibernate-faq">Hibernate知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/hibernate-faq/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
