本文可作为,北京尚学堂马士兵hibernate课程的学习笔记。
这一节,我们看看hibernate关联关系的增删改查
就关联关系而已,咱们在上一节已经提了非常多了,一对多,多对一,单向,双向...
事实上咱们能够简单的说就是A与B,有关系。
至于他们究竟是一对多,多对一,暂且不论。
咱们要讨论的是,假设我存储A,那么数据库里是否会有B;假设我删除A,那么与之相关的B是否也会删除;假设我更新了A,那么B是否会被更新;假设我查询出A,那么B是否也会被查询出来。
首先,咱们看一对多,多对一双向的样例。
还是我们上一节的样例,dream与person。
一个person有多个dream,而每个dream仅仅能属于一个person。
我们在配置xml时,让hibernate自己主动生成建表语句,而且每次都新生数据库。
<property name="hbm2ddl.auto">create</property>
实体类情况
@Entity public class Person { private int id; private String name; private Set<Dream> dreams=new HashSet<>(); @Id @GeneratedValue public int getId() { return id; } @Column(name ="myname") public String getName() { return name; } @OneToMany(mappedBy="person") public Set<Dream> getDreams() { return dreams; }//省略部分代码 } @Entity public class Dream { private int id; private String description; private Person person; @Id @GeneratedValue public int getId() { return id; } @ManyToOne @JoinColumn(name="personId") public Person getPerson() { return person; } //省略部分代码 }
OK,两个类的关系已经非常清楚了,双向多对一/一对多。
假设我们的測试代码例如以下:
//代码片1
public static void main(String[] args) { Person p=new Person(); p.setName("dlf"); Dream d=new Dream(); d.setDescription("marry glt"); d.setPerson(p); SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); session.save(p); session.save(d); session.getTransaction().commit(); }
上面的代码是没有问题的,并且即使先save dream也OK。
那么如今问题来了
d.setPerson(p);
这句代码,已经说明了dream里有一个引用指向了person。
那么我能够在代码层次不save person么?
即仅仅有一个
session.save(d);
答案是否定的,报以下的错误。
object references an unsaved transient instance - save the transient instance before flushing: com.bjsxt.hibernate.Person
由于在数据库保存dream的时候,person还没有保存,所以出错了。
上面的样例告诉我们,默认情况下:hibernate不会替我们存储"一"的那一方。
当我写到这块的时候,我忽然脑子一抽,又写下了以下的測试代码:
//代码片2 public static void main(String[] args) { Dream d=new Dream(); d.setDescription("marry glt"); Person p=new Person(); p.setName("dlf"); p.getDreams().add(d); SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); session.save(p); session.getTransaction().commit(); }
假设,我以"一"的那一方为主,上面的代码会是个什么情况?
答案是,不报错,代码能正常运行,可是数据库里仅仅有p的信息而没有d的信息。
上面的代码确实有点奇葩了,在上一节的时候,我们就已经达成了共识,在处理关联关系时,数据库层面总是在多的那一方维护关系。
所以上面的代码,并不能保存dream。
上面也说了,是在默认情况下,那特殊情况呢?
我们看api文档
上面是manytoone这个元标签能够带的属性,cascade翻译成中文就是级联。他是caschdetype型的
而Caschdetype是enum型的。能够取六个值
我们最经经常使用的就是,all,persist与remove。
all的意思是,对这个对象的增删改四中操作都会影响到与之关联的那个对象。
(关于查询的级联,由还有一个属性管理)
persist的意思是,对这个对象的添加(save操作)会连带把与之关联的那个对象也sava了。
remove的意思就是,假设删除这个对象也会删除与之关联的那个对象。
OK看以下的修改
@ManyToOne(cascade={CascadeType.ALL}) @JoinColumn(name="personId") public Person getPerson() { return person; }
此时,我们以下的代码,就能正常执行了,并且数据库里,两个对象都存储了,且外键关联也是OK的。
//代码片3 Person p=new Person(); p.setName("dlf"); Dream d=new Dream(); d.setDescription("marry glt"); d.setPerson(p); session.save(d);
写到这,我的脑子又抽了,假设我给onetomany的那一方加上cascade会怎样?
@OneToMany(mappedBy="person",cascade={CascadeType.ALL}) public Set<Dream> getDreams() { return dreams; } 測试代码: Dream d=new Dream(); d.setDescription("marry glt"); Person p=new Person(); p.setName("dlf"); p.getDreams().add(d); session.save(p);
cascade确实实用,此时数据库里已经有了dream,可是dream的外键是null。
为什么?由于dream里并没有关联person。
事实上,cascade这个东西也不是绝对的,假设关联关系比較麻烦,我们发现数据库里总是少存了一个对象,那就依照代码1的办法,直接存两次对象不就OK了。
谈点规律:
1 我们发现假设存储对象的时候,我们从多的一方操作是比較简单的。所以以后,尽量在多的一方操作。
2 为了符合逻辑,同一时候也不出错,一点两个对象之间的关系是双向的,那么在代码层次,就把两个的引用都设好,这样就不会出问题了。
3 之前说的,假设是双向的,设定mappedby。