Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

Hibernate知识集锦

11
Apr
2012

Hibernate知识集锦

By Alex
/ in Java
/ tags FAQ, Hibernate, HQL, XRM
0 Comments
重要概念
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代码:

EntityType.java
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protected final Object resolveIdentifier(Serializable id, SessionImplementor session) throws HibernateException {
    boolean isProxyUnwrapEnabled = unwrapProxy &&
            session.getFactory()
                    .getEntityPersister( getAssociatedEntityName() )
                    .isInstrumented( session.getEntityMode() );
        //EntityType是ManyToOneType的父类,后者的isNullable() == ignoreNotFound,对应@NotFound注解
    Object proxyOrEntity = session.internalLoad(
            getAssociatedEntityName(),
            id,
            eager,
            isNullable() && !isProxyUnwrapEnabled
    );
 
    if ( proxyOrEntity instanceof HibernateProxy ) {
        ( ( HibernateProxy ) proxyOrEntity ).getHibernateLazyInitializer()
                .setUnwrap( isProxyUnwrapEnabled );
    }
 
    return proxyOrEntity;
}

SessionImpl.java
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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();
    }

因此使用多态的ManyToOne时,要确保关联对象的类型准确,要么禁止延迟加载,要么启用延迟加载的同时打开 NotFoundAction.IGNORE

实体的状态
状态 说明
New (Transient)

新创建的对象,并且从来没有和Hibernate会话(持久化上下文,persistence context)关联过,没有和数据库表的一行关联

要将瞬时对象变为持久化对象,你需要明确的调用持久化API,例如persist

Persistent (Managed) 已经关联到数据库表的一行,并且被当前Hibernate管理。对这类对象的任何修改都被自动检测到,并自动在flush期间入库
Detached

如果当前Hibernate会话关闭,则先前Persistent对象变为Detached,之后对实体的更新不会被跟踪因而不会自动入库

要把Detached对象重新关联到Hibernate会话,你可以:

  1. Reattaching,通过session.update()方法。注意:在一个会话中,对于每个数据库行,只能有一个JVM对象与之关联
  2. Merging,拷贝Detached对象的字段到一个Managed对象中,如果当前会话中对应Persistent尚不存在则从数据库中抓取。在此操作后,Detached对象的持久化状态不变

对于配置了自动生成ID的实体类型,如果手工赋予了ID,会被看作是Detached,即使它是new出来的没有入库过的对象。对这种对象调用session.persist()会导致错误:detached entity passed to persist

Removed

实体对象被从数据库中删除,实际删除动作在flush时发生

JPA要求仅仅Managed对象才可以被删除,但是Hibernate允许调用session.delete删除Detached对象

实体状态转换示意图:

hibernate-entity-state-trans

持久化API

本节以如下实体来描述常用持久化API的行为:

Java
1
2
3
4
5
6
7
8
9
@Entity
public class Person {
    @Id
    @GeneratedValue
    private Long id;
 
    private String name;
}

注意下表中的API都不会立即导致SQL操作,SQL操作仅仅在flush时执行。

session. 说明
persist 用于将瞬时(new)对象转换为受管状态,例如:
Java
1
2
3
Person person = new Person();
person.setName("Alex");
session.persist(person);

注意该方法的返回值为void,它是直接把入参对象纳入受管状态,而非进行对象状态的拷贝。它的语义严格遵守JSR-220规范:

  1. 导致瞬时对象变为受管状态,并且对cascade=PERSIST|ALL的关联对象级联此操作
  2. 如果对象已经处于受管状态,调用此API无效果,但是级联操作仍然被执行
  3. 如果对象处于Detached状态,在此API调用后或者提交/flush时抛出异常
  4. 不保证id立即生成 —— 调用此方法后id可以为null,无论id的生成策略。规范允许在提交/flush时生成id
save

该方法是Hibernate原始的持久化接口,不遵守JPA规范。其行为类似于persist但是实现细节不同

该方法执行后,立即返回为瞬时对象分配的id

如果对Detached对象执行调用save(),会生成一个新的id

update

该方法是Hibernate原始的持久化接口:

  1. 在入参对象上进行操作,返回值是void。将入参对象从detached转换为persistent
  2. 如果你传入transient对象,抛出异常
merge

