Spring?Data?JPA?實體類中常用注解說明
javax.persistence 介紹
Spring Data JPA 采用約定大于配置的思想,默認(rèn)了很多東西
JPA是存儲業(yè)務(wù)實體關(guān)聯(lián)的實體來源,它顯示定義了如何定義一個面向普通Java對象(POJO)作為實體,以及如何與管理關(guān)系實體提供一套標(biāo)準(zhǔn)
javax.persistence位于hibernate-jpa-**.jar 包里面
jpa類層次結(jié)構(gòu):

JPA類層次結(jié)構(gòu)的顯示單元:
| 單元 | 描述 |
| EntityManagerFactory | 一個EntityManager的工廠類,創(chuàng)建并管理多個EntityManager實例 |
| EntityManager | 一個接口,管理持久化操作的對象,工廠原理類似工廠的查詢實例 |
| Entity | 實體是持久性對象,是存儲在數(shù)據(jù)庫中的記錄 |
| EntityTransaction | 與EntityManager是一對一的關(guān)系,對于每一個EntityManager的操作由EntityTransaction類維護 |
| Persistence | 這個類包含靜態(tài)方法來獲取EntityManagerFactory實例 |
| Query | 該接口由每個JPA供應(yīng)商實現(xiàn),能夠獲得符合標(biāo)準(zhǔn)的關(guān)系對象 |
基本注解
@Entity
@Entity定義對象將會成為被JPA管理的實體,將映射到指定的數(shù)據(jù)庫表
public @interface Entity {
String name() default "";
}
@Table
@Table指定數(shù)據(jù)庫的表名
public @interface Table {
// 表的名字,可選,默認(rèn)實體類名為表的名稱(駝峰命名規(guī)則)
String name() default "";
// 此表的catlog,可選
String catalog() default "";
// 此表所在的schema,可選
String schema() default "";
// 唯一性約束,只有在創(chuàng)建表的時候有用,默認(rèn)不需要
UniqueConstraint[] uniqueConstraints() default {};
// 索引,只有創(chuàng)建表的時候有用,默認(rèn)不需要
Index[] indexes() default {};
}
@Id
定義屬性為數(shù)據(jù)庫中的主鍵,一個實體必須有一個
@IdClass
@IdClass利用外部類的聯(lián)合主鍵
public @interface IdClass {
// 聯(lián)合主鍵的類
Class value();
}
- 作為聯(lián)合主鍵類,需要滿足以下要求:
- 必須實現(xiàn)Serializable
- 必須有默認(rèn)的public無參構(gòu)造方法
- 必須覆蓋equals和hashCode方法(EntityManager通過find方法查找Entity時是根據(jù)equals來判斷的)
用法:
(1)假設(shè)user_article表中的聯(lián)合主鍵是 title 與create_user_id,聯(lián)合主鍵類代碼如下:
@Data
public class UserArticleKey implements Serializable {
private String title;
private Long createUserId;
public UserArticleKey() {
}
public UserArticleKey(String title, String content, Long createUserId) {
this.title = title;
this.createUserId = createUserId;
}
}
(2)user_article表實體類如下:
@Entity
@IdClass(value = UserArticleKey.class)
@Data
public class UserArticle {
private Integer id;
@Id
private String title;
@Id
private Long createUserId;
}
(3) repository 類如下:
public interface ArticleRepository extends JpaRepository<UserArticle, UserArticleKey> {
}
(4)調(diào)用代碼如下:
@Test
public void testUnionKey(){
Optional<UserArticle> userArticle = this.articleRepository.findById(new UserArticleKey("新聞",1L));
if (userArticle.isPresent()){
System.out.println(userArticle.get());
}
}
@GenerateValue
主鍵生成策略
public @interface GeneratedValue {
// Id 的生成策略
GenerationType strategy() default GenerationType.AUTO;
// 通過 Sequence生成Id, 常見Oracle生成規(guī)則,需要配合@SequenceGenerator使用
String generator() default "";
}
GenerationType
public enum GenerationType {
// 通過表產(chǎn)生主鍵,框架由表模擬序列產(chǎn)生主鍵(有益于數(shù)據(jù)庫移植)
TABLE,
// 通過序列產(chǎn)生主鍵,通過@SequenceGenerator注解指定序列名,不支持MySQL
SEQUENCE,
// 采用數(shù)據(jù)ID自增長,一般用于MySQL
IDENTITY,
// JPA自動適配的策略,默認(rèn)選項
AUTO;
private GenerationType() {
}
}
@Basic
屬性是到數(shù)據(jù)表的字段的映射,實體類上的字段沒有注解時默認(rèn)為@Basic
public @interface Basic {
// 可選,默認(rèn)為立即加載(EAGER),LZAY延遲加載(應(yīng)用在大字段上)
FetchType fetch() default FetchType.EAGER;
// 可選,設(shè)置這個字段是否可為null,默認(rèn) true
boolean optional() default true;
}
@Transient
表示該屬性并非一個到數(shù)據(jù)庫表的字段的映射,表示非持久化屬性,與@Basic作用相反,JPA映射的時候會忽略@Transient標(biāo)記的字段
@Column
定義屬性對應(yīng)數(shù)據(jù)庫中的列名
public @interface Column {
// 是語句庫中表的列名, 默認(rèn)字段名和屬性名一樣, 可選
String name() default "";
// 是否唯一,默認(rèn) false, 可選
boolean unique() default false;
// 是否允許空, 默認(rèn) true, 可選
boolean nullable() default true;
// 執(zhí)行insert操作的時候是否包含此字段,默認(rèn) true, 可選
boolean insertable() default true;
// 執(zhí)行update操作的時候是否包含此字段,默認(rèn) true, 可選
boolean updatable() default true;
// 該字段在數(shù)據(jù)庫中的實際類型
String columnDefinition() default "";
// 當(dāng)映射多個表時,指在哪張表的字段,默認(rèn)為主表
String table() default "";
// 字段長度,字段類型為VARCHAR時有效
int length() default 255;
// 精度,當(dāng)字段類型為double時候, precision表示數(shù)值總長度
int precision() default 0;
// 精度, 當(dāng)字段類型為double時候, scale表示小數(shù)位數(shù)
int scale() default 0;
}
@Temporal
用來設(shè)置Date 類型的屬性映射到對應(yīng)精度的字段
@Temporal(Temporal.DATE)映射為日期@Temporal(Temporal.TIME)映射為日期(只有時間)@Temporal(Temporal.TIMESTAMP)映射為日期(日期+時間)
@Enumerated
映射menu枚舉類型的字段
源碼:
public @interface Enumerated {
// 映射枚舉的類型
EnumType value() default EnumType.ORDINAL;
}
public enum EnumType {
// 映射枚舉字段的下標(biāo)
ORDINAL,
// 映射枚舉的name
STRING;
}
如果使用 ORDINAL 在數(shù)據(jù)庫中則會存儲 0,1,2,3 這是一個索引值,這個索引值是由enum中元素的位置決定,如果enum中元素位置出現(xiàn)不正確的變動
很可能與數(shù)據(jù)庫中的數(shù)據(jù)無法對應(yīng),建議使用 STRING
用法:
@Entity(name = "t_user")
@Data
@Table
public class SystemUser implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Basic
private String uname;
private String email;
private String address;
@Enumerated(EnumType.STRING)
@Column(name = "user_gender")
private Gender sex;
public enum Gender{
MALE("男性"),
FEMALE("女性")
;
private String value;
Gender(String value) {
this.value = value;
}
}
}
@Lob
映射成數(shù)據(jù)庫支持的大對象類型
Clob(Character Large Objects)類型是長字符串類型java.sql.Clob、Character[]、char[]和String將被映射為Clob類型Blob(Binary Large Objects)類型是字節(jié)型,java.sql.Blob,Byte[]、byte[]和實現(xiàn)了Serializable接口的類型將被映射為Blob類型Clob、Blob占用內(nèi)存空間較大,一般配合@Basic(fetch=FetchType.LAZY)將其設(shè)置為延遲加載
@NamedNativeQueries、@NamedNativeQuerie、@SqlResultSetMappings、@SqlResultSetMapping、@ColumnResult
這幾個注解一般配合使用,實際情況中不會自定義這些配置
@NamedNativeQueries({
@NamedNativeQuery(
name = "getUsers",
query = "select id,title,create_user_id,create_date from user_article order by create_date desc",
resultSetMapping = "userArticleMap"
)
})
@SqlResultSetMappings({
@SqlResultSetMapping(
name = "userArticleMap",
entities = {},
columns = {
@ColumnResult(name = "id"),
@ColumnResult(name = "title"),
@ColumnResult(name = "createUserId"),
@ColumnResult(name = "createDate"),
}
)
})
@Entity
@IdClass(value = UserArticleKey.class)
@Data
public class UserArticle {
@Column
private Integer id;
@Id
private String title;
@Id
private Long createUserId;
private Date createDate;
}
關(guān)聯(lián)關(guān)系注解
@JoinColumn 定義外鍵關(guān)聯(lián)的字段名稱
主要配合 @OneToOne、@ManyToOne、@OneToMany一起使用,單獨使用沒有意義
@JoinColumns 定義多個字段的關(guān)聯(lián)關(guān)系
public @interface JoinColumn {
// 目標(biāo)表的字段名,必填
String name() default "";
// 本實體類的字段名, 非必填, 默認(rèn)本表ID
String referencedColumnName() default "";
// 外鍵字段是否唯一, 可選
boolean unique() default false;
// 外鍵字段是否允許為空, 可選
boolean nullable() default true;
// 是否跟隨一起新增, 可選
boolean insertable() default true;
// 是否跟隨一起更新, 可選
boolean updatable() default true;
// 生成DDL的時候使用的SQL片段 可選
String columnDefinition() default "";
// 包含列的表的名稱 , 可選
String table() default "";
// 外鍵約束類別, 默認(rèn)為 默認(rèn)約束行為, 可選
ForeignKey foreignKey() default @ForeignKey(ConstraintMode.PROVIDER_DEFAULT);
}
@OneToOne 一對一關(guān)聯(lián)關(guān)系
public @interface OneToOne {
// 關(guān)系目標(biāo)實體, 默認(rèn)為void.class, 可選
Class targetEntity() default void.class;
// 級聯(lián)操作策略, PERSIST(級聯(lián)新增)、REMOVE(級聯(lián)刪除)、REFRESH(級聯(lián)刷新)、MERGE(級聯(lián)更新)、ALL(全選)
// 默認(rèn)表不會產(chǎn)生任何影響
CascadeType[] cascade() default {};
// 數(shù)據(jù)獲取方式,EAGER(立即加載)、LAZY(延遲加載)
FetchType fetch() default EAGER;
// 是否允許空
boolean optional() default true;
// 關(guān)聯(lián)關(guān)系被誰維護,非必填,一遍不需要特別指定
// mappedBy不能與@JoinColumn或者@JoinTable同時使用
// mappedBy的值是指另一方的實體里面屬性的字段,而不是數(shù)據(jù)庫字段,也不是實體的對象的名字,即另一方配置了@JoinColumn或者@JoinTable注解的屬性的字段名稱
String mappedBy() default "";
// 是否級聯(lián)刪除,和CascadeType.REMOVE 效果一樣,任意配置一種即可生效
boolean orphanRemoval() default false;
}
用法:
@OneToOne // name 為當(dāng)前實體對應(yīng)數(shù)據(jù)庫表中的字段名 // referencedColumnName 為 SystemUser 實體中@Id標(biāo)記的字段對應(yīng)的數(shù)據(jù)庫字段名 @JoinColumn(name = "create_user_id",referencedColumnName = "id") private SystemUser createUser = new SystemUser();
雙向關(guān)聯(lián):
@OneToOne(mappedBy = "createUser") private UserArticle article = new UserArticle();
等價于mappedBy:
@OneToOne @JoinColumn(name = "user_article_id",referencedColumnName = "id") private UserArticle article = new UserArticle();
@OneToMany 與 @ManyToOne 一對多與多對一關(guān)聯(lián)關(guān)系
OneToMany與ManyToOne可以相對存在,也可只存在一方
public @interface OneToMany {
Class targetEntity() default void.class;
// 級聯(lián)操作策略
CascadeType[] cascade() default {};
// 數(shù)據(jù)獲取方式
FetchType fetch() default LAZY;
// 關(guān)系被誰維護,單項的
String mappedBy() default "";
// 是否級聯(lián)刪除
boolean orphanRemoval() default false;
}
public @interface ManyToOne {
Class targetEntity() default void.class;
// 級聯(lián)操作策略
CascadeType[] cascade() default {};
// 數(shù)據(jù)獲取方式
FetchType fetch() default LAZY; // 關(guān)聯(lián)是否可選。如果設(shè)置,若要為false,則必須始終存在非null關(guān)系。
boolean optional() default true;
}
@ManyToOne 映射的是一個實體對象
@Entity
@Data
public class UserArticle {
@Column
private Integer id;
@Id
private String title;
@ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@JoinColumn(name = "create_user_id",referencedColumnName = "id")
private SystemUser systemUser = new SystemUser();
private Date createDate;
}
@OneToMany 映射的是一個是列表
@Entity(name = "t_user")
@Data
@Table
public class SystemUser implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Basic
private String uname;
private String email;
private String address;
@Enumerated(EnumType.STRING)
private Gender sex;
@OneToMany
// name 當(dāng)前表id
// create_user_id 目標(biāo)表的關(guān)聯(lián)字段
@JoinColumn(name = "id",referencedColumnName = "create_user_id")
private List<UserArticle> articleList = new ArrayList<>();
public enum Gender{
MALE("男性"),
FEMALE("女性")
;
private String value;
Gender(String value) {
this.value = value;
}
}
}
@OrderBy 排序關(guān)聯(lián)查詢
與@OneToMany一起使用
public @interface OrderBy {
/**
* 要排序的字段格式如下:
* orderby_list::= orderby_item [,orderby_item]*
* orderby_item::= [property_or_field_name] [ASC | DESC]
* 字段可以是實體屬性,也可以是數(shù)據(jù)字段,默認(rèn)ASC
*/
String value() default "";
}
用法:
@OneToMany
@JoinColumn(name = "id",referencedColumnName = "create_user_id")
@OrderBy("createDate DESC") // createDate 是 UserArticle 的實體屬性
private List<UserArticle> articleList = new ArrayList<>();
@JoinTable 關(guān)聯(lián)關(guān)系表
對象與對象之間有關(guān)聯(lián)關(guān)系表的時候就用到,@JoinTable, 與@ManyToMany一起使用
public @interface JoinTable {
// 中間關(guān)聯(lián)關(guān)系表名
String name() default "";
// 表的 catalog
String catalog() default "";
// 表的 schema
String schema() default "";
// 主連接表的字段
JoinColumn[] joinColumns() default {};
// 被連接表的字段
JoinColumn[] inverseJoinColumns() default {};
// 主連接外鍵約束類別
ForeignKey foreignKey() default @ForeignKey(PROVIDER_DEFAULT);
// 被連接外鍵約束類別
ForeignKey inverseForeignKey() default @ForeignKey(PROVIDER_DEFAULT);
// 唯一約束
UniqueConstraint[] uniqueConstraints() default {};
// 表的索引
Index[] indexes() default {};
}
用法:
@Entity
// 主連接表 blog
public class Blog {
@ManyToMany
@JoinTable(
name = "blog_tag_relation", // 關(guān)系表名稱
joinColumns = @JoinColumn(name = "blog_id",referencedColumnName = "id"), // 主連接表配置
inverseJoinColumns = @JoinColumn(name = "tag_id",referencedColumnName = "id") // 被連接表配置
)
// tag 是被連接表
private List<Tag> tags = new ArrayList<>();
}
關(guān)于雙向多對多:
雙向多對多需要建立 @JoinTable的實體里, 在被連接表中的@ManyToMany中使用mappedBy="BlogTagRelation"進行配置
LeftJoin 與 Inner Join 可以提高查詢效率
當(dāng)使用@ManyToMany、@ManyToOne、@OneToMany、@OneToOne關(guān)聯(lián)時候,SQL真正執(zhí)行的時候,由一條主表查詢和N條子表查詢組成
會產(chǎn)生N+1問題
為了簡單的提高查詢效率,使用EntityGraph可以解決N+1條SQL的問題
@EntityGraph
@EntityGraph、@NamedEntityGraph用來提高查詢效率(很好的解決了N+1條SQL的問題),兩者需要配合使用,缺一不可
實體類:
@NamedEntityGraph(
name = "Blog.tags", attributeNodes = {
@NamedAttributeNode("tags")
}
)
@Entity
// 主連接表 blog
public class Blog {
@ManyToMany
@JoinTable(
name = "blog_tag_relation", // 關(guān)系表名稱
joinColumns = @JoinColumn(name = "blog_id", referencedColumnName = "id"), // 主連接表配置
inverseJoinColumns = @JoinColumn(name = "tag_id", referencedColumnName = "id") // 被連接表配置
)
// tag 是被連接表
private List<Tag> tags = new ArrayList<>();
}
repository:
public interface BlogRepository extends JpaRepository<Blog,Long> {
@Override
@EntityGraph(value = "Blog.tags")
List<Blog> findAll();
}
關(guān)于關(guān)系查詢的一些注意事項
所有注解要么全配置在字段上,要么配置在get方法上,不能混用,會無法啟動
所有的關(guān)聯(lián)都是支持單向關(guān)聯(lián)和雙向關(guān)聯(lián)的,JSON序列化的時候使用雙向注解會產(chǎn)生死循環(huán),需要手動轉(zhuǎn)化一次或使用@JsonIgnore
在所有的關(guān)聯(lián)查詢中,表一般是不需要簡歷外鍵索引的,@mappedBy的使用需要注意
級聯(lián)刪除比較危險,建議考慮清楚或完全掌握
不同的關(guān)聯(lián)關(guān)系的配置,@JoinColumn里面的name,referencedColumnName代表的意思是不一樣的
當(dāng)配置這些關(guān)聯(lián)關(guān)系的時候建議直接在表上把外鍵關(guān)系簡歷好,然后用開發(fā)工具直接生成,這樣可以減少調(diào)試時間
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
spring-mybatis與原生mybatis使用對比分析
這篇文章主要介紹了spring-mybatis與原生mybatis使用對比分析,需要的朋友可以參考下2017-11-11
Spring中的@Qualifier注解和@Resource注解區(qū)別解析
這篇文章主要介紹了Spring中的@Qualifier注解和@Resource注解區(qū)別解析,@Qualifier注解的用處是當(dāng)一個接口有多個實現(xiàn)的時候,為了指名具體調(diào)用哪個類的實現(xiàn),@Resource注解可以通過 byName命名和byType類型的方式注入,需要的朋友可以參考下2023-11-11
springboot動態(tài)調(diào)整日志級別的操作大全
這篇文章主要介紹了springboot動態(tài)調(diào)整日志級別的方法,本文通過實例圖文相結(jié)合給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-10-10
一文詳解如何在SpringMVC的視圖中渲染模型數(shù)據(jù)
SpringMVC是一個基于Spring框架的Web框架,它提供了一種方便的方式來處理 HTTP 請求和響應(yīng),在SpringMVC中,視圖是用來渲染模型數(shù)據(jù)的組件,它們負(fù)責(zé)將模型數(shù)據(jù)轉(zhuǎn)換為HTML、JSON、XML等格式的響應(yīng),在本文中,我們將討論如何在SpringMVC中的視圖中渲染模型數(shù)據(jù)2023-07-07

