JPA 实体映射

来源:互联网 发布:如果黄家驹还活着知乎 编辑:程序博客网 时间:2024/06/05 06:43

1、       实体映射

@Entity

@Table(name="T_STUDENT",uniqueConstraints=@UniqueConstraint(columnNames="name"))

public class Student implements Serializable

{

   private static final long  serialVersionUID   = 1L;

   private int id;

   private Stringname;

   private Gendergender;

   private Date  birthday;

   private byte[] portrait;

  

   public Student(){}

 

   @Id

   @GeneratedValue(strategy=GenerationType.AUTO)

   public int getId()

   {

      return id;

   }

 

   public void setId(intid)

   {

      this.id =id;

   }

   

   @Column(length=20,nullable=false)

   public String getName()

   {

      return name;

   }

 

   public void setName(String name)

   {

      this.name =name;

   }

  

   @Enumerated(EnumType.STRING)

   @Column(nullable=false)

   public GendergetGender()

   {

      return gender;

   }

 

   public void setGender(Gender gender)

   {

      this.gender =gender;

   }

 

   @Temporal(TemporalType.DATE)

   public DategetBirthday()

   {

      return birthday;

   }

 

   public void setBirthday(Date birthday)

   {

      this.birthday =birthday;

   }

 

   @Lob

   @Basic(fetch=FetchType.LAZY)

   public byte[] getPortrait()

   {

      return portrait;

   }

 

   public void setPortrait(byte[]portrait)

   {

      this.portrait =portrait;

   }

 

   @Override

   public int hashCode()

   {

      final int prime = 31;

      int result = 1;

      result = prime * result + id;

      return result;

   }

 

   @Override

   public boolean equals(Object obj)

   {

      if (this ==obj)

         return true;

      if (obj ==null)

         return false;

      if (getClass() !=obj.getClass())

         return false;

      Student other = (Student)obj;

      if (id !=other.id)

         return false;

      return true;

   }

 

   @Override

   public StringtoString()

   {

      return "Student [id=" + id + ", name=" + name + ", gender=" + gender

            + ", birthday=" + birthday + "]";

   }

}

 

1.1、@Entity

标注@Entity的类,表示该类是一个可持久化的实体。该注解只有一个name属性,表示实体的名称,在执行JPQL时使用该名称;默认为实体类的非限定类名。

例如,指定实体名称为”Student”的代码为:@Entity(name=”Student”);在执行JPQL时,需要引用实体名“SELECTs FROM Student s”。

注意:

(1)由于使用类反射机制Class.newInstance方法创建实体时,会调用默认的构造方法,所以标注为@Entity的实体类要有一个无参数的构造方法;

(2)若要使实体可序列化,可以实现Serializable接口。虽然是可选的,但建议每个实体都实现Serializable接口,同时实现hashCode和equals方法;

1.2、@Table

@Table表示所映射表的属性,如下:

l  name:实体所对应的表名,默认为实体名;

l  catalog和schema:表示实体指定的目录名或数据库名,根据不同的数据库类型所有不同;

l  uniqueContrains:实体关联的唯一约束条件,一个实体可以有多个唯一约束条件;

l  UniqueConstraints:表示一个唯一性约束条件,一个唯一性约束条件可以包含多个字段,但这些字段不能同时相同。

1.3、@Id

一个实体类至少要有一个主键(PrimaryKey),能够标识为主键的属性类型有:

分类

类型

基本类型

byte、int、short、long、char

基本类型封装类

Byte、Integer、Short、Long、Character

大数值类型

BigInteger

字符串类型

String

时间日期类

java.util.Date、java.sql.Date

float和double类型及其对应的封装类不能作为主键,这是因为判断是否唯一是通过equals方法老判断的,浮点型的精度太大,不能够准确的匹配。

1.4、@GeneratedValue

@GeneratedValue:指定主键值的生成方式,需要配合@Id使用,属性如下:

l  strategy

表示主键的生成策略,有四种类型AUTO、IDENTITY、SEQUENCE、TABLE;

l  generator

为不同策略类型所对应的生成的规则名,随策略的不同而不同;

1.4.1、Auto

不同的数据库,自增主键的生成策略可能有所不同。在实体中,主键的生成策略默认为自增(Auto)。

1.4.2、Sequence

在Oracle中可以使用Sequence来生成自动主键,此时注解如下:

@Id

@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="student_seq")

@SequenceGenerator(name="student_seq",sequenceName="student_sequence",initialValue=0,allocationSize=1)

public int getId()

{

    return id;

}

@SequenceGenerator属性有:

l  name:主键生成策略名,与@GeneratedValue中的generator值一致;

l  sequenceName:数据库中创建的Sequence的名称;

l  initialValue:主键初始值,默认为0;

l  allocationSize:每次主键增加的大小,默认为50;

注意:@SequenceGenerator的作用范围为整个持久化单元(persistunit),也即可以在某一个实体中引用其他实体中声明的Sequence。

1.4.3、Identity

同Sequence一样,有些数据库支持Identity主键策略,如MicrosoftSQL Server。

1.4.4、Table

将当前主键的值单独保存到一个数据库的表中,主键的值每次都是从指定的表中查询出来获得。这种方法生成主键的策略可以适用于任何的数据库,不必担心与不同数据库不兼容造成的问题。

@Id

   @GeneratedValue(strategy=GenerationType.TABLE,generator="student_gen")

   @TableGenerator(name="student_gen",

                   table="t_generator",

                   pkColumnName="gen_name",

                   valueColumnName="gen_value",

                   pkColumnValue="STUDENT_PK",

                   allocationSize=1)

   public int getId()

   {

      return id;

}

l  name:主键生成策略名,与@GeneratedValue中的generator值一致;

l  table:数据库中主键生成策略的表名如t_generator;

l  pkColumnName:t_generator的字段名gen_name;

l  valueColumnName:t_generator字段gen_value当前所生成的值,它的值会累加;

l  pkColumnValue:gen_name字段的值;

l  initialValue:gen_value字段的初始值默认为0;

l  allocationSize:gen_value字段每次增加的值。默认为50;

t_generator表结构如下:

+-----------+--------------+------+-----+---------+-------+

| Field     | Type         | Null | Key | Default | Extra |

+-----------+--------------+------+-----+---------+-------+

| gen_name  | varchar(255) | YES  |     |NULL    |       |

| gen_value |int(11)       | YES |     | NULL    |      |

+-----------+--------------+------+-----+---------+-------+

当每次向数据库插入实体数据时,都会首先更新t_generator表的gen_value字段,而gen_name字段则用于查询或更新条件,本身的值(pkColumnValue)并不会发生改变,如下

Hibernate:

    select

        gen_value

    from

        t_generator

    where

        gen_name = 'STUDENT_PK' forupdate

           

Hibernate:

    update

        t_generator

    set

        gen_value = ?

    where

        gen_value = ?

        and gen_name = 'STUDENT_PK'

Hibernate:

    insert

    into

        t_student

        (birthday, gender, name,portrait, id)

    values

        (?, ?, ?, ?, ?)

 

1.4.5、复合主键

有时一个实体的主键可能同时为多个(复合主键),此时需要@Embeddable(嵌入式主键)来修饰主键类,如下

@Embeddable

public class StudentPK implements Serializable

{

   private static final long  serialVersionUID   = 1L;

    private Stringname;

    private Stringemail;

   

    public StudentPK()

    {

      

    }

 

   public String getName()

   {

      return name;

   }

 

   public void setName(String name)

   {

      this.name =name;

   }

 

   public StringgetEmail()

   {

      return email;

   }

 

   public void setEmail(String email)

   {

      this.email =email;

   }

 

   @Override

   public int hashCode()

   {

      final int prime = 31;

      int result = 1;

      result = prime * result + ((email ==null) ? 0 :email.hashCode());

      result = prime * result + ((name ==null) ? 0 :name.hashCode());

      return result;

   }

 

   @Override

   public boolean equals(Object obj)

   {

      if (this ==obj)

         return true;

      if (obj ==null)

         return false;

      if (getClass() !=obj.getClass())

         return false;

      StudentPK other = (StudentPK)obj;

      if (email ==null)

      {

         if (other.email !=null)

            return false;

      }

      else

         if (!email.equals(other.email))

            return false;

      if (name ==null)

      {

         if (other.name !=null)

            return false;

      }

      else

         if (!name.equals(other.name))

            return false;

      return true;

   }

   

}

主键类必须满足以下要求:

l  必须实现Serializable接口;

l  必须有默认的public无参数构造方法;

l  必须覆盖equals和hashCode方法;

l  将嵌入式主键类使用@Embeddable标注,表示这是一个嵌入式类;

在实体类中,使用@Embeddable来标注主键,如下

@Entity

@Table(name="t_student")

public class Student implements Serializable

{

   private static final long  serialVersionUID   = 1L;

   private StudentPKid;

  

   public Student(){}

 

   @EmbeddedId

   public StudentPKgetId()

   {

      return id;

   }

 

   public void setId(StudentPK id)

   {

      this.id =id;

   }

    /*

     * ....

     * */

}

 

 

1.5、@Column

@Column表示所映射表中的字段,属性如下:

l  name:数据库表字段名;

l  unique:表示该字段是否为唯一标识,默认为false。如果表中有一个字段为唯一标识,可以使用unique属性,也可以使用@Table中的@UniqueConstraint;

l  length:表示字段的长度(当字段的类型为varchar时,才有效),默认为255;

l  precision和scale:表示精度,但字段为double时,precision表示数值的总长度,scale表示小数点所占的位数;

l  insertable:表示插入数据时,是否需要插入该字段的值;在保存实体时,持久化驱动默认使用全部成员属性映射的字段来构造INSET语句,如果不想让某个字段出现在insert语句中,可以设置insertable=false;

l  updateable:表示更新数据时,是否需要更新该字段的值。insertable和updateable一般用于只读的属性;

l  columnDefinition:定义表结构时,用于创建该字段的DDL,该属性特定于具体的数据库,不建议使用。

1.6、@Enumerated

如果需要将枚举类型成员属性映射到数据库,可以使用@Enumerated。枚举类型成员属性可以被映射为字符串形式(EnumType.STRING),也可以映射为枚举值的数据序号(EnumType.ORDINAL)。

如果使用STRING保存枚举值,虽然从数据库中查询数据时非常直观,能够清楚地看出该类型代表的意义。同时也会带来其他的问题,若枚举类型的定义发生改变,此时数据中的字符串将不能转换为枚举值,建议使用ORDINAL值。

 

1.7、@Temporal

@Temporal指定java.util.Date或java.util.Calendar类型成员属性与数据库类型date、time或timestamp的哪一种映射,缺省情况下,持久化驱动假定时间类型为timestamp。

1.8、@Lob

通常,可以在数据库中保存诸如图片、长文本类型的数据,这些数据一般是保存为Blob和Clob类型。

Blob和Clob类型的数据可以通过使用@Lob来标注,其中Clob(CharacterLarge Objects)类型是长字符串类型,映射为实体中的类型可以为char[]、Character[]或String类型。Blob(BinaryLarge Objects)类型是字节类型,映射为实体中的类型可以为byte[]、Byte[]或者实现了Serializable接口的类。

由于这两种类型的数据一般占用内存空间较大,所以通常使用懒加载的方式(配合@Basic使用)。

1.9、@Transient

JPA实现将属性和对应的Getter方法认为是可持久化的。如果实体中有属性并不需要持久化,可以使用@Transient。

1.10、可持久化类型

在JPA规范中,并不是所有的类型的属性都可以持久化的,下表列举了可映射为持久化的属性类型:

分类

类型

基本类型

byte、int、short、long、boolean、char、float、double

基本类型封装类

Byte、Integer、Short、Long、Boolean、Character、Float、Double

字节和字符数组

byte[]、Byte[]、char[]、Character[]

大数值类型

BigInteger、BigDecimal

字符串类型

String

时间日期类

java.util.Date、java.util.Calendar、java.sql.Date、java.sql.Time、java.sql.Timestamp

集合类

java.util.Collection、java.util.List、java.util.Set、java.util.Map

枚举类型

 

嵌入式

 

Entuty类型

 

 

对于可持久化的java类型中,即可以映射为基本数据类型,也可以映射为基本数据类型的封装类,那么选择哪种类型比较适合呢?

若字段可能存在空值,则建议映射为基本类型的封装类,否则将产生转换银异常,如空值转换为int值。

0 0