首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

3.对象关系映射-part2

2012-10-25 
三.对象关系映射-part2基本映射?一、@Table?@javax.persistence.Table注解可以改变实体类默认映射的表名把b

三.对象关系映射-part2

基本映射

?

一、@Table

?

@javax.persistence.Table注解可以改变实体类默认映射的表名

把book实体映射给T_BOOK表

?

@Entity@Table(name = "t_book")public class Book {@Idprivate Long id;private String title;private Float price;private String description;private String isbn;private Integer nbOfPage;private Boolean illustrations;public Book() {}// Getters, setters}
?

?

注意,根据数据库的不同,实体类默认映射到数据库的表名,有可能全大写,有可能全小写。


?

二、@SecondaryTable


我们可以用该注解定义实体关联的一个或多个次表,用@Column(table= "XXX")指定属性指向的次表,看下面的例子:

?

@Entity@SecondaryTables({ @SecondaryTable(name = "city"),@SecondaryTable(name = "country") })public class Address {@Idprivate Long id;private String street1;private String street2;@Column(table = "city")private String city;@Column(table = "city")private String state;@Column(table = "city")private String zipcode;@Column(table = "country")private String country;// Constructors, getters, setters}

?

?

该实体映射图:

?

3.对象关系映射-part2

?

?

每个表都有相同的主键。

注意:当使用次表时,你应该考虑性能问题。每次你访问这样的一个实体,持久化实现类访问多个表,用join的方式将他们关联。当你有大数据对象(BLOBs),用次表将大数据对象隔离是很好的。


三、主键


@javax.persistence.Id注释属性可以是以下类型:


1.基本Java类型:byte,int,short,long,char。

2.包装的基本Java类型:Byte,Integer,Short,Long,Character。

3.基本Java类型或包装的基本Java类型数组:int[],Integer[]等等。

4.字符串,数字类型和日期:java.lang.String,java.math.BigInteger,java.util.Date,java.sql.Date


主键值可以手动赋值,也可以通过@GeneratedValue注解自动赋值。该注解有4个值:


    SEQUENCE和IDENTITY是根据数据库的sequence或identity自增主键值

    TABLE是用数据库表来储存主键(主键名和主键值)

    AUTO是默认值,根据特定数据库选择相应的主键生成策略


四、复合主键


在JPA里我们有2种方式定义主键:@EmbeddedId和@IdClass

通常将复合主键相关属性,单独抽取出来,建立一个独立的类,这个类就是主键类,要求:

1.复合主键必须重写equals和hashcode方法(JPA查找缓存的持久化对象)

2.必须实现Serializable接口


@EmbeddedId

查看下例:

?

@Embeddablepublic class NewsId implements Serializable{private String title;private String language;// Constructors, getters, setters, equals, and hashcode}

?

@Entitypublic class News {@EmbeddedIdprivate NewsId id;private String content;// Constructors, getters, setters}

?

?

?

看看我们怎么用复合主键查询:


NewsIdpk = new NewsId("Richard Wright has died", "EN")

Newsnews = em.find(News.class, pk);


@IdClass


以这种方式的复合主键类不需要任何注解:

?

public class NewsId implements Serializable{private String title;private String language;// Constructors, getters, setters, equals, and hashcode}
?
@Entity@IdClass(NewsId.class)public class News {@Idprivate String title;@Idprivate String language;private String content;// Constructors, getters, setters, equals, and hashcode}

?

?

@EmbeddedId和@IdClass这两种方式都被映射为同一个表结构

?

CREATE TABLE `news` (  `language` varchar(255) NOT NULL,  `title` varchar(255) NOT NULL,  `content` varchar(255) DEFAULT NULL,  PRIMARY KEY (`language`,`title`)) 

?

?

明显的不同是JPQL查询:


用@IDClass时:

selectn.title from News n


用@EmbeddedID时:

selectn.newsId.title from News n



五、属性


实体属性可以是以下类型:

    Java基本数据类型(int,double,float等)及其包装类(Integer,Double,Float等)

    字节和字符数组(byte[],Byte[],char[],Character[])

    字符串,大数据类型,时间类型(java.lang.String,java.math.BigInteger,java.math.BigDecimal,java.util.Date,java.util.Calendar,java.sql.Date,java.sql.Time,java.sql.Timestamp)

    枚举类和用户自定义实现Serialiable接口类型

    基础集合类和嵌入类型


六、@Basic


可选注解@javax.persistence.Basic是映射数据库列的最基本类型。

?

@Target({METHOD, FIELD})@Retention(RUNTIME)public @interface Basic {    FetchType fetch() default EAGER;    boolean optional() default true;}

?

?

这个注解有2个参数:optional和fetch.


optional元素指定属性的值是否为空(忽略基本类型,因为基本类型是非空类型)

fetch元素有2个值:LAZY或EAGER。指定属性数据是否是懒加载或立即加载。


比如我们有一个Track(音轨)实体,有一个WAV属性(WAV文件是一个BLOB,可能有几兆),当我们访问Track实体时,我们不想立即加载WAV文件。

?

@Entitypublic class Track {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;private String title;private float duration;@Basic(fetch = FetchType.LAZY)@Lobprivate byte[] wav;private String description;// Constructors, getters, setters}

?

?

七、@Column


@Column注解元素

?

?

@Target({METHOD, FIELD})@Retention(RUNTIME)public @interface Column {    String name() default "";    /**     * (Optional) Whether the column is a unique key.  This is a     * shortcut for the <code>UniqueConstraint</code> annotation at the table     * level and is useful for when the unique key constraint     * corresponds to only a single column. This constraint applies     * in addition to any constraint entailed by primary key mapping and     * to constraints specified at the table level.     */    boolean unique() default false;    /**     * (Optional) Whether the database column is nullable.     */    boolean nullable() default true;    /**     * (Optional) Whether the column is included in SQL INSERT     * statements generated by the persistence provider.     */    boolean insertable() default true;    /**     * (Optional) Whether the column is included in SQL UPDATE     * statements generated by the persistence provider.     */    boolean updatable() default true;    /**     * (Optional) The SQL fragment that is used when     * generating the DDL for the column.     * <p> Defaults to the generated SQL to create a     * column of the inferred type.     */    String columnDefinition() default "";    /**     * (Optional) The name of the table that contains the column.     * If absent the column is assumed to be in the primary table.     */    String table() default "";    /**     * (Optional) The column length. (Applies only if a     * string-valued column is used.)     */    int length() default 255;    /**     * (Optional) The precision for a decimal (exact numeric)     * column. (Applies only if a decimal column is used.)     * Value must be set by developer if used when generating     * the DDL for the column.     */    int precision() default 0;    /**     * (Optional) The scale for a decimal (exact numeric) column.     * (Applies only if a decimal column is used.)     */    int scale() default 0;}

?

?

你可以通过该注解修改列名,列字段大小,是否为空,unique(唯一),允许它的值被更新、插入。

我们重新定义原来的Book实体

?

@Entitypublic class Book {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@Column(name = "book_title", nullable = false, updatable = false)private String title;private Float price;@Column(length = 2000)private String description;private String isbn;@Column(name = "nb_of_page", nullable = false)private Integer nbOfPage;private Boolean illustrations;// Constructors, getters, setters}
?

BOOK表DDL:

?

CREATE TABLE `book` (  `id` bigint(20) NOT NULL AUTO_INCREMENT,  `description` longtext,  `illustrations` bit(1) DEFAULT NULL,  `isbn` varchar(255) DEFAULT NULL,  `nb_of_page` int(11) NOT NULL,  `price` float DEFAULT NULL,  `book_title` varchar(255) NOT NULL,  PRIMARY KEY (`id`)) 

?

updatable和insertable默认值是true,当他们的值是false时,产生的SQL语句将不包含相应的列。


八、@Temporal


@javax.persistence.Temporal注解用来表示时间日期,可以是:DATE,TIME,TIMESTAMP.


看下例:

?

@Entitypublic class Customer {@Id@GeneratedValueprivate Long id;private String firstName;private String lastName;private String email;private String phoneNumber;@Temporal(TemporalType.DATE)private Date dateOfBirth;@Temporal(TemporalType.TIMESTAMP)private Date creationDate;// Constructors, getters, setters}

?

九、@Transient


JPA中,注解@Entity的类,所有的属性都会被自动映射到相应的表中。如果你不需映射类中的某个属性,可以使用@javax.persistence.Transient注解。

?

@Entitypublic class Customer {@Id@GeneratedValueprivate Long id;private String firstName;private String lastName;private String email;private String phoneNumber;@Temporal(TemporalType.DATE)private Date dateOfBirth;@Transientprivate Integer age;@Temporal(TemporalType.TIMESTAMP)private Date creationDate;// Constructors, getters, setters}

?

age属性映射被忽略


十、@Enumerated


我们看一下CreditCardType枚举类

?

public enum CreditCardType {VISA,MASTER_CARD,AMERICAN_EXPRESS}

?

?

枚举类的值是常量,并且以声明的顺序赋予int值。编译时,VISA赋予0,MASTER_CARD赋予1,AMERICAN_EXPRESS赋予2.持久化实现默认将枚举类型映射为Integer。

?

@Entity@Table(name = "credit_card")public class CreditCard {@Idprivate String number;private String expiryDate;private Integer controlNumber;private CreditCardType creditCardType;// Constructors, getters, setters}

?

如果从中加入新的枚举常量,保存在数据库中的一些值将不再匹配枚举。一个好的办法是保存字符值,而不是顺序值。通过@Enumerated(EnumType.STRING),顺序值(ORDINAL是默认值):

?

@Entity@Table(name = "credit_card")public class CreditCard {@Idprivate String number;private String expiryDate;private Integer controlNumber;@Enumerated(EnumType.STRING)private CreditCardType creditCardType;// Constructors, getters, setters}
?

?

十一、访问类型


到现在为止,我们标注类(@Entity,@Table)和属性(@Basic,@Column,@Temporal等),我们将注解应用在字段上(fieldaccess)也可以注解在相应的属性上(propertyaccess:gettermethod).例:我们将注解@Id标注在id字段上,同样也可以标注在getId()的方法上。这很大程度上是个人偏好。我趋向于注解在属性上(注解getters),这样可以很容易阅读实体类拥有的字段。在这个教程里,为了可读性,我们把注解标注在字段上。


当我们把注解标注在字段上,实体类的访问类型就为字段类型,持久化实现将映射所有的字段(没有标注@Transient)并将其持久化。

当我们把注解标注在属性(注解getters)上,实体类的访问类型就为属性类型,持久化实现将映射所有的属性(没有标注@Transient)并将其持久化。

如果没有通过显示指定访问类型,我们不能既标注字段又标注属性,否则会报错。


例:


标注字段:


@Entitypublic class Customer {@Id@GeneratedValueprivate Long id;@Column(name = "first_name", nullable = false, length = 50)private String firstName;@Column(name = "last_name", nullable = false, length = 50)private String lastName;private String email;@Column(name = "phone_number", length = 15)private String phoneNumber;// Constructors, getters, setters}
?


标注属性:

?

@Entitypublic class Customer {private Long id;private String firstName;private String lastName;private String email;private String phoneNumber;// Constructors@Id@GeneratedValuepublic Long getId() {return id;}public void setId(Long id) {this.id = id;}@Column(name = "first_name", nullable = false, length = 50)public String getFirstName() {return firstName;}public void setFirstName(String firstName) {this.firstName = firstName;}@Column(name = "last_name", nullable = false, length = 50)public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}@Column(name = "phone_number", length = 15)public String getPhoneNumber() {return phoneNumber;}public void setPhoneNumber(String phoneNumber) {this.phoneNumber = phoneNumber;}}

?

?

通过注解@Access可以指定标注类型,有2种参数值:AccessType.FIELD,AccessType.PROPERTY

?

@Entity@Access(AccessType.FIELD)public class Customer {@Id@GeneratedValueprivate Long id;@Column(name = "first_name", nullable = false, length = 50)private String firstName;@Column(name = "last_name", nullable = false, length = 50)private String lastName;private String email;@Column(name = "phone_number", length = 15)private String phoneNumber;// Constructors, getters, setters@Access(AccessType.PROPERTY)@Column(name = "phone_number", length = 555)public String getPhoneNumber() {return phoneNumber;}public void setPhoneNumber(String phoneNumber) {this.phoneNumber = phoneNumber;}}

?

?

标注在属性上的@Access将覆盖实体类级别的访问类型。所以最后"phone_number"的列上为555(VARCHAR(555)).


十二、基本类型集合映射


在JPA2.0中,@ElementCollection注解指定下列基本类型集合:

    java.util.Collection

    java.util.Set

    java.util.List


持久化实现默认会生成一个"实体类名_集合属性名"的集合表来储存集合值。

我们可以通过@CollectionTable注解来修改集合表名,通过@Column来修改集合表列名字(默认是"集合的属性名")


例:

?

@Entitypublic class Book {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;private String title;private Float price;private String description;private String isbn;private Integer nbOfPage;private Boolean illustrations;@ElementCollection(fetch = FetchType.LAZY)@CollectionTable(name = "Tag")@Column(name = "Value")private List<String> tags = new ArrayList<String>();// Constructors, getters, setters}

?

?

映射表:

?

3.对象关系映射-part2

?


?

十三、基本类型Map映射


在JPA1.0中,Map映射中的Keys必须是基本类型,Values必须是实体。现在,无论是Keys还是Values都可以包含基本数据类型,嵌入对象,和实体。


和基本类型集合映射一样,我们用@ElementCollection注解标注属性类型是Map类型。

通过@CollectionTable注解来修改Map表名(默认是"实体类名_Map的属性名"),

通过@Column来修改Map表value列名字(默认是"Map的属性名")

通过@MapKeyColumn来修改Map表key列名字(默认是"Map的属性名_KEY")


例:

?

@Entitypublic class CD {@Id@GeneratedValueprivate Long id;private String title;private Float price;private String description;@Lobprivate byte[] cover;@ElementCollection@CollectionTable(name = "track")@MapKeyColumn(name = "position")@Column(name = "title")private Map<Integer, String> tracks = new HashMap<Integer, String>();// Constructors, getters, setters}

?

?

映射表:

?

3.对象关系映射-part2

热点排行