经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
Spring?Data?JPA?实体类中常用注解说明
来源:jb51  时间:2021/11/22 16:44:25  对本文有异议

javax.persistence 介绍

Spring Data JPA 采用约定大于配置的思想,默认了很多东西

JPA是存储业务实体关联的实体来源,它显示定义了如何定义一个面向普通Java对象(POJO)作为实体,以及如何与管理关系实体提供一套标准

javax.persistence位于hibernate-jpa-**.jar 包里面

jpa类层次结构:

JPA类层次结构的显示单元:

单元 描述
EntityManagerFactory 一个EntityManager的工厂类,创建并管理多个EntityManager实例
EntityManager 一个接口,管理持久化操作的对象,工厂原理类似工厂的查询实例
Entity 实体是持久性对象,是存储在数据库中的记录
EntityTransaction 与EntityManager是一对一的关系,对于每一个EntityManager的操作由EntityTransaction类维护
Persistence 这个类包含静态方法来获取EntityManagerFactory实例
Query 该接口由每个JPA供应商实现,能够获得符合标准的关系对象

基本注解

@Entity

@Entity定义对象将会成为被JPA管理的实体,将映射到指定的数据库表

  1. public @interface Entity {
  2. String name() default "";
  3. }

@Table

@Table指定数据库的表名

  1. public @interface Table {
  2. // 表的名字,可选,默认实体类名为表的名称(驼峰命名规则)
  3. String name() default "";
  4. // 此表的catlog,可选
  5. String catalog() default "";
  6. // 此表所在的schema,可选
  7. String schema() default "";
  8. // 唯一性约束,只有在创建表的时候有用,默认不需要
  9. UniqueConstraint[] uniqueConstraints() default {};
  10. // 索引,只有创建表的时候有用,默认不需要
  11. Index[] indexes() default {};
  12. }

@Id

定义属性为数据库中的主键,一个实体必须有一个

@IdClass

@IdClass利用外部类的联合主键

  1. public @interface IdClass {
  2. // 联合主键的类
  3. Class value();
  4. }
  • 作为联合主键类,需要满足以下要求:
  • 必须实现Serializable
  • 必须有默认的public无参构造方法
  • 必须覆盖equals和hashCode方法(EntityManager通过find方法查找Entity时是根据equals来判断的)

用法:

(1)假设user_article表中的联合主键是 title 与create_user_id,联合主键类代码如下:

  1. @Data
  2. public class UserArticleKey implements Serializable {
  3. private String title;
  4. private Long createUserId;
  5. public UserArticleKey() {
  6. }
  7. public UserArticleKey(String title, String content, Long createUserId) {
  8. this.title = title;
  9. this.createUserId = createUserId;
  10. }
  11. }

(2)user_article表实体类如下:

  1. @Entity
  2. @IdClass(value = UserArticleKey.class)
  3. @Data
  4. public class UserArticle {
  5. private Integer id;
  6. @Id
  7. private String title;
  8. @Id
  9. private Long createUserId;
  10. }

(3) repository 类如下:

  1. public interface ArticleRepository extends JpaRepository<UserArticle, UserArticleKey> {
  2. }

(4)调用代码如下:

  1. @Test
  2. public void testUnionKey(){
  3. Optional<UserArticle> userArticle = this.articleRepository.findById(new UserArticleKey("新闻",1L));
  4. if (userArticle.isPresent()){
  5. System.out.println(userArticle.get());
  6. }
  7. }

@GenerateValue

主键生成策略

  1. public @interface GeneratedValue {
  2. // Id 的生成策略
  3. GenerationType strategy() default GenerationType.AUTO;
  4. // 通过 Sequence生成Id, 常见Oracle生成规则,需要配合@SequenceGenerator使用
  5. String generator() default "";
  6. }

GenerationType

  1. public enum GenerationType {
  2. // 通过表产生主键,框架由表模拟序列产生主键(有益于数据库移植)
  3. TABLE,
  4. // 通过序列产生主键,通过@SequenceGenerator注解指定序列名,不支持MySQL
  5. SEQUENCE,
  6. // 采用数据ID自增长,一般用于MySQL
  7. IDENTITY,
  8. // JPA自动适配的策略,默认选项
  9. AUTO;
  10. private GenerationType() {
  11. }
  12. }

@Basic

属性是到数据表的字段的映射,实体类上的字段没有注解时默认为@Basic

  1. public @interface Basic {
  2. // 可选,默认为立即加载(EAGER),LZAY延迟加载(应用在大字段上)
  3. FetchType fetch() default FetchType.EAGER;
  4. // 可选,设置这个字段是否可为null,默认 true
  5. boolean optional() default true;
  6. }

@Transient

