文档章节

Hibernate,JPA 对象关系映射之简单映射策略(转)

hiqj
 hiqj
发布于 2015/02/04 11:43
字数 3102
阅读 12
收藏 0

简单映射

ORM 对象关系的映射,现在已成为炙手可热的技术,深受广大开发人员的追捧。JPA 2.0 作为新的持久化规范,大有统一持久化市场的气势,并且简单易学,尤其是加上 JDK 5 中引入的 Annotation 注解的使用,使得 JPA 的使用更为简单,现在已经为持久化领域开发人员的首选。简单映射,是对象关系映射中最为简单的一种,也是最为基础的一种,学好简单映射是掌握继承映射,关联关系映射的前提所在。


2013 年 5 月 27 日


简单映射

近年来 ORM(Object-Relational Mapping,对象关系映射,即实体对象和数据库表的映射)技术市场热闹非凡,各种各样的持久化框架应运而生,其中影响最大的是 Hibernate 和 Toplink。Sun 公司在充分吸收现有的优秀 ORM 尤其是 Hibernate 框架设计思想的基础上,制定了新的 JPA(Java Persistence API)规范,对现在乱象丛生的持久化市场带来一个标准,大有统一持久化市场的气势。JPA 是通过 JDK5.0 注解或 XML 描述对象 - 关系表的映射关系,并将运行期实体对象持久化到数据库中去。JPA 规范小组的领导人就是 Hibernate 的发明者 Gavin King,JPA 规范的制定过程中大量参考了 Hibernate 的内容,所以如果一个对 Hibernate 很熟悉的人,使用起来 JPA 会是轻车熟路,驾轻就熟的,并且会感觉到更简单一些,这主要得益于 JDK5 中引入的注解(annotation)。

下面就使用注解介绍一下 JPA 的使用。

首先用个小例子介绍一下如何将一个单个 Java 类映射到数据库中。

清单 1. Employee 实体
 @Entity 
 public class Employee implements Serializable { 
    private static final long serialVersionUID = 1L; 
    @Id 
    private Long id; 
    private String name; 
    private int age; 
    private String addree; 
    
   // Getters and Setters 
 }

如果没有 @javax.persistence.Entity 和 @javax.persistence.Id 这两个注解的话,它完全就是一个典型的 POJO 的 Java 类,现在加上这两个注解之后,就可以作为一个实体类与数据库中的表相对应。他在数据库中的对应的表为:

图 1. Employee 表对应的 ER 图

映射规则:

1. 实体类必须用 @javax.persistence.Entity 进行注解;

2. 必须使用 @javax.persistence.Id 来注解一个主键;

3. 实体类必须拥有一个 public 或者 protected 的无参构造函数,之外实体类还可以拥有其他的构造函数;

4. 实体类必须是一个顶级类(top-level class)。一个枚举(enum)或者一个接口(interface)不能被注解为一个实体;

5. 实体类不能是 final 类型的,也不能有 final 类型的方法;

6. 如果实体类的一个实例需要用传值的方式调用(例如,远程调用),则这个实体类必须实现(implements)java.io.Serializable 接口。

将一个 POJO 的 Java 类映射成数据库中的表如此简单,这主要得益于 Java EE 5种引入的  Configuration by Exception 的理念,这个理念的核心就是容器或者供应商提供一个缺省的规则,在这个规则下程序是可以正确运行的,如果开发人员有特殊的需求,需要改变这个默认的规则,那么就是对默认规则来说就是一个异常(Exception)。

如上例所示:默认的映射规则就是数据库表的名字和对应的 Java 类的名字相同,表中列的名字和 Java 类中相对应的字段的名字相同。

现在我们可以改变这种默认的规则:

清单 2. 使用 @Table 和 @Column 注解修改映射规则
 @Entity 
 @Table(name="Workers") 
 public class Employee implements Serializable { 
    private static final long serialVersionUID = 1L; 
    @Id 
    @GeneratedValue 
    private Long id; 
    @Column(name="emp_name", length=30) 
    private String name; 
    @Column(name="emp_age", nullable=false) 
    private int age; 
    @Column(name="emp_address", nullable=false ,unique=true) 
    private String addree; 
    
     // Getters and Setters 

 }

改变默认规则后 在数据库中对应的表为:

图 2. 修改后的表对应的 ER 图

首先我们可以可以使用

@ Javax.persistence.Table 这个注解来改变 Java 类在数据库表种对应的表名。这个注解的定义如下:

