Spring Data JPA的@Entity注解
基础注解
@Entity
源码
public @interface Entity {
// 可选,默认是此实体类的名字,全局唯一
String name() default "";
}
@Entity定义对象将会成为被JPA管理的实体,将映射到指定的数据库表。
@Table
用于指定数据库表的表名
源码
package javax.persistence;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
// 表的名字,可选。如果不填写,系统认为和实体的名字一样为表名
String name() default "";
// 此表的catalog,可选
String catalog() default "";
// 此表的schema,可选
String schema() default "";
// 唯一性约束,只有创建表的时候有用,默认不需要
UniqueConstraint[] uniqueConstraints() default {};
//索引,只有创建表的时候使用,默认不需要
Index[] indexes() default {};
}
@Id
@Id定义属性为数据库的主键,一个实体里面必须有一个
@GeneratedValue
源码
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface GeneratedValue {
// Id的生成策略
GenerationType strategy() default GenerationType.AUTO;
// 通过Sequence生成Id,常见的是Oracle数据库Id生成规则,需要配合@SequenceGenerator使用
String generator() default "";
}
GenerationType一共有四个值
public enum GenerationType {
// 通过表产生主键,框架由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植
TABLE,
// 通过序列产生主键,通过@SequenceGenerator注解指定序列名称,MySQL不支持这种方式
SEQUENCE,
// 采用数据库ID自增长,一般用于MYSQL数据库
IDENTITY,
// JPA自动选择合适的策略,是默认选项。
AUTO;
private GenerationType() {
}
}
@Basic
@Basic表示属性是到数据库表的字段的映射。如果实体的字段没有任何注解,默认即为@Basic
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Basic {
// 可选,EAGER为立即加载,LAZY为延迟加载
FetchType fetch() default FetchType.EAGER;
// 可选,设置这个字段是否为null,默认为true
boolean optional() default true;
}
@Transient
@Transient表示该属性并非一个到数据库表字段的映射,表示持久化属性,与@Basic作用相反。JPA映射数据库的时候就会忽略它。
@Column
源码
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
// 数据库中表的列明。可选,默认为与实体属性名保持一样,如果实体属性名中由大写,它会自动分割
String name() default "";
// 是否唯一,默认false,可选
boolean unique() default false;
// 数据字段是否允许为空,可选,默认为true
boolean nullable() default true;
// 执行insert操作的时候是否包含此字段,可选,默认为true
boolean insertable() default true;
// 执行update的时候是否包含此字段,可选,默认为true
boolean updatable() default true;
// 表示该字段在数据库中的实际类型
String columnDefinition() default "";
// 数据库字段的长度,可选,默认为255
int length() default 255;
}
@Temporal
@Temporal用来设置Date类型的属性映射到对应精准的字段。
- @Temporal(TemporalType.DATA)映射为日期 --data
- @Temporal(TemporalType.TIME)映射为日期 --time
- @Temporal(TemporalType.TIMESTAMP)映射为日期 --data time
@Enumerated
@Enumerated很好用,直接映射enum枚举类型的字段
源码
public @interface Enumerated {
// 枚举映射的类型,默认为ORDINAL
EnumType value() default EnumType.ORDINAL;
}
EnumType可选:
- ORDINAL:映射枚举的下标
- SIRING:映射枚举的name
示例:
// 定义一个枚举类
public enum Gender{
MAIL("男性"),FMAIL("女性");
private String value;
private Gender(String value){
this.valus = valus;
}
}
//实体类中的使用
@Entity
@Table(name = "tb_user")
public class User implements Serializable{
@Enumerated(EnumType.STRING)
@Column(name = "user_gender")
private Gender gender;
}
这时插入两条数据,数据库里面的值是MAIL/FMAIL,而不是男性、女性,如果是我们用@Enumerated(EnumType.ORDINAL),那么这时数据库里存储的是0,1。但是实际工作中不建议这样做,因为下标使会发生变化。
@Lob
Lob将属性映射为数据库支持的大对象类型,支持以下两种数据库类型的字段
- Clob类型是长字符串类型,java.sql.Clob、Character[]、char[]和String将被映射为Clobl类型
- Blob类型是字节类型,java.sql.Blob、Byte[]、byte[]和实现了Serializable接口的类型将被映射为Blob类型
- Clob、Blob占用内存空间较大,一般配合@Basic(fatch=FechType.LAZY)将其设置为延迟加载
关联关系注解
@JoinColumn
定义外键关联的字段名称
public @interface JoinColumn {
// 目标表的字段名,必填
String name() default "";
// 本实体的字段名,非必填,默认为本表ID
String referencedColumnName() default "";
// 外键字段是否唯一,默认为false
boolean unique() default false;
// 外键字段是否允许为空,默认为允许
boolean nullable() default true;
// 是否跟随一起新增
boolean insertable() default true;
// 是否跟随一起修改
boolean updatable() default true;
}
用法:主要配合@OneToOne、@ManyToOne、@OneToMany一起使用,但是使用没有意义。
@OneToOne
源码
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OneToOne {
// 关系目标实体,非必填,默认为该字段的类型
Class targetEntity() default void.class;
// cascade 级联操作策略
// PERSIST 级联新建
// REMOVE 级联删除
// REFRESH 级联刷新
// MERGE 级联更新
// ALL 四项权限
CascadeType[] cascade() default {};
// 数据获取方式,立即加载和延迟加载
FetchType fetch() default FetchType.EAGER;
// 是否允许为空
boolean optional() default true;
// 关联关系被谁维护,非必填,一般不需要特别指定
String mappedBy() default "";
// 是否级联删除,和CascadeType.REMOVE的效果一样,只要配置了两种的一种就会自动级联删除
boolean orphanRemoval() default false;
}
用法:@OneToOne需要配合@JoinCloumn一起使用。注意:可以双相关联,也可以只配置一方,需要视需求而定。
示例:假设一个部门只有一个员工
@OneToOne
@JoinColumn(name="employee_id",referencedColumnName = "id")
private Employee employeeAttribute = new Employee();
employee_id指的是Department里面的字段,而referencedColumnName="id"指的是Employee表里面的字段
如果需要双相关联,Employee的内容如下
@OneToOne(mappedBy = "employeeAttribute")
private Department department;
当然不使用mappedBy,和下面效果是一样的
@OneToOne
@JoinColumn(name = "id", referencedColumnName = "employee_id")
private Employee employeeAttribute = new Employee();
@OneToMany与@ManyToOne
@OneToMany与@ManyToOne可以相对存在,也可以只存在一方
@Entity
@Table(name = "user")
public class User {
@Id
private Long id;
@OneToMany(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "user"
)
private Set<Role> setRole;
}
@Entity
@Table(name = "role")
public class Role {
@Id
private Long id;
@ManyToOne(
cascade = CascadeType.ALL,
fetch = FetchType.EAGER
)
@JoinColumn(name = "user_id")
private User user;
}
@OrderBy关联查询时排序
public @interface OrderBy {
// 要排序的字段,格式如下
// orderby_list::=orderby_item[,orderby_item]*
// orderby_item::=[property_or_field_name][ASC|DESC]
String value() default "";
}
用法
@Entity
@Table(name = "user")
public class User {
@Id
private Long id;
@OneToMany(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "user"
)
@OrderBy("roleName DESC ")
private Set<Role> setRole;
}
OrderBy中的字段对应的是实体中的名称
@JoinTable
如果对象与对象之间有一个关联关联表的时候,就会用到@JoinTable,一般和@ManyToMany一起使用。
源码:
public @interface JoinTable {
// 中间关联关系表名
String name() default "";
// 表的catelog
String catalog() default "";
// 表的schema
String schema() default "";
// 主链接表的字段
JoinColumn[] joinColumns() default {};
// 被联机的表外键字段
JoinColumn[] inverseJoinColumns() default {};
}
示例:
假设Blog和Tag是多对多的关系,有一个关联关系表blog_tag_relation,表中有两个属性blog_id和tag_id,那么Blog实体里面的写法如下:
@Entity
public class Blog {
@Id
private Long id;
@ManyToMany
@JoinTable(
name = "blog_tag_relation",
joinColumns = @JoinColumn(name = "blog_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "tag_id", referencedColumnName = "id")
)
private List<Tag> tags = new ArrayList<>();
}
@ManyToMany
源码
public @interface ManyToMany {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default FetchType.LAZY;
String mappedBy() default "";
}
@ManyToMany表示多对多,和@OneToOne、@ManyToOne一样也有单向、双向之分。单向双向和注解没有关系,只看实体类之间是否相互引用。
示例:一个文章可以有多个标签,一个标签也可以在多个文章上,Blog和Tag就是多对多的关系
// 单向多对多
@Entity
public class Blog {
@Id
private Long id;
@ManyToMany
@JoinTable(
name = "blog_tag_relation",
joinColumns = @JoinColumn(name = "blog_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "tag_id", referencedColumnName = "id")
)
private List<Tag> tags = new ArrayList<>();
}
@Entity
public class BlogTagRelation {
@Column(name = "blog_id")
private Integer blogId;
@Column(name = "tag_id")
private Integer tag_id;
}
// 双向多对多
@Entity
public class Blog {
@Id
private Long id;
@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(
name = "blog_tag_relation",
joinColumns = @JoinColumn(name = "blog_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "tag_id", referencedColumnName = "id")
)
private List<Tag> tags = new ArrayList<>();
}
@Entity
public class Tag {
@Id
private String id;
@ManyToMany(mappedBy = "BlogTagRelation")
private List<Blog> blog = new ArrayList<>();
}
@EntityGraph
JPA2.1推出的@EnttiyGraph、@NamedEntityGraph用来提高查询效率,很好地解决了N+1条SQL的问题。两者配合起来使用,缺一不可。
1、现在Entity里面定义@NamedEntityGraph,其他不变,其中,@NamedAttributeNode可以有多个,也可以有一个
@NamedEntityGraph(
name = "UserInfoEntity.addressEntityList",
attributeNodes = {
@NamedAttributeNode("addressEntityList"),
@NamedAttributeNode("userBlogEntityList")
})
@Entity(name = "UserInfoEntity")
@Table(name = "user_info", schema = "test")
public class UserInfoEntity implements Serializable {
@Id
@Column(name = "id", nullable = true)
private Integer id;
@OneToOne(optional = true)
@JoinColumn(
referencedColumnName = "id",
name = "address_id",
nullable = false
)
private UserReceivingAddressEntity addressEntityList;
@OneToMany
@JoinColumn(name = "create_user_id", referencedColumnName = "id")
private List<UserBlogEntity> userBlogEntityList;
}
2、只需要在查询方法上加@EntityGraph注解即可,其中value就是@NamedEntityGraph中的name。
public interface UserRepository extends JpaRepository<User,Long> {
@Override
@EntityGraph(value = "UserInfoEntity.addressEntityList")
List<User> findAll();
}
关于关系查询的一些坑
1、所有的注解要么配置在字段上,要么配置在get方法上,不要混用
2、所有的关联都是支持单向关联和双相关联的。JSON序列化的时候双向注解会产生死循环,需要人为手动转化一次,或者使用@JsonIgnore
3、在所有的关联查询中,表一般是不需要建立外键索引
4、级联删除比较危险,建议考虑清楚,或者完全掌握
5、不同的关联关系的设置,@JoinCloum里面的name、referencedCloumName代表的意思是不一样的,很容易混淆,可以根据打印出来的sql调整
6、当配置这些关联关系的时候建议直接在表上面,把外键建好,然后通过工具生成,这样可以减少调试的时间