JPA常见注解

KinglyJn      2012-12-16

@Entity

注册在类头上,将一个类声明为一个实体bean(即一个持久化POJO类) 。

@Table

注册在类头上,注解声明了该实体bean映射指定的表(table)。

@Id

用来注册主属性,标识这个属性是实体类的唯一识别的值

注意:这个注解只能标注单一列构成的主键,如tbl_grade那种有两个字段组成的联合主键由其他注解标识。

<id name= "uuid">
    <generator class= "assigned"/>
</id>

@Id 只是标识这个属性是主键,但是并没有指出其生成策略,如上例中的assigned就是由程序员指定的生成策略。
如果仅仅写出@Id,即是使用assigned生成略,如:

//如果想使用Oracle支持的sequence取主键,
//必须通过@GeneratedValue来指定生成策略,
//而由@SequenceGenerator指定如何使用sequence。
@Id  
@Column 
@SequenceGenerator(      //可以定义在属性上,也可以定义在类上
    name = "generator1", //定义名为generator1的主键生成策略
    sequenceName="seq_a" //引用名为数据库中名为seq_a的序列    
    allocationSize = 1,  //每次sequence加1  
)  
@GeneratedValue(  
    strategy = GenerationType.SEQUENCE, //使用sequence生成主键  
    generator = "generator1" //引用下面名为gernator的生成策略  
)  
private int uuid;    


@GeneratedValue

用来注册主属性的生成策略
示例:@GeneratedValue(strategy=GenerationType.AUTO)
strategy: 表示主键生成策略,有AUTO,INDENTITY,SEQUENCE 和 TABLE 4种, 分别表示让ORM框架自动选择,根据数据库的Identity字段生成,根据数据库表的Sequence字段生成,以有根据一个额外的表生成主键,默认为AUTO。一般我们设置为默认,也就是自动增长,即GenerationType.AUTO即可,让其自动增长,所以我们也可以不写。
generator:表示主键生成器的名称,这个属性通常和ORM框架相关,例如,Hibernate可以指定uuid等主键生成方式.一般也不写,取默认值

@Basic

可以看做是基础修饰,默认每一个属性都有。默认如果不写的话为:@Basic(fetch=FetchType.EAGER,optional=true) fetch表示的是加载策略/读取策略,有EAGER和LAZY两种,分别表示主支抓取和延迟加载,默认为EAGER。但是,如果想要使用懒加载,则必须使用load方法,使用get方式是不可以的。当使用Query的时候会不会懒加载。
optional表示的是该属性是否允许为null,默认为true,即允许为null。

@Column

用来注册属性,标识属性对应的字段