清单 3. @Table 注解的定义
 @Target(value = {ElementType.TYPE}) 
 @Retention(value = RetentionPolicy.RUNTIME) 
 public @interface Table { 

    public String name() default ""; 

    public String catalog() default ""; 

    public String schema() default ""; 

    public UniqueConstraint[] uniqueConstraints() default {}; 
 }

从它的定义上可以看出来,这是一个类级别(class level)的注解 , 只能用在类的前面,其中 name 属性的值就是映射到数据库中时对应表的名字,缺省是类名。

@javax.persistence.Column 注解,定义了列的属性,你可以用这个注解改变数据库中表的列名(缺省情况下表对应的列名和类的字段名同名);指定列的长度;或者指定某列是否可以为空,或者是否唯一,或者能否更新或插入。

它的定义如下:

清单 4. @Column 注解的定义
 @Target(value = {ElementType.METHOD, ElementType.FIELD}) 
 @Retention(value = RetentionPolicy.RUNTIME) 
 public @interface Column { 

    public String name() default ""; 

    public boolean unique() default false; 

    public boolean nullable() default true; 

    public boolean insertable() default true; 

    public boolean updatable() default true; 

    public String columnDefinition() default ""; 

    public String table() default ""; 

    public int length() default 255; 

    public int precision() default 0; 

    public int scale() default 0; 
 }

从它的定义可以看出他只可以用在类中的方法前面或者字段前面。

其中 name 属性的值为数据库中的列名,unique 属性说明该烈是否唯一,nullable 属性说明是否可以为空,length 属性指明了该列的最大长度等等。其中 table 属性将在 @SecondaryTable 的使用中已有过介绍。

JPA 中两种注解方式

JPA 中将一个类注解成实体类(entity class)有两种不同的注解方式:基于属性(property-based)和基于字段(field-based)的注解。

1,基于字段的注解,就是直接将注解放置在实体类的字段的前面。前面的 Employee 实体类就是使用的这种注解方式;

2,基于属性的注解,就是直接将注解放置在实体类相应的 getter 方法前面,而不是 setter 方法前面(这一点和 Spring 正好相反)。前面的 Employee 实体类如果使用基于属性注解的方式就可以写成如下形式。

清单 5. 基于属性的注解
 @Entity 
 @Table(name="Employees") 
 public class Employee implements Serializable { 
    private static final long serialVersionUID = 1L; 
   
    private Long id; 
    private String name; 
    private int age; 
    private String addree; 
    
    @Id 
    @GeneratedValue 
    public Long getId() { 
        return id; 
    } 

    public void setId(Long id) { 
        this.id = id; 
    } 
   @Column(name="emp_address", nullable=false ,unique=true) 
    public String getAddree() { 
        return addree; 
    } 

    public void setAddree(String addree) { 
        this.addree = addree; 
    } 
    @Column(name="emp_age", nullable=false) 
    public int getAge() { 
        return age; 
    } 

    public void setAge(int age) { 
        this.age = age; 
    } 
    @Column(name="emp_name", length=30) 
    public String getName() { 
        return name; 
    } 

    public void setName(String name) { 
        this.name = name; 
    } 
 }

他在数据库对应的表结构为:

图 3. 基于属性注解

可以看出,使用两种注解方式在数据库中映射成的表都是相同的。

但是同一个实体类中必须并且只能使用其中一种注解方式,要么是基于属性的注解,要么是基于字段的注解。两种不同的注解方式,在数据库中对应的数据库表是相同的,没有任何区别,开发人员可以根据自己的喜好任意选用其中一种注解方式。

@SecondaryTable 的使用

上面介绍的几个例子都是一个实体类映射到数据库中的一个表中,那么能否将一个实体类映射到数据库两张或更多表中呢表中呢。在有些情况下如数据库中已经存在原始数据类型,并且要求不能更改,这个时候如果能实现一个实体类对应两张或多张表的话,将是很方便的。JPA2.0 中提供了一个 @SecondaryTablez 注解(annotation)就可以实现这种情况。下面用一个例子说明一下这个注解的使用方法:

