通常在企业开发中,开发Dao层有两种做法:
(1)先建表,后再根据表来编写配置文件和实体bean。使用这种方案的开发人员受到了传统数据库建模的影响。
(2)先编写配置文件和实体bean,然后再生成表,使用这种方案的开发人员采用的是领域建模思想,这种思想相对前一种思想更加OOP。
建议使用第二种(领域建模思想),从软件开发来想,这种思想比第一种思想更加面向对象。 领域建模思想也是目前比较新的一门建模思想,第一种是传统的建模思想,已经有10来年的发展历程了,而领域建模思想是近几年才兴起的,这种思想更加的面向对象。
一、一对多双向关联与级联操作:
以订单类和订单商品类为例:
多的一方为关系维护端,关系维护端负责外键记录的更新,关系被维护端是没有权利更新外键记录。
1、订单类:
@Entity
public class Orders {
private String orderid;
private Float amount = 0f;
private Set<OrderItem> items=new HashSet<OrderItem>();
@Id @Column(length=12)
public String getOrderid() {
return orderid;
}
public void setOrderid(String orderid) {
this.orderid = orderid;
}
@Column(nullable=false)
public Float getAmount() {
return amount;
}
public void setAmount(Float amount) {
this.amount = amount;
}
//REFRESH,级联刷新(调用refresh方法才会起作用);PERSIST,级联保存(persist);
//MERGE,级联更新(merge方法);REMOVE,级联删除(remove方法);
//级联:cascade={CascadeType.ALL})如果要使用上面四项的使用,可以使用ALL来代替
//@OneToMany默认行为是延迟加载
//mappedBy:指定关系被维护端,指定OrderItem里面的order,相当于hibernate的inverse放弃维护
@OneToMany(cascade={CascadeType.REFRESH,CascadeType.PERSIST,CascadeType.MERGE,CascadeType.REMOVE}
,fetch=FetchType.LAZY,mappedBy="orders")
public Set<OrderItem> getItems() {
return items;
}
public void setItems(Set<OrderItem> items) {
this.items = items;
}
public void addOrderItem(OrderItem orderItem){
orderItem.setOrders(this);
this.items.add(orderItem);
}
}
2、订单列表类:
@Entity
public class OrderItem {
private Integer id;
private String productName;
private Float sellPrice = 0f;
private Orders orders;//对应Order类里面指定的关系被维护端
@Id @GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(length=40,nullable=false)
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
@Column(nullable=false)
public Float getSellPrice() {
return sellPrice;
}
public void setSellPrice(Float sellPrice) {
this.sellPrice = sellPrice;
}
//默认立即加载
//optional=true,选项允许为null,false时,不能为null
@ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)
@JoinColumn(name="order_id")//设置外键名称
public Orders getOrders() {
return orders;
}
public void setOrders(Orders orders) {
this.orders = orders;
}
}
3、一对多的测试类:
//JPA的Dao层
@Transactional
public class JpaDaoImpl implements JpaDao {
//事务管理
@PersistenceContext
private EntityManager em;
//JPA一对多测试类
@Override
public void jpaTest() {
Orders orders=new Orders();
orders.setAmount(34f);
orders.setOrderid("999");
OrderItem orderItem1=new OrderItem();
orderItem1.setProductName("篮球");
orderItem1.setSellPrice(150f);
OrderItem orderItem2=new OrderItem();
orderItem2.setProductName("足球");
orderItem2.setSellPrice(90f);
orders.addOrderItem(orderItem1);
orders.addOrderItem(orderItem2);
em.merge(orders);
}
}
由于配置了事务管理,这里就不需要手动开启、提交事务和关闭资源等重复的代码,直接交由事务进行管理。
具体配置步骤可以参看这篇博客:https://blog.csdn.net/a745233700/article/details/81415550
二、一对一双向关联与级联操作:
以身份证类和人为例:
1、Persion类:
@Entity
public class Person {
public Person() {
}
public Person(String name) {
this.name=name;
}
private Integer id;
private String name;
private IDcard idcard;
@Id @GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(length=10,nullable=false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//一对一配置
@OneToOne(optional=false,cascade={CascadeType.ALL})
@JoinColumn(name="idcard_id")
public IDcard getIdcard() {
return idcard;
}
public void setIdcard(IDcard idcard) {
this.idcard = idcard;
}
}
2、IDcard类:
@Entity
public class IDcard {
public IDcard() {}
public IDcard(String cardno) {
this.cardno = cardno;
}
private Integer id;
private String cardno;
private Person person;
@Id @GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(length=18,nullable=false)
public String getCardno() {
return cardno;
}
public void setCardno(String cardno) {
this.cardno = cardno;
}
//一对一配置:
@OneToOne(mappedBy="idcard",cascade={CascadeType.PERSIST,CascadeType.MERGE,CascadeType.REFRESH})
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
3、一对一测试类:
//JPA一对多测试类
@Override
public void jpaTest() {
Person person=new Person("小张");
person.setIdcard(new IDcard("448xxx1990xxxx1234"));
em.persist(person);
}
三、多对多双向关联与级联操作:
以教师类和学生类为例:
1、教师类:
//老师为关系被维护端
@Entity
public class Teacher {
public Teacher(){}
public Teacher(String name) {
this.name = name;
}
private Integer id;
private String name;
private Set<Student> students=new HashSet<Student>();
@Id @GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(length=10,nullable=false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToMany(cascade=CascadeType.REFRESH,mappedBy="teachers")
public Set<Student> getStudents() {
return students;
}
public void setStudents(Set<Student> students) {
this.students = students;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Teacher other = (Teacher) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
}
2、学生类:
//学生为关系维护端
@Entity
public class Student {
public Student(){}
public Student(String name) {
this.name = name;
}
private Integer id;
private String name;
private Set<Teacher> teachers=new HashSet<Teacher>();
@Id @GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(length=10,nullable=false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToMany(cascade=CascadeType.REFRESH)
@JoinTable(name="student_teacher",//设置第三张表的表名
inverseJoinColumns=@JoinColumn(name="teacher_id"),//设置被维护端在第三张表中的外键名称
joinColumns=@JoinColumn(name="student_id"))//设置维护端在第三张表中的外键名称
public Set<Teacher> getTeachers() {
return teachers;
}
public void setTeachers(Set<Teacher> teachers) {
this.teachers = teachers;
}
public void addTeacher(Teacher teacher){
this.teachers.add(teacher);
}
public void removeTeacher(Teacher teacher){
if(this.teachers.contains(teacher)){
this.teachers.remove(teacher);
}
}
}
3、多对多测试类:
//JPA多对多测试类:没有建立关系联系的添加
@Override
public void jpaTest() {
//没有建立关系联系的添加
em.persist(new Student("小张"));
em.persist(new Teacher("李老师"));
}
//JPA多对多测试类:建立学生跟老师的联系
@Override
public void jpaTest() {
//建立学生和老师的关系
Student student=em.find(Student.class, 15);
student.addTeacher(em.getReference(Teacher.class, 16));
}
//JPA多对多测试类:删除学生跟老师的联系
@Override
public void jpaTest() {
//删除学生跟老师的联系
Student student=em.find(Student.class, 15);
student.removeTeacher(em.getReference(Teacher.class, 16));
}
//JPA多对多测试类:删除对象:只删除教师
//直接不接触外键,直接删除老师,这种方式删除不了,被维护端没有权限删除外键,抛异常
@Override
public void jpaTest() {
em.remove(em.getReference(Teacher.class, 16));
}
//JPA多对多测试类:删除对象:只删除教师
//先解除学生与老师的关系,再删除教师对象
@Override
public void jpaTest() {
Student student=em.find(Student.class, 15);
Teacher teacher=em.getReference(Teacher.class, 16);
student.removeTeacher(teacher);
em.remove(teacher);
}
//JPA多对多测试类:删除对象:学生,并删除第三表中的记录,不删除老师
//关系维护端有权限删除外键
@Override
public void jpaTest() {
em.remove(em.getReference(Student.class, 15));
}
四、联合主键:
以飞机航线为例:两个城市决定一条航线。
1、联合主键的三个要求:
(1)必须定义无参构造函数;
(2)必须实现序列化接口Serializable;
(3)必须重写hashCode()和equals()方法。
2、AirLinkPK联合主键类:
/*联合主键的三个要求:
1.必须定义无参构造函数
2.必须实现序列化接口Serializable
3.必须重写hashCode()和equals()方法
*/
@Embeddable
public class AirLinePK implements Serializable {
public AirLinePK(){}
public AirLinePK(String startCity, String endCity) {
this.startCity = startCity;
this.endCity = endCity;
}
private String startCity;//开始城市
private String endCity;//结束城市
@Column(length=3)
public String getStartCity() {
return startCity;
}
public void setStartCity(String startCity) {
this.startCity = startCity;
}
@Column(length=3)
public String getEndCity() {
return endCity;
}
public void setEndCity(String endCity) {
this.endCity = endCity;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((endCity == null) ? 0 : endCity.hashCode());
result = prime * result + ((startCity == null) ? 0 : startCity.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AirLinePK other = (AirLinePK) obj;
if (endCity == null) {
if (other.endCity != null)
return false;
} else if (!endCity.equals(other.endCity))
return false;
if (startCity == null) {
if (other.startCity != null)
return false;
} else if (!startCity.equals(other.startCity))
return false;
return true;
}
}
3、AirLine类:
@Entity
public class AirLine {
public AirLine(){}
public AirLine(AirLinePK id) {
this.id = id;
}
public AirLine(String startCity,String endCity,String name){
this.id=new AirLinePK(startCity,endCity);
this.name=name;
}
private AirLinePK id;
private String name;
//联合主键的实体标识符
@EmbeddedId
public AirLinePK getId() {
return id;
}
public void setId(AirLinePK id) {
this.id = id;
}
@Column(length=20)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
3、联合主键测试类:
//联合主键测试类
@Override
public void jpaTest() {
em.persist(new AirLine("PEK","SHA","北京飞上海"));
}