@Column(  
    name="columnName";                                (1)  
    boolean unique() default false;                   (2)  
    boolean nullable() default true;                  (3)  
    boolean insertable() default true;                (4)  
    boolean updatable() default true;                 (5)  
    String columnDefinition() default "";             (6)  
    String table() default "";                        (7)  
    int length() default 255;                         (8)  
    int precision() default 0; // decimal precision   (9)  
    int scale() default 0; // decimal scale           (10)  

(1) name 可选,列名(默认值是属性名)
(2) unique 可选,是否在该列上设置唯一约束(默认值false)
(3) nullable 可选,是否设置该列的值可以为空(默认值false)
(4) insertable 可选,该列是否作为生成的insert语句中的一个列(默认值true)
(5) updatable 可选,该列是否作为生成的update语句中的一个列(默认值true)
(6) columnDefinition 可选: 为这个特定列覆盖SQL DDL片段 (这可能导致无法在不同数据库间移植)
(7) table 可选,定义对应的表(默认为主表)
(8) length 可选,列长度(默认值255)
(8) precision 可选,列十进制精度(decimal precision)(默认值0)
(10) scale 可选,如果列十进制数值范围(decimal scale)可用,在此设置(默认值0)

@Version

标识这个属性用来映射乐观锁的version

@Transient

标识这个属性不用持久化

以上的@Id、@GeneratedValue、@Column、@Version,可以用来注册属性,既可以写在Java类的属性上,也可以注册在属性对应的getter上。@Transient注册在多余的属性或多余的getter上,但是必须与以上的@Column等对应。


@Embedded【大对象的属性头上】

Embedded将几个字段组合成一个类,并作为整个Entity的一个属性。
例如User包括id,name,city,street,zip属性。我们希望city,street,zip属性映射为Address对象。这样,User对象将具有id,name和address这三个属性。Address对象必须定义为@Embededable:

@Embeddable //可以被嵌入的
public class Address {
	city,street,zip
}


@Entity
public class User {
    @Embedded   //将address的每一个属性嵌入到User的表中,Address类没有自己的表
    public Address getAddress() {
        //..........
    }
}


@Enumerated

指明枚举类型的属性存储的类型(字符串、索引)

@Enumerated(EnumType.STRING) 
private Gender gender=Gender.MAN;


@Lob

大文本存储。数据库存储类型为LONGTEXT

@Lob
public String info;


@MappedSuperclass

基于代码复用和模型分离的思想,在项目开发中使用JPA的@MappedSuperclass注解将实体类的多个属性分别封装到不同的非实体类中。

  • @MappedSuperclass注解只能标准在类上:@Target({java.lang.annotation.ElementType.TYPE})

  • 标注为@MappedSuperclass的类将不是一个完整的实体类,他将不会映射到数据库表,但是他的属性都将映射到其子类的数据库字段中。

  • 标注为@MappedSuperclass的类不能再标注@Entity或@Table注解,也无需实现序列化接口。 但是如果一个标注为@MappedSuperclass的类继承了另外一个实体类或者另外一个同样标注了@MappedSuperclass的类的话,他将可以使用@AttributeOverride或@AttributeOverrides注解重定义其父类(无论是否是实体类)的属性映射到数据库表中的字段。
    比如可以重定义字段名或长度等属性,使用@AttributeOverride中的子属性@Column进行具体的定义。

    注意:对于其父类中标注@Lob注解的属性将不能重载,并且@AttributeOverride里的@Column设置都将不起作用。JPA规范中对@Lob注解并没有说明不能同时标注@Column注解,但是在实际使用中Hibernate JPA不支持这中标注方式。

  • 此外,这样的类还可以直接标注@EntityListeners实体监听器,他的作用范围仅在其所有继承类中,并且实体监听器同样可以保被其子类继承或重载。
  • 标注为@MappedSuperclass的类其属性最好设置为protected或default类型的,以保证其同一个包下的子类可以直接调用它的属性。便于实体监听器或带参数构造函数的操作。
  • 由于标注为@MappedSuperclass的类将不是一个完整的实体类,因此其不能标注@Table,并且无法使用@UniqueConstraint设置字段的Unique属性,这一点以及对属性类型重载(如重载标注为@Lob的属性)的支持jpa规范还有待改进。
  • 可以同时标注@DiscriminatorValue注解,以设定实体子类的实体标识字段的值。该属性一般是在实体继承的时候使用的较多,但是在实体映射的时候可以不用设置。
  • 比较实体继承与实体映射的区别:实体继承的三种策略分别是:SINGLE_TABLE(所有继承的实体都保存在同一张数据库表中),JOINED(每个实体子类都将保存在一个单独的表中),TABLE_PER_CLASS(有继承关系的所有实体类都将保存在单独的表中)。实体映射最类似于JOINED实体继承方式,他也是将实体子类单独保存为一张表,但是两者最大的区别就在于:查询的时候JOINED使用的是多态查询,在查询父类时其所有实体子类的数据也将同时被查询出,因此查询时间和性能都将有影响。但是实体映射方式的数据库查询等同于没有实体继承关系的查询,也就是说,他仅在实体层体现出一种继承的关系却并没有在数据库中体现这样一种关系,他的操作都是独立的并且将不会影响到实体子类。
//父类Cat
package com.xj.model;  
import javax.persistence.GeneratedValue;  
import javax.persistence.Id;  
import javax.persistence.MappedSuperclass;  
@MappedSuperclass  
public class Cat {  
    private Long id;  
    private String name;  
    @Id  
    @GeneratedValue  
    public Long getId() {  
        return id;  
    }  
    public void setId(Long id) {  
        this.id = id;  
    }  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
}  


//子类1 BeijingCat 
package com.xj.model;  
import javax.persistence.Entity;  
import javax.persistence.Table;  
@Entity  
@Table(name="t_beijingcat")  
public class BeijingCat extends Cat {  
    private String beijing;  
  
    public String getBeijing() {  
        return beijing;  
    }  
  
    public void setBeijing(String beijing) {  
        this.beijing = beijing;  
    }  
} 


//子类2 ShanghaiCat 
package com.xj.model;  
import javax.persistence.Entity;  
import javax.persistence.Table;  
@Entity  
@Table(name="t_shanghaiCat")  
public class ShanghaiCat extends Cat {  
    private String shanghai;  
  
    public String getShanghai() {  
        return shanghai;  
    }  
  
    public void setShanghai(String shanghai) {  
        this.shanghai = shanghai;  
    }  
}  


//结果生成 两张表t_beijingcat、 t_shanghaicat 
//如果不用@MappedSuperclass 而用@Entity替换的话,只会生成一张cat表,字段有:id,name,shanghaicat,bejingcat   


@ManyToOne

示例:@ManyToOne(fetch=FetchType, cascade=CascadeType)

表示的是一个 多对一 的映射。该注解标注的属性通常是数据库表的外键。

  1. optional:是否允许该字段为null,即这个属性值是否可以为null,就是说是不是一定要有外键,比如说学生对班级,学生不可以没有班级,所以为false,即不能够为null。
  2. fetch,默认为FetchType.EAGER 还有FetchType.LAZY(懒加载)
  3. cascade:这边比较经常用。表示默认的级联操作方式。有ALL(all)、PERSIST(persist)、MERGE(merge)、REFRESH(refresh)和REMVOE(remove)中的若干组合,默认为无级联操作。
    CascadeType各个值的含义:
    • CascadeType.REFRESH(refresh): 表示的是级联刷新,当多个用户同时操作一个实体,为了用户取到的数据是实时的,在用实体中的数据之前就可以调用一下refresh()方法!
    • CsacadeType.REMOVE(remove): 级联删除,当调用remove()方法删除Order实体时会先级联删除OrderItem的相关数据
    • CascadeType.MERGE(merge): 级联更新,当调用了Merger()方法,如果Order中的数据改变了会相应的更新到OrderItem中的数据
    • CascadeType.ALL: 包含以上所有级联属性。
      注:以上几种级联操作,只能实在满足数据库的约束时才能生效,比如上边的Order和OrderItem存在主外键关联,所以执行REMOVE()方法时是不能实现级联删除的。 CascadeType.PERSIST(persist):级联保存,当调用了Persist()方法,会级联保存相应的数据。
  4. TagertEntity:表示该属性关联的实体类型,该属性通常不必指定,ORM框架根据属性类型自动判断targetEntity
//订单Order和用户User是一个ManyToOne的关系
//在Order类中定义
@ManyToOne()
@JoinColumn(name="USER")
public User getUser() {
    return user;
}


@JoinColumn(可选)

这个量的作用就是用来定义外键在我们这个表中的属性名。只能够在有外键处才能够使用。@JoinColumn(name=”USER”)
如果我们没有定义这个属性,则默认的外键的属性名,如:实体Order有一个user属性来关联实体User, 则Order的user属性为一个外键, 其默认的名称为实体User的名称+下划线+实体User的主键名称。

@OneToMany

描述的是一个一对多的关联,该属性应该为集体类型,在数据库中并没有实际字段。

  • fetch:表示的是抓取策略,默认为FetchType.LAZY,因为关联的多个对象通常不必从数据库中预先读取到内存
  • cascade:表示级联操作策略,对于OneToMany类型的关联非常重要,通常该实体更新或删除的时候,其关联的实体也应当被更新或删除

例如:实体User和Order是OneToMany的关系,则实体User被删除时,其关联的实体Order也应该被全部删除

@OneTyMany(cascade=ALL)
public List getOrders() {
    return orders;
}


@OneToOne

OneToOne描述的是一个一对一的关联

  • fetch:表示的是抓取策略,默认为LAZY
  • cascade:表示的是级联删除策略
@OneToOne(fetch=FetchType.LAZY)
public Blog getBlog() {
    return blog;
}


@ManyToMany

ManyToMany描述的是一个多对多关联。多对多关联实际上是两个一对多关联,但是在ManyToMany描述中,中间表是由ORM框架自动处理

  • targetEntity:表示多对多关联的另一个实体类的全名,例如:package.Book.class
  • mappedBy:表示多对多关联的另一个实体类对应的属性集合名称

User实体表示用户,Book实体表示书籍,为了描述用户收藏的书籍,可以在User和Book之间建立ManyToMany关联

@Entity
public class User {
    private List books;
    
    @ManyToMany(targetEntity=package.Book.class)
    public List getBooks() {
        return books;
    }
    public void setBooks(List books) {
        this.books=books;
    }
}

@Entity
public class Book {
    private List users;
    
    @ManyToMany(targetEntity=package.Users.class, mappedBy="books")
    public List getUsers() {
        return users;
    }
    public void setUsers(List users) {
        this.users=users;
    }
}

两个实体间相互关联的属性必须标记为@ManyToMany,并相互指定targetEntity属性,需要注意的是,有且只有一个实体的@ManyToMany注解需要指定mappedBy属性,指向targetEntity的集合属性名称 利用ORM工具自动生成的表除了User和Book表外,还自动生成了一个User_Book表,用于实现多对多关联。

标准的1:1

<!--主1【tbl_product】 -->  
<one-to-one name="info" cascade="all"/>  

<!--从1【tbl_product_info】 -->  
<id name="uuid">  
    <generator class="foreign【写死,使用外来生成策略】">  
        <param name="property">product【引用自己的Java属性名】</param>  
    </generator>  
</id>  
<one-to-one name="product"/>  
//主1【tbl_product】:  
@OneToOne(cascade=CascadeType.ALL)  
@PrimaryKeyJoinColumn  
private ProductInfoModel info;  


//从1【tbl_product_info】:  
@Id  
@Column  
@GeneratedValue(generator="copy【引用生成策略】")  
@GenericGenerator(name="copy【定义生成策略】", 
    strategy="foreign【写死,使用外来策略】", 
    parameters=@Parameter(name="property", 
    value="product【引用自己的Java属性】")
)  
private int uuid;  
@OneToOne(mappedBy="info【引用对方的Java属性】")  
private ProductModel product;  

或者

<!-- 主1【tbl_product】-->  
<one-to-one name="info" foreign-key="puuid【对方的数据库外键列名】" cascade="all"/>  

<!-- 从1【tbl_product_info】-->  
<many-to-one name="product" column="puuid【自己的数据库外键列名】" unique="true【写死】"/>  
//主1【tbl_product】:  
@OneToOne(cascade=CascadeType.ALL, mappedBy="product【对方的Java类属性名 】")  
private ProductInfoModel info;  


//从1【tbl_product_info】:  
@OneToOne  
@JoinColumn(name="puuid【自己的数据库外键列名】")  
private ProductModel product;  


标准的1:N

<!-- 1【tbl_parent】-->  
<set name="children">  
    <key column="puuid【对方的数据库外键列名】"/>  
    <one-to-many class="cn.javass.model.c.ChildModel【对方的Java类名】"/>  
</set>  

<!-- 多【tbl_child】-->  
<many-to-one name="parent" column="puuid【自己的数据库外键列名】"/>  
//1【tbl_parent】:  
@OneToMany  
@JoinColumn(name="puuid【对方的数据库外键列名】")  
private Set<ChildModel> children = new HashSet<ChildModel>();  


//多【tbl_child】:  
@ManyToOne  
@JoinColumn(name="puuid【自己的数据库外键列名】")  
private ParentModel parent;  


标准的N:N

<set name="courses" table="tbl_grade【联接表】">  
    <key column="suuid【联接表里代表自己的数据库字段名】"/>  
    <many-to-many column="cuuid【联接表里代表对方的数据库字段名】" class="cn.javass.model.e.CourseMode【对方的类名】"/>  
</set>  
@ManyToMany  
@JoinTable(  
    name="tbl_grade【联接表】",  
    joinColumns=@JoinColumn(name="suuid【联接表里代表自己的数据库字段名】"),  
    inverseJoinColumns=@JoinColumn(name="cuuid 【联接表里代表对方的数据库字段名】" )  
)  
private Set<CourseModel> courses = new HashSet<CourseModel>(); 



Tags:


Share: