SpringDataJPA系列(6)Entiry注解使用
JPA协议规定
- 实体是直接进行数据库持久化操作的领域对象,必须通过 @Entity 注解进行标示
- 实体必须有一个 public 或者 protected 的无参数构造方法
- 实体里面必须要有一个主键,主键标示的字段可以是单个字段,也可以是复合主键字段
- 持久化映射的注解可以标示在 Entity 的字段 field 上,也可以将持久化注解运用在 Entity 里面的 get/set 方法上
//字段上
@Column(length = 20, nullable = false)
private String userName;
//get/set上
@Column(length = 20, nullable = false)
public String getUserName(){
return userName;
}
详细的协议地址:https://download.oracle.com/otn-pub/jcp/persistence-2_2-mrel-spec/JavaPersistence.pdf
Entiry注解
有哪些Entity注解,可以打开@Entity注解所在的包一窥究竟:
差不多有100多个注解…
这里只提及一些最常见的,包括 @Entity、@Table、@Access、@Id、@GeneratedValue、@Enumerated、@Basic、@Column、@Transient、@Lob、@Temporal 等。
- @Entity:定义对象将会成为被 JPA 管理的实体,必填,将字段映射到指定的数据库表中,使用起来很简单,直接用在实体类上面即可
- @Table:指定数据库的表名,表示此实体对应的数据库里面的表名,非必填,默认表名和 entity 名字一样
- @Access:指定 entity 里面的注解是写在字段上面,还是 get/set 方法上面生效,非必填。当实体里面的第一个注解出现在字段上或者 get/set 方法上面,就以第一次出现的方式为准
- @Id:定义属性为数据库的主键,一个实体里面必须有一个主键,但不一定是这个注解,可以和 @GeneratedValue 配合使用或成对出现
- @GeneratedValue:主键生成策略,共有四个值
- @Enumerated:这个注解很好用,因为它对 enum 提供了下标和 name 两种方式,用法直接映射在 enum 枚举类型的字段上
//有一个枚举类,用户的性别
public enum Gender {
MAIL("男性"), FMAIL("女性");
private String value;
private Gender(String value) {
this.value = value;
}
}
//实体类@Enumerated的写法如下
@Entity
@Table(name = "tb_user")
public class User implements Serializable {
@Enumerated(EnumType.STRING)
@Column(name = "user_gender")
private Gender gender;
.......................
}
这时候插入两条数据,数据库里面的值会变成 MAIL/FMAIL,而不是“男性” / 女性。
- @Basic:表示属性是到数据库表的字段的映射。如果实体的字段上没有任何注解,默认即为 @Basic。也就是说默认所有的字段肯定是和数据库进行映射的,并且默认为 Eager 类型
- @Transient:该属性并非一个到数据库表的字段的映射,表示非持久化属性
- @Column:定义该属性对应数据库中的列名
- @Temporal:设置 Date 类型的属性映射到对应精度的字段(日期、时间、日期时间)
注解生成技巧
生成的结果示例如下:
联合主键
@IdClass 做联合主键
可以通过 javax.persistence.EmbeddedId 和 javax.persistence.IdClass 两个注解实现联合主键的效果。
第一步:新建一个 UserInfoID 类里面是联合主键。
public class UserInfoID implements Serializable {
private String name,telephone;
}
第二步:再新建一个 UserInfo 的实体,采用 @IdClass 引用联合主键类。
@IdClass(UserInfoID.class)
public class UserInfo {
private Integer ages;
@Id
private String name;
@Id
private String telephone;
}
使用示例:
userInfoRepository.save(UserInfo.builder().ages(1).name("jack").telephone("123456789").build());
Optional<UserInfo> userInfo = userInfoRepository.findById(UserInfoID.builder().name("jack").telephone("123456789").build());
资源库仍然按照标准DQM方式进行名称查询,实际上表的主键是 primary key (name, telephone),而 Entity 里面不再是一个 @Id 字段了。
@Embeddable 和@EmbedId
第一步:在我们上面例子中的 UserInfoID 里面添加 @Embeddable 注解。
@Embeddable
public class UserInfoID implements Serializable {
private String name,telephone;
}
第二步:改一下我们刚才的 User 对象,删除 @IdClass,添加 @EmbeddedId 注解:
public class UserInfo {
private Integer ages;
@EmbeddedId
private UserInfoID userInfoID;
@Column(unique = true)
private String uniqueNumber;
}
使用情况和上面@IdClass 的类似,那么 @IdClass 和 @EmbeddedId 的区别是什么?在使用的时候,Embedded 用的是对象,而 IdClass 用的是具体的某一个字段,二者的JPQL 也会不一样。
继承关系的实现
在 Java 面向对象的语言环境中,@Entity 之间的关系多种多样,而根据 JPA 的规范,我们大致可以将其分为以下几种:
-
纯粹的继承,和表没关系,对象之间的字段共享。利用注解 @MappedSuperclass,协议规定父类不能是 @Entity
-
单表多态问题,同一张 Table,表示了不同的对象,通过一个字段来进行区分。利用
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
注解完成,只有父类有 @Table
-
多表多态,每一个子类一张表,父类的表拥有所有公用字段。通过
@Inheritance(strategy = InheritanceType.JOINED)
注解完成,父类和子类都是表,有公用的字段在父表里面
-
Object 的继承,数据库里面每一张表是分开的,相互独立不受影响。通过
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
注解完成,父类(可以是一张表,也可以不是)和子类都是表,相互之间没有关系。
@Inheritance 的这种使用方式会逐渐被淘汰,因为这样的表的设计很复杂,本应该在业务层面做的事情(多态),而在 datasoure 的表级别做了。所以在 JPA 中使用这个的时候你就会想:“这么复杂的东西,我直接用 Mybatis 算了。”其实它们是一样的,只是我们使用的思路不对。
我个人建议第一种情况,项目中会经常碰到,其它三种除非是老项目中维护需要,不建议如此使用了。