jpa onetoone_拥抱开源从表设计到 JPA 实现
long?may?the?sunshine.
今天的我拿起鍵盤就是猛敲代碼。
果然,十分鐘后各種 JPA 報錯開始了。跟新手黨一樣,看到一個錯誤就解決一個,沒有好好思考為什么會出現(xiàn)這樣的錯誤。
于是乎,遇到一個解決一個,解決一個又遇到一個,經(jīng)過數(shù)十個報錯的來回起伏。
敏銳的我發(fā)現(xiàn)苗頭有些不對。全靠腦細胞的記憶,以及開始對第一個錯誤的解決過程開始模糊不清了。
最后,我采用了《數(shù)據(jù)庫 ER 圖》的方式,重新開始分析、梳理。
也就是本文的初衷。
當我寫到最后的時候。我的 Junit 用例全部跑通了。贊。
以下是正文,稍微有點。。。。。。。。。。。。。長。
01 數(shù)據(jù)庫?ER 圖
ER 圖概念
實體 entity:用矩形表示,數(shù)據(jù)模型中的數(shù)據(jù)對象。
屬性 attribute:用橢圓形表示,數(shù)據(jù)對象所具有的屬性(所具有的列)。其中唯一屬性 unique attribute,用下劃線表示。
關(guān)系 relationshop:用菱形表示,數(shù)據(jù)對象與數(shù)據(jù)對象之間的聯(lián)系。
假設(shè)有兩個實體集 A、B,它們有以下三種關(guān)聯(lián)關(guān)系。
一對一 1:1
A 的每個實體至多與 B 的一個實體有關(guān)系。
B 的每個實體至多與 A 的一個實體有關(guān)系。
滿足以上兩點,即 A 與 B 的關(guān)系是一對一。
一對多?1:N
A 的每個實體至少與 B 的 N(N>0)個實體有關(guān)系。
B 的每個實體至多與 A 的一個實體有關(guān)系。
滿足以上兩點,即 A 與 B 的關(guān)系是一對多,B 與 A 的關(guān)系是多對一。
多對多?M:N
A 的每個實體至少與 B 的 M(M>0)個實體有關(guān)系。
B 的每個實體至少與 A 的 N(N>0)個實體有關(guān)系。
滿足以上兩點,即 A 與 B 的關(guān)系是多對多。
02 JPA 關(guān)聯(lián)
在 JPA 中分別使用 @OneToOne、@OneToMany、@ManyToOne、@ManyToMany 注解表示一對一、一對多,多對一、多對多三種關(guān)聯(lián)關(guān)系。
OneToOne
targetEntity,作為關(guān)聯(lián)目標的實體類。
cascade,必須級聯(lián)到關(guān)聯(lián)目標的操作。
ALL,級聯(lián)所有操作。
PERSIST,級聯(lián)保存操作。
MERGE,級聯(lián)修改操作。
REMOVE,級聯(lián)刪除操作。
REFRESH,級聯(lián)刷新操作。
DETACH,級聯(lián)分離操作。(2.0 版本開始支持)
fetch,關(guān)聯(lián)是延遲加載還是必須立刻獲取。
optional,關(guān)聯(lián)是否為可選。
mappedBy,擁有關(guān)系的字段。僅在關(guān)聯(lián)的反側(cè)(非所有權(quán))指定此元素。
orphanRemoval,是否將刪除操作應(yīng)用于已從關(guān)系中刪除的實體,以及是否將刪除操作級聯(lián)到那些實體。
OneToMany
targetEntity、cascade、fetch、mappedBy、orphanRemoval
ManyToOne
targetEntity、cascade、fetch、orphanRemoval
ManyToMany
targetEntity、cascade、fetch、mappedBy
在以上關(guān)聯(lián)注解的使用過程中,還需要?@JoinColumn 指定實體關(guān)聯(lián)、元素集合的列。
例如:@ManyToOne@JoinColumn(name="ADDR_ID")public Address getAddress() { return address; }@OneToMany@JoinColumn(name="CUST_ID")public?SetgetOrders()?{return?orders;}03 分析
圖 A -?ER 圖
本案例有四張數(shù)據(jù)庫表,分別為導(dǎo)購員、商品數(shù)據(jù)、訂單主數(shù)據(jù),以及訂單明細數(shù)據(jù)。(如上圖所示)
導(dǎo)購員、商品數(shù)據(jù)是基礎(chǔ)數(shù)據(jù)表,即不主動關(guān)聯(lián)其他的實體集。
商品主數(shù)據(jù),包含兩種關(guān)聯(lián)關(guān)系。
與導(dǎo)購員之間的關(guān)系是多對一。即 @ManyToOne,注意這里只需要級聯(lián)刷新操作即可。
與訂單明細數(shù)據(jù)的關(guān)系是一對多。即@OneToMany,注意這里需要級聯(lián)保存、修改、刪除、刷新所有的操作。
商品明細數(shù)據(jù),也包含兩種關(guān)聯(lián)關(guān)系。
與商品數(shù)據(jù)之間的關(guān)系是多對一。即 @ManyToOne,注意這里只需要級聯(lián)刷新操作即可。
與訂單主數(shù)據(jù)的關(guān)系是多對一。即@ManyToOne,注意這里需要級聯(lián)保存、修改、刪除、刷新所有的操作。
04?示例代碼
導(dǎo)購數(shù)據(jù)?UscGuideEntity
package cn.live.opos.center.entity;// 省略 import/** * usc_guide. * * @author chenxinjie * @date 2020-08-01 */@Entity@Table(name = "usc_guide", uniqueConstraints = { @UniqueConstraint(columnNames = "no") })public class UscGuideEntity implements Serializable { private static final long serialVersionUID = -5648617800765002770L; @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = "jpa-uuid") @GenericGenerator(name = "jpa-uuid", strategy = "org.hibernate.id.UUIDGenerator") @Column(name = "id", length = 36) private String id; @Column(name = "no", length = 20, nullable = false) private String no; @Column(name = "name", length = 40, nullable = false) private String name; @Column(name = "gender", columnDefinition = "int default 0", nullable = false) private int gender; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Temporal(TemporalType.TIMESTAMP) @Column(name = "ts", columnDefinition = "timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP() ON UPDATE CURRENT_TIMESTAMP()", nullable = false) private Date ts;??//?省略?get/set?方法}商品數(shù)據(jù)?PscSkuEntity
package cn.live.opos.center.entity;// 省略 import@Entity@Table(name = "psc_sku", uniqueConstraints = { @UniqueConstraint(columnNames = "sku") })public class PscSkuEntity implements Serializable { private static final long serialVersionUID = 8904367725209990433L; @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = "jpa-uuid") @GenericGenerator(name = "jpa-uuid", strategy = "org.hibernate.id.UUIDGenerator") @Column(name = "id", length = 36) private String id; @Column(name = "sku", length = 50, nullable = false) private String sku; @Column(name = "product_no", length = 40, nullable = false) private String productNo; @Column(name = "product_name", length = 100, nullable = false) private String productName; @Column(name = "color_no", precision = 4, scale = 0, nullable = false) private int colorNo; @Column(name = "color_name", nullable = false) private String colorName; @Column(name = "size_no", precision = 4, scale = 0, nullable = false) private int sizeNo; @Column(name = "size_name", nullable = false) private String sizeName; @Column(name = "tag_price", precision = 10, scale = 0, nullable = false) private int tagPrice; @Column(name = "retail_price", precision = 10, scale = 0, nullable = false) private int retailPrice; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Temporal(TemporalType.TIMESTAMP) @Column(name = "ts", columnDefinition = "timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP() ON UPDATE CURRENT_TIMESTAMP()", nullable = false) private Date ts; // 省略 get/set 方法}訂單主數(shù)據(jù)?OscOrderEntity
package cn.live.opos.center.entity;// 省略 import@Entity@EntityListeners(AuditingEntityListener.class)@Table(name = "osc_order", uniqueConstraints = { @UniqueConstraint(columnNames = "order_no") })public class OscOrderEntity implements Serializable { private static final long serialVersionUID = -4409502876337140593L; @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = "jpa-uuid") @GenericGenerator(name = "jpa-uuid", strategy = "org.hibernate.id.UUIDGenerator") @Column(name = "id", length = 36) private String id; @Column(name = "order_no", length = 40, nullable = false) private String orderNo; @CreatedDate @JsonFormat(pattern = "yyyy-MM-dd") @Temporal(TemporalType.DATE) @Column(name = "order_date", nullable = false) private Date orderDate; /** * 1: sell of goods. 2: return of goods. */ @Column(name = "order_type", nullable = false) private int orderType; @Column(name = "order_status", nullable = false) private int orderStatus; @Column(name = "num", precision = 5, scale = 0, nullable = false) private int num; @Column(name = "total", precision = 10, scale = 0, nullable = false) private int total; @Column(name = "guide_no", length = 20, nullable = false) private String guideNo; @LastModifiedDate @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Temporal(TemporalType.TIMESTAMP) @Column(name = "ts", columnDefinition = "timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP() ON UPDATE CURRENT_TIMESTAMP()", nullable = false) private Date ts; @OneToMany(targetEntity = OscOrderItemEntity.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinColumn(name = "order_no", referencedColumnName = "order_no", insertable = false, updatable = false) private List orderItems; @ManyToOne(targetEntity = UscGuideEntity.class, cascade = CascadeType.REFRESH) @JoinColumn(name = "guide_no", referencedColumnName = "no", insertable = false, updatable = false) private UscGuideEntity guideEntity; // 省略 get/set 方法}訂單明細數(shù)據(jù)?OscOrderItemEntity
package cn.live.opos.center.entity;// 省略 import@Entity@EntityListeners(AuditingEntityListener.class)@Table(name = "osc_order_item", uniqueConstraints = { @UniqueConstraint(columnNames = { "order_no", "sku" }) })public class OscOrderItemEntity implements Serializable { private static final long serialVersionUID = -7331381906879927968L; @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = "jpa-uuid") @GenericGenerator(name = "jpa-uuid", strategy = "org.hibernate.id.UUIDGenerator") @Column(name = "id", length = 36) private String id; @Column(name = "order_no", length = 40, nullable = false) private String orderNo; @Column(name = "sku", length = 50, nullable = false) private String sku; @Column(name = "num", precision = 5, scale = 0, nullable = false) private int num; @Column(name = "tag_price", precision = 10, scale = 0, nullable = false) private int tagPrice; @Column(name = "retail_price", precision = 10, scale = 0, nullable = false) private int retailPrice; @Column(name = "total", precision = 10, scale = 0, nullable = false) private int total; @LastModifiedDate @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Temporal(TemporalType.TIMESTAMP) @Column(name = "ts", columnDefinition = "timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP() ON UPDATE CURRENT_TIMESTAMP()", nullable = false) private Date ts; @ManyToOne(targetEntity = OscOrderEntity.class, cascade = CascadeType.ALL) @JoinColumn(name = "order_no", referencedColumnName = "order_no", insertable = false, updatable = false) private OscOrderEntity orderEntity; @ManyToOne(targetEntity = PscSkuEntity.class, cascade = CascadeType.REFRESH) @JoinColumn(name = "sku", referencedColumnName = "sku", insertable = false, updatable = false) private PscSkuEntity skuEntity;??// 省略 get/set 方法}05 效果
使用 JPA 查詢一個訂單主數(shù)據(jù),JPA 會自動將配置好的其他表的數(shù)據(jù)實體自動查詢出來。
也就是,省略了查詢導(dǎo)購員、訂單明細數(shù)據(jù)、商品數(shù)據(jù)三條?SQL 語句。
PS. 完整示例代碼,見?https://github.com/FoamValue/oPos.git
06?小結(jié)
今天先寫到這里。
夜深了,讓我們下周再見。?
這個周末,又一次成功“強迫”自己學(xué)習。
感謝各位小伙伴的閱讀,這里是一個技術(shù)人的學(xué)習與分享。
總結(jié)
以上是生活随笔為你收集整理的jpa onetoone_拥抱开源从表设计到 JPA 实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何理解android的函数,通过And
- 下一篇: d3.js html显示图片,d3.js