三.对象关系映射-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}
该实体映射图:
http://dl.iteye.com/upload/attachment/496208/51c3c429-21f9-34cd-9383-0a515421f246.png
每个表都有相同的主键。
注意:当使用次表时,你应该考虑性能问题。每次你访问这样的一个实体,持久化实现类访问多个表,用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}
映射表:
http://dl.iteye.com/upload/attachment/496240/10f55a54-f7d2-363e-89fa-b4d09ad063b4.png
十三、基本类型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}
映射表:
http://dl.iteye.com/upload/attachment/496245/567a316c-be89-389f-88a3-7da89bb346fb.png
页:
[1]