hibernate cascade和inverse
- 格式:doc
- 大小:37.00 KB
- 文档页数:3
Hibernate项目的构建与配置1.在项目里倒入Hibernate所必须的Jar包(1)Hibernate框架可以使用在任何的Java项目里,并不一定是Web项目。
只需要在项目里倒入Hibernate所必须要使用的jar包就可以了。
(2)在Hibernate的官网下载hibernate-release-4.2.2.Final.zip解压,要使用Hibernate必须导入的jar包就在目录“hibernate-release-4.2.2.Final\lib\required”下。
倒入此路径下的所有jar包就可以了。
2.配置hibernate.cfg.xml文件(1)配置hibernate.cfg.xml文件可以参考“\project\etc”目录下的hibernate.cfg.xml文件与hibernate.properties文件。
(2)使用Hibernate连接MySQL的hibernate.cfg.xml配置文件如下:<hibernate-configuration><session-factory>(设置显示Hibernate产生的SQL语句)<property name="show_sql">true</property>(设置MySQL的SQL语法的方言)<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>(设置MySQL的驱动程序)<property name="hibernate.connection.driver_class">org.gjt.mm.mysql.Driver</property>(设置MySQL的数据库路径、用户名、密码)<property name="hibernate.connection.url">jdbc:mysql:///java</property><property name="ername">root</property><property name="hibernate.connection.password">lizhiwei</property>(设置当数据库要保存的表不存在时,就新建表)<property name="hibernate.hbm2ddl.auto">update</property>(设置对象与数据库表的关系映射文件)<mapping resource="vo/User.hbm.xml"/></session-factory></hibernate-configuration>(3)此配置文件一般放在项目的src目录下。
Inverse 和cascade的区别实际上,他们是互不相关的概念:inverse是指的关联关系的控制方向,而cascade指的是层级之间的连锁操作。
级联删除在默认情况下,当Hibernate删除一个持久化对象时,不会自动删除与他关联的其他持久化对象,如果希望Hibernate删除Customer对象时,自动删除和Customer关联的Order对象,可以把cascade的属性设置为delete。
注意:在关联的双方的哪一方设置cascade为delete呢?是不是要在双方都要设置呢?这个主要看项目本身。
如果项目是针对一对多的,应该在“one”方设置cascade为delete,不能在“many”方设置cascade为delete 。
提示:所谓删除一个持久化对象,并不是指从内存中删除这个对象,而是从数据库中删除相关的记录。
这个对象依然存在于内存中,只不过由持久化状态转变成为临时状态。
cascade属性可以有多个值,中间用逗号分开,eg:cascade="save-update,delete<set name="orders" cascade="all-delete-orphan" inverse="true"><key column="c_id"></key><one-to-many class="net.mbs.mypack.Order" /></set>Cascade属性的all-delete-orphan值当我们解除Customer和Order对象之间的关系时:customer.getOrdersa().remove(order);order.setCustomer(null);系统会执行update order set c_id=null where id=? //前提:c_id列允许为null如果希望Hibernate自动删除不再和Customer对象关联的Order对象,可以把cascade属性设置为all-delete-orphanall-delete-orphan值的总结1:当保存Customer对象时,级联保存所有关联的Order对象,相当于cascade=“save-update”2:当删除Customer对象时,级联删除所有关联的Order对象,相当于cascade=“delete”. 3;删除不再和Customer对象关联的所有Order对象当关联双方存在父子关系时,就可以把父方的cascade属性设置为“all-delete-orphan”] 所谓父子关系,是指由父方子方的持久化生命周期,子方对象必须和一个父方对象关联如果删除父方对象,应该级联删除所有的关联的子方对象。
Hibernate中的inverse在表关系映射中经常应用,inverse的值有两种,“true”和“false”。
inverse="false"是默认的值,如果设置为true 则表示对象的状态变化不会同步到数据库 ;设置成false则相反;inverse的作用:在hibernate中是通过inverse的设置来决定是有谁来维护表和表之间的关系的。
我们说inverse设立不当会导致性能低下,其实是说inverse设立不当,会产生多余重复的SQL语句甚至致使JDBC exception的throw。
这是我们在建立实体类关系时必须需要关注的地方。
一般来说,inverse=true 是推荐使用,双向关联中双方都设置 inverse=false的话,必会导致双方都重复更新同一个关系。
但是如果双方都设立inverse=true的话,双方都不维护关系的更新,这也是不行的,好在一对多中的一端:many-to-one 默认是inverse=false,避免了这种错误的产生。
但是多对多就没有这个默认设置了,所以很多人经常在多对多的两端都使用inverse=true,结果导致连接表的数据根本没有记录,就是因为他们双分都没有责任维护关系。
所以说,双向关联中最好的设置是一端为inverse=true,一端为inverse=false。
一般inverse=false会放在多的一端,那么有人提问了,many-to-many两边都是多的,inverse到底放在哪儿?其实hibernate建立多对多关系也是将他们分离成两个一对多关系,中间连接一个连接表。
所以通用存在一对多的关系,也可以这样说:一对多是多对多的基本组成部分。
cascade 有五个选项分别是:all ,delete ,none,save-update,delete-orphan ;all : 所有情况下均进行关联操作。
none:所有情况下均不进行关联操作。
在数据库里,进行增加、修改、删除记录的时候,经常会涉及到父子关系的表。
例如:有省份表和城市表,其中城市表有一个外键p rovin ce_id引用到省份表的主键。
这样,可以把省份表看成是父表,把城市表看成是子表,城市表记录的存在依赖于省份表的记录。
(文中提到的例子,所有的代码在附件里都有,所以这里的描述从简)一、在MySQL里的cas cade以下直接在M ySQL的控制台操作省份表和城市表在省份表增加一条“广东”的记录,在城市表增加一条“广州”的记录,并且把“广州”的外键引用到“广东”的主键。
“广州”的存在依赖于“广东”,如果删除省份表的“广东”,将会影响到城市表的“广州”。
根据城市表的外键约束的on delete设置,有如下三种情况:1、外键没有on delete的设置:当删除“广东”的时候,MySQL会报错,删除失败。
2、外键设置为o n delete cascad e:当删除“广东”的时候,同时把“广州”也删除。
3、外键设置为o n delete set null:当删除“广东”的时候,“广州”的外键pro vince_id会被自动设置为null,即“广州”脱离了对“广东”的依赖关系。
二、在Hiber nate里的casc ade以下用Hib ernat e来操作省份表和城市表首先,在hiber nate.cfg.xml文件配置好连接M ySQL数据库的相关属性(请在那里修改登陆数据库的密码)。
然后,为省份表和城市表添加相关的POJ O类和XM L映射文件。
用SQL语句建表(在附件的te st_ca scade.sql里),城市表有一个外键pro vince_id引用到省份表的主键,并把这个外键设置为on delete cascad e。
Hibernate注解常用的hibernate annotation标签如下:@Entity--注释声明该类为持久类。
@Table(name="promotion_info")--持久性映射的表(表名="promotion_info)。
@Column(name=”DESC”,nullable=false,length=512)--用于指定持久属性或字段的映射列。
@Id--注释可以表明哪种属性是该类中的独特标识符(即相当于数据表的主键)。
@GeneratedValue--定义自动增长的主键的生成策略。
@Transient--将忽略这些字段和属性,不用持久化到数据库。
@Temporal(TemporalType.TIMESTAMP)--声明时间格式。
@Enumerated--声明枚举@Version--声明添加对乐观锁定的支持@OneToOne--可以建立实体bean之间的一对一的关联@OneToMany--可以建立实体bean之间的一对多的关联@ManyToOne--可以建立实体bean之间的多对一的关联@ManyToMany--可以建立实体bean之间的多对多的关联@Formula--一个SQL表达式,这种属性是只读的,不在数据库生成属性(可以使用sum、average、max等)@OrderBy--Many端某个字段排序(List)下面是对以上常用Hibernate注解标签的详细介绍与举例:@Entity--注释声明该类为持久类。
将一个Javabean类声明为一个实体的数据库表映射类,最好实现序列化.此时,默认情况下,所有的类属性都为映射到数据表的持久性字段.若在类中,添加另外属性,而非映射来数据库的, 要用下面的Transient来注解.@Table(name="promotion_info")--持久性映射的表(表名="promotion_info).@T able是类一级的注解,定义在@Entity下,为实体bean映射表,目录和schema的名字,默认为实体bean的类名,不带包名.示例:@Entity@T able(name="CUST", schema="RECORDS")public class Customer { ... }@Column(name=”DESC”,nullable=false,length=512)--用于指定持久属性或字段的映射列。
今天是个五一假期,没出去玩,正好研究研究hibernate,感觉自己对关系关联这块理解的还不是很好,今天就花大工夫研究研究。
下面是关于cascade和inverse的理解。
欢迎各路高手不吝指教。
一般的我们的关系关联都是一对多(多对一),我也从这里说起,使用的两个表了就是我们的员工部门表,大家应该很熟了吧?员工表中有个部门编号去关联部门表。
我直接奔主题:我在employee和department中都没有设置级联(cascade)关系。
做个测试类:
1.public static void insert1(){
2. Session session=null;
3. try{
4. session=HibernateSessionFactory.getSession();
5. Employee e1=new Employee();
6. e1.setEmpname("guolei");
7. Employee e2=new Employee();
8. e2.setEmpname("yunhui");
9. Department d=new Department();
10. d.setDepartname("manager");
11. e1.setDepartment(d);
12. e2.setDepartment(d);
13. Set<Employee> set=new HashSet<Employee>();
14. set.add(e1);
15. set.add(e2);
16. d.setEmployees(set);
17. Transaction tran=session.beginTransaction();
18. session.save(e1);
19. session.save(e2);
20. session.save(d);//d是部门
21. mit();
22. }finally{
23. if(session!=null) session.close();
24. }
25. }
在没有设置级联关系的前提下,我首先将22行注释,测试insert1()函数,出现异常:object references an unsaved transient instance - save the transient instance before flushing。
分析一下,为什么会报这个异常了,这个异常的意思是什么?我大概翻译一下这句话,他的意思是说我们所要保存的对象和一个未被保存的瞬时对象相关联。
再看看我们得代码就不得而知了,因为我们在保存员工的同时,牵扯到了部门,当我们要保存部门的时候发现就没有这个部门,所以会报错。
好比我们去一家公司,公司把我们安排到某个部门后,我们去相应的部门去报到,却发现没有这个部门,你能不火大?还有我们发现打印了两条sql语句:Hibernate: insert into test.employee (empname, departid) values (?, ?) Hibernate: insert into test.employee (empname, departid) values (?, ?) 再分析一下这两条语句,他的意思是分别将员工姓名插入到表中,此时也有departid,但注意此时的部门id是空,他并没有对部门编号进行插入。
同时我们继续我们得猜想,他在这两条
SQL语句中没有插入部门编号,当他要插入部门编号的时候发现部门对象d还是个瞬时对象,所以就报错了。
如果我们将第11行和第12行注释,然后运行程序,程序不会再报异常,但是表中的部门id是空的。
好了,现在我们将程序中所有的注释全部去掉(11行,12行,20行),然后再运行程序,程序完美运行,看看程序打印的SQL语句:
1.Hibernate: insert into test.employee (empname, departid) values (?, ?)
2.Hibernate: insert into test.employee (empname, departid) values (?, ?)
3.Hibernate: insert into test.department (departname) values (?)
4.Hibernate: update test.employee set empname=?, departid=? where empid=?
5.Hibernate: update test.employee set empname=?, departid=? where empid=?
6.Hibernate: update test.employee set departid=? where empid=?
7.Hibernate: update test.employee set departid=? where empid=?
第一行和第二行就是插入员工姓名。
第三行是插入部门信息。
第四行和第五行是更新departid,因为在第一二行中插进去的是空的。
第六行和第七行同样是更新部门id。
为什么会更新两次部门id了?有必要么?确实没必要,如果说第4行和第5行的sql语句是因为有程序中的e1.setDepartment(d);e2.setDepartment(d);那么最后的两个sql语句了?同样我们将第16行注释,发现没有那两条语句了。
我们会不禁思考,既然第4行和第5行中已经经部门id更新,那么最后的两条语句就是很多余了,也就是说d.setEmployees(set);是多余的。
这句话半对半错,为什么了?因为确实有最后的两条update是多余的,但是我们如果在程序中不设置employees,那么当我们要在部门表中查询员工的时候employees属性就是空的,这也不符合我们得思维逻辑,这个时候我们就需要inverse了,inverse表示是否放弃维护表关系。
一般默认为false,也就是维护,我们在一对多的关系中将他设置为true就可以避免后两个语句的产生了。
<set name="employees" inverse="true">
<key>
<column name="departid"/>
</key>
<one-to-many class="domain.Employee"/>
</set>
我们在实际的程序中,也就只有在set中将他设置为true,在其他地方都不要进行设置。
接着继续讲我们的cascade,cascade的可选属性有:all(所有情况下都进行关联操作),none(默认值),save-update(在执行save,update,saveorUpdate时进行关联操作),delete(在delete时进行关联操作)。
当我们设置了cascade后,保存employee时就可以自动保存department。
具体设不设置级联操作,要依据情况而定。
在这里再谈一个小知识点,还是上面的程序我稍作修改:
1.public static void insert1(){
2. Session session=null;
3. try{
4. session=HibernateSessionFactory.getSession();
5. Employee e1=new Employee();
6. e1.setEmpname("guolei");
7. Employee e2=new Employee();
8. e2.setEmpname("yunhui");
9. Department d=new Department();
10. d.setDepartname("manager");
11. e1.setDepartment(d);
12. e2.setDepartment(d);
13. Set<Employee> set=new HashSet<Employee>();
14. set.add(e1);
15. set.add(e2);
16. d.setEmployees(set);
17. Transaction tran=session.beginTransaction();
18. session.save(d);
19. session.save(e1);
20. session.save(e2);
21. mit();
22. }finally{
23. if(session!=null) session.close();
24. }
25. }
我在程序中先保存部门,然后再保存员工,发现打印的SQL语句中,没有update了,如下:Hibernate: insert into test.department(departname) values (?) Hibernate: insert into test.employee(empname, departid) values (?, ?) Hibernate: insert into test.employee(empname, departid) values (?, ?) 为什么了?这就涉及到POJO对象的持久化状态中能监测到对象的变化了。