表示该属性并非一个到数据库表的字段的映射,表示非持久化属性,与@Basic作用相反,JPA映射的时候会忽略@Transient标记的字段

@Column

定义属性对应数据库中的列名

  1. public @interface Column {
  2. // 是语句库中表的列名, 默认字段名和属性名一样, 可选
  3. String name() default "";
  4. // 是否唯一,默认 false, 可选
  5. boolean unique() default false;
  6. // 是否允许空, 默认 true, 可选
  7. boolean nullable() default true;
  8. // 执行insert操作的时候是否包含此字段,默认 true, 可选
  9. boolean insertable() default true;
  10. // 执行update操作的时候是否包含此字段,默认 true, 可选
  11. boolean updatable() default true;
  12. // 该字段在数据库中的实际类型
  13. String columnDefinition() default "";
  14. // 当映射多个表时,指在哪张表的字段,默认为主表
  15. String table() default "";
  16. // 字段长度,字段类型为VARCHAR时有效
  17. int length() default 255;
  18. // 精度,当字段类型为double时候, precision表示数值总长度
  19. int precision() default 0;
  20. // 精度, 当字段类型为double时候, scale表示小数位数
  21. int scale() default 0;
  22. }

@Temporal

用来设置Date 类型的属性映射到对应精度的字段

  • @Temporal(Temporal.DATE) 映射为日期
  • @Temporal(Temporal.TIME) 映射为日期(只有时间)
  • @Temporal(Temporal.TIMESTAMP) 映射为日期(日期+时间)

@Enumerated

映射menu枚举类型的字段

源码:

  1. public @interface Enumerated {
  2. // 映射枚举的类型
  3. EnumType value() default EnumType.ORDINAL;
  4. }
  5. public enum EnumType {
  6. // 映射枚举字段的下标
  7. ORDINAL,
  8. // 映射枚举的name
  9. STRING;
  10. }

如果使用 ORDINAL 在数据库中则会存储 0,1,2,3 这是一个索引值,这个索引值是由enum中元素的位置决定,如果enum中元素位置出现不正确的变动

很可能与数据库中的数据无法对应,建议使用 STRING

用法:

  1. @Entity(name = "t_user")
  2. @Data
  3. @Table
  4. public class SystemUser implements Serializable {
  5. @Id
  6. @GeneratedValue(strategy = GenerationType.AUTO)
  7. private Long id;
  8. @Basic
  9. private String uname;
  10. private String email;
  11. private String address;
  12. @Enumerated(EnumType.STRING)
  13. @Column(name = "user_gender")
  14. private Gender sex;
  15. public enum Gender{
  16. MALE("男性"),
  17. FEMALE("女性")
  18. ;
  19. private String value;
  20. Gender(String value) {
  21. this.value = value;
  22. }
  23. }
  24. }

@Lob

映射成数据库支持的大对象类型

  • Clob(Character Large Objects) 类型是长字符串类型java.sql.Clob、Character[]、char[]和String将被映射为Clob类型
  • Blob(Binary Large Objects) 类型是字节型,java.sql.Blob,Byte[]、byte[]和实现了Serializable接口的类型将被映射为Blob类型
  • ClobBlob占用内存空间较大,一般配合@Basic(fetch=FetchType.LAZY)将其设置为延迟加载

@NamedNativeQueries、@NamedNativeQuerie、@SqlResultSetMappings、@SqlResultSetMapping、@ColumnResult

这几个注解一般配合使用,实际情况中不会自定义这些配置

  1. @NamedNativeQueries({
  2. @NamedNativeQuery(
  3. name = "getUsers",
  4. query = "select id,title,create_user_id,create_date from user_article order by create_date desc",
  5. resultSetMapping = "userArticleMap"
  6. )
  7. })
  8. @SqlResultSetMappings({
  9. @SqlResultSetMapping(
  10. name = "userArticleMap",
  11. entities = {},
  12. columns = {
  13. @ColumnResult(name = "id"),
  14. @ColumnResult(name = "title"),
  15. @ColumnResult(name = "createUserId"),
  16. @ColumnResult(name = "createDate"),
  17. }
  18. )
  19. })
  20. @Entity
  21. @IdClass(value = UserArticleKey.class)
  22. @Data
  23. public class UserArticle {
  24. @Column
  25. private Integer id;
  26. @Id
  27. private String title;
  28. @Id
  29. private Long createUserId;
  30. private Date createDate;
  31. }

关联关系注解

@JoinColumn 定义外键关联的字段名称

主要配合 @OneToOne、@ManyToOne、@OneToMany一起使用,单独使用没有意义

