hibernate关系梳理以及心得总结
hibernate是什么?为什么要用hibernate?或许问题不该这么问,应该如是说,如果我用hibernate将会带来什么改变,什么情况下可以用,什么情况下比较好用,什么情况下就不能用。
- 曾经有一个人说,如果你不能简单的将事物说明白,那你自己也不是真的懂。
- 在我的想法中我不敢苟同,很多事我们都尝试以简单的语言去解释,但很多事并不能如我们的愿,谁可以夸下海口说自己真的懂,那我只能说你真不懂。
- 此些hibernate文章大多都是试验得出结论,然后拿来使用,我并不求可以将hibernate说明白,只是学以致用,且主要在用。
hibernate改变了原有对数据库的操作。
- java中操作数据库,实现crud,一开始我们都是从jdbc用起。自己写sql,然后执行sql,将返回结果封装。
- hibernate封装了简单的sql语句,并将返回结果自动orm到了java对象。此时我们根据主键查询也只需要调用findOne()方法即可,添加操作也只需save()一下。
- hibernate此种做法,有利有弊,对于一般的业务,将会变得简便,但是对于复杂业务则没有纯sql灵活。
- 我自己的想法,比较常见的业务,两个表连表查询,虽然hibernate支持原生sql和自己的hql语句,但我觉得hibernate的主旨是让不懂sql的后端程序员也能处理数据,它最想要的做法不是再写join这些语句,而是直接从一个对象身上get到它的相关对象,所以就产生了简单的关系映射。
hibernate的关系映射也无外乎数据库中的那几个关系
- @OneToOne,一对一
- @OneToMany,一对多
- @ManyToOne,多对一
- @ManyToMany,多对多
- 干脆就是独立的,和谁都没有关系
关系都是双向的,现实生活中是,但是在代码里不是,你可以人为的设定从一方可以找到另一方,但是从另一方不能找到这一方。做法很简单,你可以在A类中写一个成员属性b,类型为B类,但是在B类中不写类型为A的成员属性。
从实际出发,在设计javaBean的时候,就得理清楚关系,考虑好单双向和共生性。
@OneToOne
- 一对一关系,不仅存在着双向性,还存在着共生性。但是设计时还是有套路可循的。
- @OneToOne注解的cascade属性,可以直接设置CascadeType.MERGE和CascadeType.REFRESH,级联更新和级联刷新,不会产生多余的影响。
- @OneToOne注解的fetch属性,默认值为FetchType.EAGER急加载。即如果A中由B类型的成员属性,那么在查询A的同时把对应的B也查询出来,因为是一对一,一个A只有一个B,急加载并无多大影响。
- 双向性,如果只有一方的类中有另一方类型的成员属性,则为单向。如果双方的类中互相包含对方类型的成员属性,则为双向。
- @OneToOne的mappedBy属性与@JoinColumn注解,成对出现且互斥,即mappedBy出现在A类中,@JoinColumn只能出现在B类中。
如果是双向性,但是没有使用mappedBy属性与@JoinColumn注解,则在自动生成的两个表中都包含对方的外键,且形成了循环外键。
最好的做法还是将关系设计成双向性且配合mappedBy属性与@JoinColumn注解。mappedBy的意思是在本类对应的表中我不要外键,@JoinColumn的意思是在我这个类对应的表里要有外键。mappedBy的取值为对方类中被@JoinColumn修饰的成员属性。 - 共生性,因为一对一的特殊性质,所以可以讨论共生性,共生性涉及到级联添加和级联删除。即A与B同时添加,A与B同时删除。@OneToOne注解的cascade属性中设置CascadeType.PERSIST和CascadeType.REMOVE。
public class A {
@OneToOne(cascade={CascadeType.MERGE, CascadeType.REFRESH})
@JoinColumn(name="b_id")
private B b;
}
public class B {
@OneToOne(cascade={CascadeType.MERGE, CascadeType.REFRESH}, mappedBy="b")
private A a;
}
//看情况设置CascadeType.PERSIST和CascadeType.REMOVE
@OneToMany和@ManyToOne
- 一对多和多对一关系是成对出现的,从一方出发是一对多,那么从对方出发就是多对一。但是注解可以单独使用。
- 单独使用@OneToMany和@ManyToOne时,设置@JoinColumn都会在多的那方生成外键,这点就很奇怪。
- 同上,不管是@OneToMany还是@ManyToOne,都可以直接设置CascadeType.MERGE和CascadeType.REFRESH。
- @ManyToOne是急加载,@OneToMany是懒加载。即查询一的一方不会把它所有的多的一方一起查出,因为会影响效率。而查询多的一方会把一方一起查出,因为只有一个所以不怎么影响。
- 最好的做法还是将关系设计成双向性且配合mappedBy属性与@JoinColumn注解。
- 一对多关系仍然有可能涉及共生性,即删除了一的一方之后,多的一方需不需要一起删除,创建了多的一方那一的一方需不需要一起被创建。
建议的做法是考虑删除即可。在代码中保证如果要创建多的一方那必须先存在一的一方,然后就不考虑创建的时候得共生问题。
public class A {
@OneToMany(cascade={CascadeType.MERGE, CascadeType.REFRESH}, mappedBy="a")
private List<B> b;
}
public class B {
@ManyToOne(cascade={CascadeType.MERGE, CascadeType.REFRESH})
@JoinColumn(name="a_id")
private A a;
}
//看情况在A类中设置CascadeType.REMOVE
@ManyToMany
- 多对多的关系就不用考虑双向性,因为你不双向,就等于自断一臂,只能从一方来维护关系不是很方便。
- 多对多的关系也不用考虑共生性,因为你不可能级联添加和级联删除,因为不管是哪方,都能看做是一的一方也都能看做是多的一方。如果你要级联添加,你可以把要添加的数据看做是一的一方,就没必要级联添加了。如果你要删除,那么把要删除的数据看做是多的一方,也没必要级联删除了。
- 上来直接设置CascadeType.MERGE和CascadeType.REFRESH,且只设置这两个值。
- mappedBy和@JoinColumn也不需要了,因为我们在数据库层面使用的是中间表。
- @JoinTable(name=”“, joinColumns=@JoinColumn(name=”“), inverseJoinColumns=@JoinColumn(name=”“)),该注解用于创建中间表,指定本类在该表中的外键和对方在该表中的外键。
可以在两个类中都使用这个注解,也可以在一个类中使用,另一个类中mappedBy
public class A {
@ManyToMany(cascade={CascadeType.MERGE, CascadeType.REFRESH})
@JoinTable(name="a_b", joinColumns=@JoinColumn(name="a_id"), inverseJoinColumns=@JoinColumn(name="b_id"))
private List<B> b;
}
public class B {
@ManyToOne(cascade={CascadeType.MERGE, CascadeType.REFRESH})
@JoinTable(name="a_b", joinColumns=@JoinColumn(name="b_id"), inverseJoinColumns=@JoinColumn(name="a_id"))
private List<A> a;
/*
@ManyToOne(cascade={CascadeType.MERGE, CascadeType.REFRESH}, mappedBy="b")
private List<A> a;
*/
}
感谢你们看我叨逼叨这么久,如果有纰漏请指正。