JPA教程

这篇博客详细介绍了Java Persistence API (JPA) 的核心概念,包括JPA的背景、实体对象、注解元数据、XML元数据以及查询语言。JPA提供了一种标准化的ORM映射方式,通过注解或XML定义对象-关系映射,并支持面向对象的查询语言。文中通过实例展示了如何使用JPA进行实体继承、关联关系、Lob类型字段的持久化,以及如何编写JPA查询语句进行数据操作。JPA使得开发者能够在Java SE和EE环境中轻松进行对象持久化,提高了开发效率。
摘要由CSDN通过智能技术生成

 JPA 教程

1.JPA概述

JPA(Java Persistence API)作为Java EE 5.0平台标准的ORM规范,将得到所有Java EE服务器的支持。Sun这次吸取了之前EJB规范惨痛失败的经历,在充分吸收现有ORM框架的基础上,得到了一个易于使用、伸缩性强的ORM规范。从目 前的开发社区的反应上看,JPA受到了极大的支持和赞扬,JPA作为ORM领域标准化整合者的目标应该不难实现。

JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中,图 1很好地描述了JPA的结构:

Sun引入新的JPA ORM规范出于两个原因:其一,简化现有Java EE和Java SE应用的对象持久化的开发工作;其二,Sun希望整合对ORM技术,实现天下归一。

JPA由EJB 3.0软件专家组开发,作为JSR-220实现的一部分。但它不囿于EJB 3.0,你可以在Web应用、甚至桌面应用中使用。JPA的宗旨是为POJO提供持久化标准规范,由此可见,经过这几年的实践探索,能够脱离容器独立运 行,方便开发和测试的理念已经深入人心了。目前Hibernate 3.2、TopLink 10.1.3以及OpenJpa都提供了JPA的实现。

 

 

JPA的总体思想和现有Hibernate、TopLink,JDO等ORM框架大体一致。总的来说,JPA包括以下3方面的技术:

ORM映射元数据,JPA支持XML和JDK 5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中;

JPA 的API,用来操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来。

查询语言,这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。

2.实体对象

访问数据库前,我们总是要设计在应用层承载数据的领域对象(Domain Object),ORM框架将它们持久化到数据库表中。为了方便后面的讲解,我们用论坛应用为例,建立以下的领域对象:

Topic是论坛的主题,而PollTopic是调查性质的论坛主题,它扩展于Topic,一个调查主题拥有多个选项PollOption。这三个领域对象很好地展现了领域对象之间继承和关联这两大核心的关系。这3个领域对象将被映射到数据库的两张表中:

 

 

其中,Topic及其子类PollTopic将映射到同一张t_topic表中,并用topic_type字段区分两者。而PollOption映射到t_polloption中。

具有ORM元数据的领域对象称为实体(Entity),按JPA的规范,实体具备以下的条件:

必须使用javax.persistence.Entity注解或者在XML映射文件中有对应的元素;

必须具有一个不带参的构造函数,类不能声明为final,方法和需要持久化的属性也不能声明为final;

如果游离状的实体对象需要以值的方式进行传递,如通Session bean的远程业务接口传递,则必须实现Serializable接口;

需要持久化的属性,其访问修饰符不能是public,它们必须通过实体类方法进行访问。

3.使用注解元数据

基本注解

首先,我们对Topic领域对象进行注解,使其成为一个合格的实体类:

代码清单1:Topic实体类的注解

package com.baobaotao.domain; … import javax.persistence.Column; import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorType; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType;
 

 

 

import javax.persistence.Temporal; import javax.persistence.TemporalType;

@Entity(name = "T_TOPIC") ① public class Topic implements Serializable ...{ @Id ②-1 @GeneratedValue(strategy = GenerationType.TABLE) ②-2 @Column(name = "TOPIC_ID") ②-3 private int topicId;

@Column(name = "TOPIC_TITLE", length = 100) ③ private String topicTitle;

@Column(name = "TOPIC_TIME") @Temporal(TemporalType.DATE) ④ private Date topicTime;

@Column(name = "TOPIC_VIEWS") private int topicViews;

//省略get/setter方法 }
 

 

@Entity:将领域对象标注为一个实体,表示需要保存到数据库中,默认情况下类名即为表名,通过name属性显式指定表名,如①处的name = "T_TOPIC",表示Topic保存到T_TOPIC表中; @Id :对应的属性是表的主键,如②-1所示; @GeneratedValue:主键的产生策略,通过strategy属性指定。默认情况下,JPA自动选择一个最适合底层数据库的主键生成策略,如SqlServer对应identity,MySql对应auto increment。在javax.persistence.GenerationType中定义了以下几种可供选择的策略:

1) IDENTITY:表自增键字段,Oracle不支持这种方式;

 

 