清单 6. @SecondaryTable 的使用
 @Entity 
 @SecondaryTables({ 
    @SecondaryTable(name = "Address"), 
    @SecondaryTable(name = "Comments") 
 }) 
 public class Forum implements Serializable { 
    private static final long serialVersionUID = 1L; 
    @Id 
    @GeneratedValue 
    private Long id; 
    private String username; 
    private String password; 
    @Column(table = "Address", length = 100) 
    private String street; 
    @Column(table = "Address", nullable = false) 
    private String city; 
    @Column(table = "Address") 
    private String conutry; 
    @Column(table = "Comments") 
    private String title; 
    @Column(table = "Comments") 
    private String Comments; 
    @Column(table = "Comments") 
    private Integer comments_length; 
    
    // Getters and Setters 
 }

清单 5 中定义了两个 Secondary 表,分别为 Address 和 Comments,同时在 Forum 实体类中也通过 @Column 注解将某些子段分别分配给了这两张表,那些 table 属性得值是 Adress 的就会存在于 Address 表中,同理 table 属性的值是 Comments 的就会存在于 Comments 表中。那些没有用 @Column 注解改变属性默认的字段将会存在于 Forum 表中。图 4 就是持久化后在数据库中对应的表的 ER 图,从图中可看出来,这些字段如我们预料的一样被映射到了不同的表中。

图 4. @SecondaryTable 持久化后对赢得 ER 图

嵌套映射

在使用嵌套映射的时候首先要有一个被嵌套的类,清单 5 中 Address 实体类使用 @Embeddable 注解,说明这个就是一个可被嵌套的类,与 @EmbeddedId 复合主键策略中的主键类(primary key class)稍有不同的是,这个被嵌套类不用重写 hashCode() 和 equals() 方法,复合主键将在后面进行介绍。

清单 7. 被嵌套类
 @Embeddable 
 public class Address implements Serializable  { 

    private String street; 
    private String city; 
    private String province; 
    private String country; 

    // Getters and Setters 

 }

清单 6 中 Employee 实体类是嵌套类的拥有者,其中使用了 @Embedded 注解将 Address 类嵌套进来了。

清单 8. 嵌套类的使用者
 @Entity 
 public class Employee implements Serializable { 
    private static final long serialVersionUID = 1L; 
    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id; 
    private String name; 
    private String email; 
    private String cellPhone; 
    @Embedded 
    private Address address; 

    // Getters and Setters 
   }

清单 7 是持久化后生成的数据库表,可以看出被嵌套类的属性,也被持久化到了数据库中,默认的表名就是嵌套类的拥有者的类名。

