jpa onetoone_拥抱开源从表设计到 JPA 实现
long?may?the?sunshine.
今天的我拿起鍵盤(pán)就是猛敲代碼。
果然,十分鐘后各種 JPA 報(bào)錯(cuò)開(kāi)始了。跟新手黨一樣,看到一個(gè)錯(cuò)誤就解決一個(gè),沒(méi)有好好思考為什么會(huì)出現(xiàn)這樣的錯(cuò)誤。
于是乎,遇到一個(gè)解決一個(gè),解決一個(gè)又遇到一個(gè),經(jīng)過(guò)數(shù)十個(gè)報(bào)錯(cuò)的來(lái)回起伏。
敏銳的我發(fā)現(xiàn)苗頭有些不對(duì)。全靠腦細(xì)胞的記憶,以及開(kāi)始對(duì)第一個(gè)錯(cuò)誤的解決過(guò)程開(kāi)始模糊不清了。
最后,我采用了《數(shù)據(jù)庫(kù) ER 圖》的方式,重新開(kāi)始分析、梳理。
也就是本文的初衷。
當(dāng)我寫(xiě)到最后的時(shí)候。我的 Junit 用例全部跑通了。贊。
以下是正文,稍微有點(diǎn)。。。。。。。。。。。。。長(zhǎng)。
01 數(shù)據(jù)庫(kù)?ER 圖
ER 圖概念
實(shí)體 entity:用矩形表示,數(shù)據(jù)模型中的數(shù)據(jù)對(duì)象。
屬性 attribute:用橢圓形表示,數(shù)據(jù)對(duì)象所具有的屬性(所具有的列)。其中唯一屬性 unique attribute,用下劃線表示。
關(guān)系 relationshop:用菱形表示,數(shù)據(jù)對(duì)象與數(shù)據(jù)對(duì)象之間的聯(lián)系。
假設(shè)有兩個(gè)實(shí)體集 A、B,它們有以下三種關(guān)聯(lián)關(guān)系。
一對(duì)一 1:1
A 的每個(gè)實(shí)體至多與 B 的一個(gè)實(shí)體有關(guān)系。
B 的每個(gè)實(shí)體至多與 A 的一個(gè)實(shí)體有關(guān)系。
滿足以上兩點(diǎn),即 A 與 B 的關(guān)系是一對(duì)一。
一對(duì)多?1:N
A 的每個(gè)實(shí)體至少與 B 的 N(N>0)個(gè)實(shí)體有關(guān)系。
B 的每個(gè)實(shí)體至多與 A 的一個(gè)實(shí)體有關(guān)系。
滿足以上兩點(diǎn),即 A 與 B 的關(guān)系是一對(duì)多,B 與 A 的關(guān)系是多對(duì)一。
多對(duì)多?M:N
A 的每個(gè)實(shí)體至少與 B 的 M(M>0)個(gè)實(shí)體有關(guān)系。
B 的每個(gè)實(shí)體至少與 A 的 N(N>0)個(gè)實(shí)體有關(guān)系。
滿足以上兩點(diǎn),即 A 與 B 的關(guān)系是多對(duì)多。
02 JPA 關(guān)聯(lián)
在 JPA 中分別使用 @OneToOne、@OneToMany、@ManyToOne、@ManyToMany 注解表示一對(duì)一、一對(duì)多,多對(duì)一、多對(duì)多三種關(guān)聯(lián)關(guān)系。
OneToOne
targetEntity,作為關(guān)聯(lián)目標(biāo)的實(shí)體類(lèi)。
cascade,必須級(jí)聯(lián)到關(guān)聯(lián)目標(biāo)的操作。
ALL,級(jí)聯(lián)所有操作。
PERSIST,級(jí)聯(lián)保存操作。
MERGE,級(jí)聯(lián)修改操作。
REMOVE,級(jí)聯(lián)刪除操作。
REFRESH,級(jí)聯(lián)刷新操作。
DETACH,級(jí)聯(lián)分離操作。(2.0 版本開(kāi)始支持)
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)系中刪除的實(shí)體,以及是否將刪除操作級(jí)聯(lián)到那些實(shí)體。
OneToMany
targetEntity、cascade、fetch、mappedBy、orphanRemoval
ManyToOne
targetEntity、cascade、fetch、orphanRemoval
ManyToMany
targetEntity、cascade、fetch、mappedBy
在以上關(guān)聯(lián)注解的使用過(guò)程中,還需要?@JoinColumn 指定實(shí)體關(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ù)庫(kù)表,分別為導(dǎo)購(gòu)員、商品數(shù)據(jù)、訂單主數(shù)據(jù),以及訂單明細(xì)數(shù)據(jù)。(如上圖所示)
導(dǎo)購(gòu)員、商品數(shù)據(jù)是基礎(chǔ)數(shù)據(jù)表,即不主動(dòng)關(guān)聯(lián)其他的實(shí)體集。
商品主數(shù)據(jù),包含兩種關(guān)聯(lián)關(guān)系。
與導(dǎo)購(gòu)員之間的關(guān)系是多對(duì)一。即 @ManyToOne,注意這里只需要級(jí)聯(lián)刷新操作即可。
與訂單明細(xì)數(shù)據(jù)的關(guān)系是一對(duì)多。即@OneToMany,注意這里需要級(jí)聯(lián)保存、修改、刪除、刷新所有的操作。
商品明細(xì)數(shù)據(jù),也包含兩種關(guān)聯(lián)關(guān)系。
與商品數(shù)據(jù)之間的關(guān)系是多對(duì)一。即 @ManyToOne,注意這里只需要級(jí)聯(lián)刷新操作即可。
與訂單主數(shù)據(jù)的關(guān)系是多對(duì)一。即@ManyToOne,注意這里需要級(jí)聯(lián)保存、修改、刪除、刷新所有的操作。
04?示例代碼
導(dǎo)購(gòu)數(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 方法}訂單明細(xì)數(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 查詢一個(gè)訂單主數(shù)據(jù),JPA 會(huì)自動(dòng)將配置好的其他表的數(shù)據(jù)實(shí)體自動(dòng)查詢出來(lái)。
也就是,省略了查詢導(dǎo)購(gòu)員、訂單明細(xì)數(shù)據(jù)、商品數(shù)據(jù)三條?SQL 語(yǔ)句。
PS. 完整示例代碼,見(jiàn)?https://github.com/FoamValue/oPos.git
06?小結(jié)
今天先寫(xiě)到這里。
夜深了,讓我們下周再見(jiàn)。?
這個(gè)周末,又一次成功“強(qiáng)迫”自己學(xué)習(xí)。
感謝各位小伙伴的閱讀,這里是一個(gè)技術(shù)人的學(xué)習(xí)與分享。
總結(jié)
以上是生活随笔為你收集整理的jpa onetoone_拥抱开源从表设计到 JPA 实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 如何理解android的函数,通过And
- 下一篇: d3.js html显示图片,d3.js