逻辑如下:

  1. 根据id从数据库中加载实体
  2. 从入参对象拷贝字段到加载的实体
  3. 返回加载的实体,该对象与入参是两个对象

JSR-220的语义被遵守:

  1. 如果实体是detached,则拷贝字段到加载的受管实体上
  2. 如果实体是transient,则拷贝字段到新建的受管实体上
  3. 级联关联的cascade=MERGE|ALL对象
  4. 如果实体目前已经是受管的,不拷贝字段,但是级联操作仍然进行
saveOrUpdate

Hibernate的特殊API,JPA中没有对应的接口

行为类似于update,可以被用来reattaching一个实例,不管它是transient还是detached

常见问题
Hibernate3使用XRM(EntityMode=dom4j),修改query.list()的dom元素后报错

报错信息:
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
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:690)
解决:查询完成后,调用sess.clear();

Hibernate HQL is null问题

HQL应该写成:prop is null。不能用参数: prop is :p 并把值传进去

Hibernate setParameter()报错

报错信息:Position beyond number of declared ordinal parameters. Remember that ordinal parameters are 1-based! Position: 2
报错原因:位置参数第一个设置为0,和JDBC不一样,JDBC的索引以1开始

Hibernate setParameter()传递对象给位置参数?报错

报错信息:Expected positional parameter count: ?, actual parameters ...
报错原因:未知,可以使用命名参数(named parameters)解决

如何为HQL命名参数传递多个值(IN子句)
1
2
3
4
5
6
7
8
9
10
String hql = "update user where id in (:id)";
sess = getSessionFactory().getCurrentSession();
Session msess = sess.getSession( EntityMode.MAP );
Query q = msess.createQuery( hql.toString() );
List<Object> params = new ArrayList<Object>();
for ( Map<String, ?> d : dataSet.getAllData() )
{
    params.add( d.get( "id") );
}
q.setParameterList( "id", params );
Hibernate序列化对象时出错

报错信息:org.hibernate.type.SerializationException: could not serialize

报错原因:某些时候Hibernate需要使用Java串行化机制来序列化对象,因此,必须在注解@Transient的同时,为字段添加transient关键字,这样Java串行化机制才能忽略该字段。

Java
1
2
3
4
@Inject
@Transient
@JsonIgnore
private  transient ServiceHelper     service;
HQL报错:org.hibernate.QueryException: could not resolve property
  1. 非实体属性不能存在于HQL的SELECT或者WHERE子句中
  2. 对于任意多态映射(@Any),即使关联的所有类型的实体均有属性A(通过@MappedSuperclass指定),A亦不能出现在SELECT或者WHERE子句中
@AnyMetaDef的@MetaValue的targetEntity不精确到具体子类时,无法保存元数据列的问题

在Hibernate 3.6.x中,使用任意多态映射时,如果其中某个关联目标是一个实体类层次,例如:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
@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;

则必须把所有子类的@MetaValue全部定义出来,否则在入库时元数据列ADMIN_TYPE保存为null,导致后续无法读取出mainAdmin属性。下面的补丁解决此问题:

MetaType.java
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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<?> 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;
}

应用上述补丁之后,加载对象后,发现mainAdmin属性的类型与其真实类型不对应,其类型是@metaValue中声明的Org类,下面的补丁解决此问题:

AnyType.java
1
2
3
4
5
6
7
8
//修改此文件的130、210、216行,改为调用下面的方法:
 
private Object internalLoad( String entityName, Serializable id, SessionImplementor session )
{
    //这边第三个参数改为true,即禁止了延迟加载,避免了代理对象类型不正确的问题
    return session.internalLoad( entityName, id, true, false );
}
//上面的方法返回的对象不是代理,和原代码语义可能存在不一致(本人未验证)
配置示例
Spring配置:Hibernate4
XHTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    <bean id="sessionFactory" p:dataSource-ref="dataSource" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="packagesToScan">
            <list>
                <value>cc.gmem.mgr.*.model</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <value>
                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
            </value>
        </property>
    </bean>
    <bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"
        p:sessionFactory-ref="sessionFactory" />
← 使用Java进行网络编程
SNMP协议学习笔记 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • Maven知识集锦
  • ActiveMQ知识集锦
  • SpringMVC知识集锦
  • Spring知识集锦
  • 基于JavaConfig方式的Spring+Hibernate集成

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
  • Three.js学习笔记 24 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2