Hibernate在one-to-many实现父子关系的自动插入

      这是个老生长谈的问题,在学习Hibernate的时候,当时是认为永远都用不上,因为现在数据库设计并没有严格按照范式来进行设计,更多的时候采用逻辑外键关系,在代码中去控制这种业务逻辑。但是,这次在'景点+门票'的1-N关系模型时候产品设计了插入景点信息时候就录入N个门票类型信息。所以,个人认为在插入(CMS中数据的录入时候)使用起来这种级联增删改比较方便,因为插入不会带来巨大数据库压力(hibbernate的这种级联操作确实效率不高,从打印出来的HQL中可以明显看到,Hibernate对N的一端也是一条插入的)。

      注意:在数据库表设计的时候,子表中设置物理外键关联父表 不是必须的 ,而且现在的数据库设计更多的是偏重逻辑外键关系,这的确会为后期的工作带来很大的方便。

景点 和 门票类型的POJO和hbm代码

景点POJO

public class TbScenery {
	private String id;
	private String jdname;
	
	private List<TbSceneryTickets> tbSceneryTickets;
	//getter setter....
}

景点hbm

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >

<hibernate-mapping>
    <class name="TbScenery" table="TB_SCENERY">
        <id name="id" type="java.lang.String">
            <column name="ID" length="32" />
            <generator class="sequence" >  
    			<param name="sequence">SEQ_SCENERY</param>  
    		</generator>
        </id>
        <property name="jdname" type="java.lang.String">
            <column name="JDNAME" length="500" />
        </property>
	<!-- 这里需要Parent一端设置inverse="true" cascade="all"-->
	<list name="tbSceneryTickets" lazy="false" inverse="true" cascade="all">
		<key><column name="SCENERY_ID" /></key>
		<index column="ID" type="java.lang.String" />
		<one-to-many class="TbSceneryTickets" />
	</list>
    </class>
</hibernate-mapping>

门票POJO:

public class TbSceneryTickets implements java.io.Serializable {
	private String id;
	private String sceneryId;//外键
	private String typeName;
	//getter setter....
}

门票hbm

<hibernate-mapping>
	<class name="TbSceneryTickets" table="TB_SCENERY_TICKETS" lazy="false">	
        <id name="id" type="java.lang.String">
            <column name="ID" length="32" />
            <generator class="sequence" >  
    			<param name="sequence">SEQ_SCENERY_TICKET</param>  
    		</generator>
        </id>
		
		<property name="sceneryId" type="java.lang.String" column="SCENERY_ID"
			length="32" />
		<property name="typeName" type="java.lang.String" column="TYPE_NAME"
			length="100" />
	</class>
</hibernate-mapping>

测试后发现在做更新,删除操作的时候的时候没有任何问题,只是在做如下的插入操作的时候会发现插入失败(这里的方法用了事务控制):

public void test() {
	TbScenery scenery = new TbScenery();
	scenery.setJdname("东方明珠");
	
	//由于景点对象的id主键 和 门票对象的id主键都是从序列中获取到的,这里没法设置这些值。
	//也没有办法为门票对象赋景点外键sceneryId的值
	TbSceneryTickets ticket1 = new TbSceneryTickets();
	ticket1.setTypeName("成人票");
	
	TbSceneryTickets ticket2 = new TbSceneryTickets();
	ticket1.setTypeName("儿童票");
	
	List<TbSceneryTickets> tbSceneryTickets = new ArrayList<TbSceneryTickets>();
	tbSceneryTickets.add(ticket1);
	tbSceneryTickets.add(ticket2);
	scenery.setTbSceneryTickets(tbSceneryTickets);
	
	dao.saveOrUpdate(scenery);
}
 

      原因是:在新建’景点‘,并且有了n个’门票类型的时候‘,不能一次性提交这个’景点‘对象,tbSceneryTickets赋值给scenery前需要先save一下scenery,因为scenery是一个new出来的对象,不是一个已经被持久化过的对象,对它进行tbSceneryTickets赋值,Hibernate并不能够检查到它的状态。其实说得浅显些,就是scenery(景点对象)自己都没有id标识,那每个TbSceneryTickets对象(门票类型对象)怎么知道外键是什么呢?

     对于one-to-many,一般都是如下操作:

public void test() {
	TbScenery scenery = new TbScenery();
	scenery.setJdname("东方明珠");
	
	//这个步骤结束后,scenery就有自己的主键了,通过debug可以非常清楚的看到
	dao.saveOrUpdate(scenery);
	
	TbSceneryTickets ticket1 = new TbSceneryTickets();
	ticket1.setTypeName("成人票");
	//当然如果不放心,可以自己手工给ticket赋外键
	ticket1.setSceneryId(scenery.getId());
	
	TbSceneryTickets ticket2 = new TbSceneryTickets();
	ticket1.setTypeName("儿童票");
	ticket2.setSceneryId(scenery.getId())
	
	List<TbSceneryTickets> tbSceneryTickets = new ArrayList<TbSceneryTickets>();
	tbSceneryTickets.add(ticket1);
	tbSceneryTickets.add(ticket2);
	scenery.setTbSceneryTickets(tbSceneryTickets);
	
	dao.saveOrUpdate(scenery);
}

在项目中可以使用这个原理来轻松完成one-to-many的级联操作(增删改查)。

猜你喜欢

转载自kingxss.iteye.com/blog/1657486