@JoinColumns 定义多个字段的关联关系

  1. public @interface JoinColumn {
  2. // 目标表的字段名,必填
  3. String name() default "";
  4. // 本实体类的字段名, 非必填, 默认本表ID
  5. String referencedColumnName() default "";
  6. // 外键字段是否唯一, 可选
  7. boolean unique() default false;
  8. // 外键字段是否允许为空, 可选
  9. boolean nullable() default true;
  10. // 是否跟随一起新增, 可选
  11. boolean insertable() default true;
  12. // 是否跟随一起更新, 可选
  13. boolean updatable() default true;
  14. // 生成DDL的时候使用的SQL片段 可选
  15. String columnDefinition() default "";
  16. // 包含列的表的名称 , 可选
  17. String table() default "";
  18. // 外键约束类别, 默认为 默认约束行为, 可选
  19. ForeignKey foreignKey() default @ForeignKey(ConstraintMode.PROVIDER_DEFAULT);
  20. }

@OneToOne 一对一关联关系

  1. public @interface OneToOne {
  2. // 关系目标实体, 默认为void.class, 可选
  3. Class targetEntity() default void.class;
  4. // 级联操作策略, PERSIST(级联新增)、REMOVE(级联删除)、REFRESH(级联刷新)、MERGE(级联更新)、ALL(全选)
  5. // 默认表不会产生任何影响
  6. CascadeType[] cascade() default {};
  7. // 数据获取方式,EAGER(立即加载)、LAZY(延迟加载)
  8. FetchType fetch() default EAGER;
  9. // 是否允许空
  10. boolean optional() default true;
  11. // 关联关系被谁维护,非必填,一遍不需要特别指定
  12. // mappedBy不能与@JoinColumn或者@JoinTable同时使用
  13. // mappedBy的值是指另一方的实体里面属性的字段,而不是数据库字段,也不是实体的对象的名字,即另一方配置了@JoinColumn或者@JoinTable注解的属性的字段名称
  14. String mappedBy() default "";
  15. // 是否级联删除,和CascadeType.REMOVE 效果一样,任意配置一种即可生效
  16. boolean orphanRemoval() default false;
  17. }

用法:

  1. @OneToOne
  2. // name 为当前实体对应数据库表中的字段名
  3. // referencedColumnName 为 SystemUser 实体中@Id标记的字段对应的数据库字段名
  4. @JoinColumn(name = "create_user_id",referencedColumnName = "id")
  5. private SystemUser createUser = new SystemUser();

双向关联:

  1. @OneToOne(mappedBy = "createUser")
  2. private UserArticle article = new UserArticle();

等价于mappedBy:

  1. @OneToOne
  2. @JoinColumn(name = "user_article_id",referencedColumnName = "id")
  3. private UserArticle article = new UserArticle();

@OneToMany 与 @ManyToOne 一对多与多对一关联关系

OneToMany与ManyToOne可以相对存在,也可只存在一方

  1. public @interface OneToMany {
  2. Class targetEntity() default void.class;
  3. // 级联操作策略
  4. CascadeType[] cascade() default {};
  5. // 数据获取方式
  6. FetchType fetch() default LAZY;
  7. // 关系被谁维护,单项的
  8. String mappedBy() default "";
  9. // 是否级联删除
  10. boolean orphanRemoval() default false;
  11. }
  12. public @interface ManyToOne {
  13. Class targetEntity() default void.class;
  14. // 级联操作策略
  15. CascadeType[] cascade() default {};
  16. // 数据获取方式
  17. FetchType fetch() default LAZY;     // 关联是否可选。如果设置,若要为false,则必须始终存在非null关系。
  18. boolean optional() default true;
  19. }

@ManyToOne 映射的是一个实体对象

  1. @Entity
  2. @Data
  3. public class UserArticle {
  4. @Column
  5. private Integer id;
  6. @Id
  7. private String title;
  8. @ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
  9. @JoinColumn(name = "create_user_id",referencedColumnName = "id")
  10. private SystemUser systemUser = new SystemUser();
  11. private Date createDate;
  12. }

@OneToMany 映射的是一个是列表

  1. @Entity(name = "t_user")
  2. @Data
  3. @Table
  4. public class SystemUser implements Serializable {
  5. @Id
  6. @GeneratedValue(strategy = GenerationType.AUTO)
  7. private Long id;
  8. @Basic
  9. private String uname;
  10. private String email;
  11. private String address;
  12. @Enumerated(EnumType.STRING)
  13. private Gender sex;
  14. @OneToMany
  15. // name 当前表id
  16. // create_user_id 目标表的关联字段
  17. @JoinColumn(name = "id",referencedColumnName = "create_user_id")
  18. private List<UserArticle> articleList = new ArrayList<>();
  19. public enum Gender{
  20. MALE("男性"),
  21. FEMALE("女性")
  22. ;
  23. private String value;
  24. Gender(String value) {
  25. this.value = value;
  26. }
  27. }
  28. }

