long may the sunshine.
今天的我拿起键盘就是猛敲代码。
果然,十分钟后各种 JPA 报错开始了。跟新手党一样,看到一个错误就解决一个,没有好好思考为什么会出现这样的错误。
于是乎,遇到一个解决一个,解决一个又遇到一个,经过数十个报错的来回起伏。
敏锐的我发现苗头有些不对。全靠脑细胞的记忆,以及开始对第一个错误的解决过程开始模糊不清了。
最后,我采用了《数据库 ER 图》的方式,重新开始分析、梳理。
也就是本文的初衷。
当我写到最后的时候。我的 Junit 用例全部跑通了。赞。
以下是正文,稍微有点。。。。。。。。。。。。。长。
01 数据库 ER 图
ER 图概念
假设有两个实体集 A、B,它们有以下三种关联关系。
02 JPA 关联
在 JPA 中分别使用 @OneToOne、@OneToMany、@ManyToOne、@ManyToMany 注解表示一对一、一对多,多对一、多对多三种关联关系。
OneToOne
OneToMany
targetEntity、cascade、fetch、mappedBy、orphanRemoval
ManyToOne
targetEntity、cascade、fetch、orphanRemoval
ManyToMany
targetEntity、cascade、fetch、mappedBy
在以上关联注解的使用过程中,还需要 @JoinColumn 指定实体关联、元素集合的列。
例如:
@ManyToOne
@JoinColumn(name="ADDR_ID")
public Address getAddress() { return address; }
@OneToMany
@JoinColumn(name="CUST_ID")
public Set<Order> getOrders() {return orders;}
03 分析
图 A - ER 图
本案例有四张数据库表,分别为导购员、商品数据、订单主数据,以及订单明细数据。(如上图所示)
导购员、商品数据是基础数据表,即不主动关联其他的实体集。
商品主数据,包含两种关联关系。
商品明细数据,也包含两种关联关系。
04 示例代码
导购数据 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 方法
}
商品数据 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 方法
}
订单主数据 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<OscOrderItemEntity> orderItems;
@ManyToOne(targetEntity = UscGuideEntity.class, cascade = CascadeType.REFRESH)
@JoinColumn(name = "guide_no", referencedColumnName = "no", insertable = false, updatable = false)
private UscGuideEntity guideEntity;
// 省略 get/set 方法
}
订单明细数据 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 查询一个订单主数据,JPA 会自动将配置好的其他表的数据实体自动查询出来。
也就是,省略了查询导购员、订单明细数据、商品数据三条 SQL 语句。
PS. 完整示例代码,见 https://github.com/FoamValue/oPos.git
06 小结
今天先写到这里。
夜深了,让我们下周再见。?
这个周末,又一次成功“强迫”自己学习。
感谢各位小伙伴的阅读,这里是一个技术人的学习与分享。