清单 9. 使用嵌套类生成的表结构
 CREATE TABLE `employee` ( 
  `ID` bigint(20) NOT NULL, 
  `EMAIL` varchar(255) default NULL, 
  `NAME` varchar(255) default NULL, 
  `CELLPHONE` varchar(255) default NULL, 
  `STREET` varchar(255) default NULL, 
  `PROVINCE` varchar(255) default NULL, 
  `CITY` varchar(255) default NULL, 
  `COUNTRY` varchar(255) default NULL, 
  PRIMARY KEY  (`ID`) 
 ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

被嵌套类的注解方式,field 方式或者 property 方式,依赖于嵌套类的拥有者。上面例子中的 Employee 实体类采用的是 field 注解方式,那么在持久化的过程中,被嵌套类 Address 也是按照 field 注解方式就行映射的。

我们也可以通过 @Access 注解改变被嵌套类映射方式,清单 8 通过使用 @Access 注解将 Address 被嵌套类的注解方式设定成了 property 方式。清单 9 Employee 仍然采用 filed 注解方式。这种情况下,持久化的时候,被嵌套类就会按照自己设定的注解方式映射,而不会再依赖于嵌套类的拥有者的注解方式。但这并不会映射的结果。

清单 10. 基于 property 方式注解的被嵌套类
 @Embeddable 
 @Access(AccessType.PROPERTY) 
 public class Address implements Serializable  { 

    private String street; 
    private String city; 
    private String province; 
    private String country; 

    @Column(nullable=false) 
    public String getCity() { 
        return city; 
    } 

    public void setCity(String city) { 
        this.city = city; 
    } 
   @Column(nullable=false,length=50) 
    public String getCountry() { 
        return country; 
    } 

    public void setCountry(String country) { 
        this.country = country; 
    } 
    @Column(nullable=false,length=20) 
    public String getProvince() { 
        return province; 
    } 

    public void setProvince(String province) { 
        this.province = province; 
    } 

    public String getStreet() { 
        return street; 
    } 

    public void setStreet(String street) { 
        this.street = street; 
    } 
 }
清单 11. 基于 field 方式注解
 @Entity 
 @Access(AccessType. FIELD) 
 public class Employee implements Serializable { 
    private static final long serialVersionUID = 1L; 
    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id; 
    private String name; 
    private String email; 
    private String cellPhone; 
    @Embedded 
    private Address address; 

    // Getters and Setters 
   }

事先设定被嵌套类的注解方式,是一种应该大力提倡的做法,因为当同一个类被不同的注解方式的类嵌套时,可能会出现一些错误。

总结

简单映射是 ORM,也就是对象关系映射中较为简单的一种,他只是数据库表与类之间的一一对应,并未涉及表之间的关系,也就未涉及类与类之间的关系,也可以说是其他如继承映射,关联关系映射的基础,所以说熟悉并掌握简单关系映射还是很有必要的。


本文转载自:http://www.ibm.com/developerworks/cn/java/j-lo-jpasimpemap/

共有 人打赏支持
hiqj
粉丝 24
博文 209
码字总数 93039
作品 0
渝北
程序员
JPA和Hibernate到底是什么关系???

(文章借用了其他几篇博客中的内容,因为是初学者,许多概念性的关系自己不能完全理解,就将大神的搬了过来,如有不当,可留言,本人自会妥善处理,谢谢) 在学习框架的过程中,发现学的东西...

moson
昨天
0
0
Spring Data JPA 与 MyBatis 适用场景对比

两种框架自己都用过一段时间,现在想总结一下各自适用的场景,部分比较取自群友分享 框架简介 Spring Data JPA是Spring Data的子模块。使用Spring Data,使得基于“repositories”概念的JPA...

小海bug
07/31
0
0
Hibernate注解(一)类级别注解

1、Hibernate注解简介 2、JPA与Hibernate的关系 3、Hibernate注解分类 4、@Entity 5、@Table 6、@Embeddable 7、schema与catalog 一、Hibernate注解简介 使用注解的目的:为了简化繁琐的ORM...

leeqico
06/28
0
0
第11章—使用对象关系映射持久化数据—SpringBoot+SpringData+Jpa进行查询修改数据库

SpringBoot+SpringData+Jpa进行查询修改数据库 JPA由EJB 3.0软件专家组开发,作为JSR-220实现的一部分。但它又不限于EJB 3.0,你可以在Web应用、甚至桌面应用中使用。JPA的宗旨是为POJO提供持...

charlyFeng
08/06
0
0
理解JPA注解@GeneratedValue

一、JPA通用策略生成器 通过annotation来映射hibernate实体的,基于annotation的hibernate主键标识为@Id, 其生成规则由@GeneratedValue设定的.这里的@id和@GeneratedValue都是JPA的标准用法,...

一生只为虞美人
07/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

函数调用约定 (cdecl stdcall)

函数调用约定 (cdecl stdcall) 在 C 语言里,我们通过阅读函数声明,就知道怎么携带参数去调用函数,也能在函数体定义内使用这些参数。但是 CPU 并不直接完成函数调用的传参操作,这需要人为...

傅易
2分钟前
0
0
Python 核心编程 (全)

浅拷贝和深拷贝 1.浅拷贝:是对于一个对象的顶层拷贝,通俗的理解是:拷贝了引用,并没有拷贝内容。相当于把变量里面指向的一个地址给了另一个变量就是浅拷贝,而没有创建一个新的对象,如a=b...

代码打碟手
5分钟前
0
0
mysql5.7 修改datadir

mysql 的默认存储路径为 /var/lib/mysql ,修改后为 /data/mysql 关闭服务 service mysql stop 复制mysql 数据文件到新的目录 cp -rp /var/lib/mysql /data 查看原目录的权限,如果新目...

hotsmile
21分钟前
0
0
证书安装指引之Tomcat 证书部署

Tomcat 证书部署 0 申请证书 1 获取证书 如果申请证书时有填写私钥密码,下载可获得Tomcat文件夹,其中有密钥库 www.domain.com.jks; 如果没有填写私钥密码,证书下载包的Tomcat文件夹中包括...

吴伟祥
26分钟前
0
0
ConcurrentHashMap1.7和1.8的底层不同实现

1.Hashmap和HashTable在线程安全方面的优劣? Hashmap多线程会导致HashMap的Entry链表形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为空,就会产生死循环获取Entry。 Hash...

刘祖鹏
41分钟前
6
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部