@OrderBy 排序关联查询

与@OneToMany一起使用

  1. public @interface OrderBy {
  2. /**
  3. * 要排序的字段格式如下:
  4. * orderby_list::= orderby_item [,orderby_item]*
  5. * orderby_item::= [property_or_field_name] [ASC | DESC]
  6. * 字段可以是实体属性,也可以是数据字段,默认ASC
  7. */
  8. String value() default "";
  9. }

用法:

  1. @OneToMany
  2. @JoinColumn(name = "id",referencedColumnName = "create_user_id")
  3. @OrderBy("createDate DESC") // createDate 是 UserArticle 的实体属性
  4. private List<UserArticle> articleList = new ArrayList<>();

@JoinTable 关联关系表

对象与对象之间有关联关系表的时候就用到,@JoinTable, 与@ManyToMany一起使用

  1. public @interface JoinTable {
  2. // 中间关联关系表名
  3. String name() default "";
  4. // 表的 catalog
  5. String catalog() default "";
  6. // 表的 schema
  7. String schema() default "";
  8. // 主连接表的字段
  9. JoinColumn[] joinColumns() default {};
  10. // 被连接表的字段
  11. JoinColumn[] inverseJoinColumns() default {};
  12. // 主连接外键约束类别
  13. ForeignKey foreignKey() default @ForeignKey(PROVIDER_DEFAULT);
  14. // 被连接外键约束类别
  15. ForeignKey inverseForeignKey() default @ForeignKey(PROVIDER_DEFAULT);
  16. // 唯一约束
  17. UniqueConstraint[] uniqueConstraints() default {};
  18. // 表的索引
  19. Index[] indexes() default {};
  20. }

用法:

  1. @Entity
  2. // 主连接表 blog
  3. public class Blog {
  4. @ManyToMany
  5. @JoinTable(
  6. name = "blog_tag_relation", // 关系表名称
  7. joinColumns = @JoinColumn(name = "blog_id",referencedColumnName = "id"), // 主连接表配置
  8. inverseJoinColumns = @JoinColumn(name = "tag_id",referencedColumnName = "id") // 被连接表配置
  9. )
  10. // tag 是被连接表
  11. private List<Tag> tags = new ArrayList<>();
  12. }

关于双向多对多:

双向多对多需要建立 @JoinTable的实体里, 在被连接表中的@ManyToMany中使用mappedBy="BlogTagRelation"进行配置

LeftJoin 与 Inner Join 可以提高查询效率

当使用@ManyToMany、@ManyToOne、@OneToMany、@OneToOne关联时候,SQL真正执行的时候,由一条主表查询和N条子表查询组成

会产生N+1问题

为了简单的提高查询效率,使用EntityGraph可以解决N+1条SQL的问题

@EntityGraph

@EntityGraph、@NamedEntityGraph用来提高查询效率(很好的解决了N+1条SQL的问题),两者需要配合使用,缺一不可

实体类:

  1. @NamedEntityGraph(
  2. name = "Blog.tags", attributeNodes = {
  3. @NamedAttributeNode("tags")
  4. }
  5. )
  6. @Entity
  7. // 主连接表 blog
  8. public class Blog {
  9. @ManyToMany
  10. @JoinTable(
  11. name = "blog_tag_relation", // 关系表名称
  12. joinColumns = @JoinColumn(name = "blog_id", referencedColumnName = "id"), // 主连接表配置
  13. inverseJoinColumns = @JoinColumn(name = "tag_id", referencedColumnName = "id") // 被连接表配置
  14. )
  15. // tag 是被连接表
  16. private List<Tag> tags = new ArrayList<>();
  17. }

repository:

  1. public interface BlogRepository extends JpaRepository<Blog,Long> {
  2. @Override
  3. @EntityGraph(value = "Blog.tags")
  4. List<Blog> findAll();
  5. }

关于关系查询的一些注意事项

所有注解要么全配置在字段上,要么配置在get方法上,不能混用,会无法启动

所有的关联都是支持单向关联和双向关联的,JSON序列化的时候使用双向注解会产生死循环,需要手动转化一次或使用@JsonIgnore

在所有的关联查询中,表一般是不需要简历外键索引的,@mappedBy的使用需要注意

级联删除比较危险,建议考虑清楚或完全掌握

不同的关联关系的配置,@JoinColumn里面的name,referencedColumnName代表的意思是不一样的

当配置这些关联关系的时候建议直接在表上把外键关系简历好,然后用开发工具直接生成,这样可以减少调试时间

以上为个人经验,希望能给大家一个参考,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号