JPA定义实体之间的关系有如下几种:
@OneToOne @ManyToOne @OneToMany @ManyToMany
在定义它们的时候可以通过fetch属性指定加载方式,有两个值:
FetchType.LAZY:延迟加载 FetchType.EAGER:急加载
急加载就好理解了,在加载一个实体的时候,其中定义是急加载的的属性(property)和字段(field)会立即从数据库中加载 开发过程中遇到问题最多的就是延迟加载,并且问题都是一个:
“为什么我定义为延迟加载了,但没起作用,相关的属性或者字段还是会立即加载出来?”
对于这个问题,我的理解是这样的,我们首先假设有如下的影射关系:
@Entity @Table(name = "orders") class Order{ @OneToMany(cascade = {CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH},fetch = FetchType.LAZY,mappedBy = "order") private Collection lineItems = new HashSet (); @OneToOne(cascade={CascadeType.REMOVE},fetch=FetchType.LAZY,mappedBy="order") @JoinColumn(name="order_id") private OrderPrice salePrice; @OneToOne(cascade={CascadeType.REMOVE},fetch=FetchType.LAZY) @JoinColumn(name="customer_id") private Customer customer; } @Entity @Table(name = "order_items") class LineItem{ @ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH},fetch = FetchType.LAZY) @JoinColumn(name = "order_id",referencedColumnName = "order_id") private Order order; } @Entity @Table(name = "order_finance") @AttributeOverride(name="id",column=@Column(name="order_id")) class OrderPrice extends Price{ private Order order; @OneToOne(cascade={},fetch=FetchType.LAZY) @JoinColumn(name="order_id",referencedColumnName="order_id") public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } } @MappedSupperclass @Table(name = "order_finance") class Price{ @Id public Integer getId(){ ... } }
表的关系是:orders表是一个单独的表,order_items表有一个外键 (order_id)引用到orders表,order_finance有一个外键(order_id)引用到orders表. order_items------->orders<------------order_finance
|
customer
现在的问题就是:
Order.lineItems 这个@OneToMany的LAZY延迟加载是起作用的,find order的时候没有find出lineItems
Order.customer 这个@OneToMany的LAZY延迟加载是起作用的,find order的时候没有find出Customer
Order.salePrice 这个@OneToOne的LAZY延迟加载没起作用,find order后会把相关的OrderPrice也fetch 出来
LineItem.order 这个@ManyToOne的LAZY延迟加载是起作用的,find lineitem没有把相关的order find出来
OrderPrice.order 这个@OneToOne的LAZY延迟加载没起作用,find orderprice的时候把相关的order find出来了
延迟加载,顾名思义,就是在访问具体的属性时才从数据库中加载,比如例子中,只有调用OrderPrice.getOrder()的时候才应该会加载Order这个实体,加载OrderPrice的时候是不应该加载Order的。
那么首先想想,对于延迟加载,hibernate怎么知道什么时候会调用到相关的实体的get方法呢?
答案是它不知道,hibernate不知道什么时候会调用到相关的 get方法,那么hibernate如何实现只有访问到才加载这一点? hibernate使用了代理(Proxy),对实体的调用会被代理接受和处理,hibernate可以设置这个代理被调用到的时候去加载数据,从而实现 延迟加载。那么对于一个映射对象,要么它有值,要么它是null,对于null值建立代理是没多大作用的,而且也不能对null建立动态代理。那就是说 hibernate在对延迟加载建立代理的时候要考虑这个映射的对象是否是null。如果是null不需要建立代理,直接把映射的值设置成null,如果 映射的对象不为null,那么hibernate就建立代理对象
延迟加载失败都是由于确定映射的内容是否是null引起的 先来看@OneToMany,比如例子中的Order.lineitems,这是一个Collection,hibernate在加载Order的时候不加载lineitems,而是创建一个代理(Proxy)一个针对Collection的代理(通常是org.hibernate.collection.persistentBag)。 除非你调用了像Order.getLineItems.size()或者Order.getLineItems.get()方法的时候hibernate 才会去加载这个order的lineitems数据,要不然只是调用Order.getLineItems是不会加载到数据的,因为这个时候并没有具体的 访问LineItem. 由于代理是针对Collection建立的,而不是针对实体建立的,hibernate不用太多考虑是否为null,如果lineitem没有,也只是代 表这个集合是长度是0,这个集合是不为Null的。所以这很容易实现延迟加载
现在在来看例子@OneToOne Order.salePrice。它为什么会失败呢? hibernate也会建立代理,但这个代理是针对OrderPrice建立的(如果延迟加载成功,这个代理类形如Customer_javasisst_$1), 默认optioanl=true,也就是说OrderPrice可以为null,那么hibernate就要考虑,这里是放一个null呢?还是放一个代 理。但在Order这个实体里是不能确定它有没有价格的(但在价格里知道他的Order,有个外键指向order),所以hibernate要确认这个 OrderPrice是否存在,这个确认就导致的延迟加载失败,因为OrderPrice要被查询一次,如果不存在映射值为null,如果存在这个时候值 都取出来了,当然就不用什么代理了
Order.customer延迟加载是成功的,order表有一个外键关联到customer表,hibernate应该从这里知道这个customer是确实存在的,不用把映射值设置成null了,可以设置成代理类Customer_javasisst_$2
那如果把Order.salePrice的映射定义修改成:
@OneToOne(cascade={CascadeType.REMOVE},fetch=FetchType.LAZY,optional=false,mappedBy="order") @JoinColumn(name="order_id")
private OrderPrice salePrice;
延迟加载就成功了,因为optional=false指定salePrice不是可选的,必须有值,所以hibernate也不用考虑是该放null还是放代理,既然必须有值,又是延迟加载,那就设置成代理类了
根据上面所说的,OrderPrice定义有一个外键关联到Order,那 OrderPrice.order这个延迟加载应该是成功的,但为什么会失败呢? 难道是Order与OrderPrice两边都定义了OneToOne关系? 我这个例子中,这里失败我想是因为OrderPrice这个实体的定 义:@AttributeOverride(name="id",column=@Column(name="order_id"))
再来看看ManyToOne的LineItem.order,这个延迟加载也是成功 的。因为lineitem定义了外健关系到order 对于延迟加载的对象,如果已经脱离了容器,调用会得到org.hibernate.LazyInitializationException: could not initialize proxy - no Session方法异常
还有一种情况下延迟加载“看起来是没起作用的”:其实是起作用的,但可能在什么地方的代码调用到了相关的get方法,把延迟加载的对象加载出来的,所以看起来是没有成功的
总结:
- 对于延迟加载,hibernate无法知道什么时候会调用到延迟加载的属性/字段的get方法,所以对于延迟加载的属性/字段,hibernate会通过建立代理Proxy来包装(Wrapper)一下
- 代 理可能会根据实体本身建立,也可以是根据一个集合建立,如果是根据一个集合建立,延迟加载一般都能成功,如果是根据实体建立,null是不能建立代理的, 如果能够确定代理类一定存在,那延迟加载就能成功,相关的映射放置的是代理类,如果不能确定映射的属性是否存在,那就会去数据库中进行查询,这就导致的延 迟失败。 外键定义可以让hibernate知道映射的属性是否存在 也可以通过optional=false来告诉hibernate,映射的属性一定存在.
相关推荐
确保包含JPA API和Hibernate实现的jar文件。 2. **配置持久化单元**:在`persistence.xml`文件中定义持久化单元,指定使用的JPA供应商(Hibernate),数据库连接信息,以及实体类的包名。 ```xml <provider>org....
【标题】"jap.rar_jap简单教程"指的是一个关于JAP(可能是Java Application Project)的压缩包资源,其中包含了学习和理解JSP(JavaServer Pages)基础应用的材料。这个教程面向的是初学者,旨在帮助他们入门Web开发...
@ManyToOne(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY) @JoinColumn(name = "personid", unique = false, nullable = true, insertable = true, updatable = true) public Person getPerson() { ...
JAP.DLL wince 上的数据服务组件用于JAP数据库嵌入式版本
JAP.DLL wince 上的数据服务组件 用于JAP数据库嵌入式版本
对于开发人员来说,理解和定制JAP的配置至关重要,以确保与自身应用系统的无缝集成。 总的来说,JAP作为一款全面的认证中间件,为WEB应用提供了安全、灵活的身份验证解决方案。其模块化设计和对多种认证协议的支持...
在现代Java应用开发中,Spring MVC作为Web框架和Java ...以上就是关于"springMVC+JAP整合出去persistence.xml配置文件"的详细说明,通过这种方式,我们可以构建出更加现代化、高效且易于扩展的Java Web应用。
压缩包文件`JAP_Hibernate_lib`可能包含这些必要的库,例如hibernate-core、hibernate-entitymanager、javax.persistence-api等。在Maven或Gradle项目中,这些库通常会以依赖的形式存在于pom.xml或build.gradle文件...
commons-lang-2.3.jap+log4j-1.2.16.jar+slf4j-api-1.5.8.jar+slf4j-log4j12-1.6.1.jar hibernate框架所用的所有的jar包。
- **概述**:这是 Hibernate 实现 JPA 规范的核心组件,包含了 JPA 规范所定义的所有接口。 - **作用**:提供了一个标准的 API 接口,用以管理实体的状态(如持久化、删除等操作)。 - **安装**:通过构建工具添加...
2. **Hibernate实现**:`hibernate-entitymanager.jar`是Hibernate提供的JPA实现,它包含了对JPA规范的实现以及一些扩展功能。 3. **Hibernate核心**:`hibernate-core.jar`是Hibernate的核心库,包括实体管理、...
在Java编程语言中,"JAP分页"通常指的是Java应用程序中的分页查询技术,用于在大量数据中实现高效的检索和展示。这个压缩包文件名"JAVA JAP分页写法大全"暗示了它可能包含各种不同的分页实现示例,这对于初学者来说...
了解JavaScript可以帮助开发者更好地理解客户端脚本的概念,这对于调试JSP中的JavaScript代码非常有帮助。 3. **CSS**:CSS用于控制网页的样式,是网页设计不可或缺的一部分。掌握CSS有助于开发者创建美观且响应式的...
Hibernate是JPA的一个实现,提供了丰富的功能,如缓存、事务管理等。 3. **MyBatis**:MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。它将SQL语句与Java代码分离,允许开发者自由地编写复杂的SQL...
【PCSX2 BIOS 文件 通用版】:PCSX2是一款开源的PlayStation 2(PS2)游戏模拟器,允许用户在个人电脑上运行PS2游戏。BIOS文件是模拟器的重要组成部分,它包含了PS2硬件的核心系统信息,用于使模拟器能够正确地模拟...
SSJ.rar_Jap_spring_ssj 是一个与Java应用程序开发相关的压缩包,它结合了Struts、Spring和JAP(可能是Java应用平台)这三种技术。这个项目可能是一个示例或者教程,展示了如何将这三个框架集成在一起以实现特定的...