2) AUTO: JPA自动选择合适的策略,是默认选项; 3) SEQUENCE:通过序列产生主键,通过@SequenceGenerator注解指定序列名,MySql不支持这种方式; 4) TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。不同的JPA实现商生成的表名是不同的,如 OpenJPA生成openjpa_sequence_table表Hibernate生成一个hibernate_sequences表,而 TopLink则生成sequence表。这些表都具有一个序列名和对应值两个字段,如SEQ_NAME和SEQ_COUNT。

@Column(name = "TOPIC_ID"):属性对应的表字段。我们并不需要指定表字段的类型,因为JPA会根据反射从实体属性中获取类型;如果是字符串类型,我们可以指定字段长度,以便可以自动生成DDL语句,如③处所示; @Temporal(TemporalType.DATE):如果属性是时间类型,因为数据表对时间类型有更严格的划分,所以必须指定具体时间类型,如④所示。在javax.persistence.TemporalType枚举中定义了3种时间类型:

1) DATE :等于java.sql.Date 2) TIME :等于java.sql.Time 3) TIMESTAMP :等于java.sql.Timestamp

继承关系

Topic和PollTopic是父子类,JPA 采用多种方法来支持实体继承。在父类中必须声明继承实体的映射策略,如代码清单2所示:

代码清单2:继承实体的映射策略

… @Entity(name = "T_TOPIC") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) ① @DiscriminatorColumn(name = "TOPIC_TYPE", discriminatorType =

DiscriminatorType.INTEGER, length = 1) ② @DiscriminatorValue(value="1")③
 

 

 

public class Topic implements Serializable ...{ … }
 

 

对于继承的实体,在javax.persistence.InheritanceType定义了3种映射策略:

SINGLE_TABLE:父子类都保存到同一个表中,通过字段值进行区分。这是我们Topic实体所采用的策略, Topic和PollTopic都保存到同一张表中,通过TOPIC_TYPE字段进行区分,Topic在T_TOPIC表中对应TOPIC_TYPE= 1的记录,而PollTopic对应TOPIC_TYPE=2的记录(稍后在PollTopic实体中指定);区别的字段通过 @DiscriminatorColumn说明,如②所示,区分字段对应该实体的值通过@DiscriminatorValue指定,如③所示;

JOINED:父子类相同的部分保存在同一个表中,不同的部分分开存放,通过表连接获取完整数据;

TABLE_PER_CLASS:每一个类对应自己的表,一般不推荐采用这种方式。

关联关系

我们再继续对PollTopic进行注解,进一步了解实体继承的JPA映射定义:

代码清单3:PollTopic映射描述

package com.baobaotao.domain; … @Entity @DiscriminatorValue(value="2") ① public class PollTopic extends Topic ...{②继承于Topic实体 private boolean multiple; ③ @Column(name = "MAX_CHOICES") private int maxChoices; @OneToMany(mappedBy="pollTopic",cascade=CascadeType.ALL) ④
 

 

 

private Set options = new HashSet(); //省略get/setter方法 }
 

 

在①处,通过@DiscriminatorValue将区分字段TOPIC_TYPE的值为2。由于PollTopic实体继承于Topic实体,其它的元数据信息直接从Topic获得。

JPA规范规定任何属性都默认映射到表中,所以虽然我们没有给③处的multiple属性提供注解信息,但JPA将按照 默认的规则对该字段进行映射:字段名和属性名相同,类型相同。如果我们不希望将某个属性持久化到数据表中,则可以通过@Transient注解显式指定:

@Transient

private boolean tempProp1;

在④处,我们通过@OneToMany指定了一个一对多的关联关系,一个PollTopic包括多个 PollOption对象(我们将在稍后的PollOption中通过ManyToOne描述PollOption和PollTopic的关系,以建立 PollTopic和PollOption的双向关联关系)。 @OneToMany中通过mappedBy属性指定“Many”方类引用“One”方类的属性名,这里mappedBy="pollTopic"表示PollOption实体拥有一个指定PollTopic的pollTopic属性。

下面,我们来看一下Many方PollOption实体类的映射描述:

代码清单4:PollOption映射描述

package com.baobaotao.domain; … @Entity(name="T_POLL_OPTION") public class PollOption implements Serializable ...{ @Id @GeneratedValue(strategy = GenerationType.TABLE) @Column(name = "OPTION_ID")
 

 

 

private int optionId;

@Column(name = "OPTION_ITEM") private String optionItem;

@ManyToOne ① @JoinColumn(name="TOPIC_ID", nullable=false) ② private PollTopic pollTopic; }
 

 

在①处通过@ManyToOne描述了PollOption和PollTopic的多对一关联关系,并通过@JoinColumn指定关联PollTopic实体所对应表的“外键”,如②所示。

当然也可以通过@OneToOne和@ManyToMany指定一对一和多以多的关系,方法差不多,不再赘述。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值