深入解析Java的Hibernate框架中的一对一关联映射
作为一个orm框架,hibernate肯定也需要满足我们实现表与表之间进行关联的需要。hibernate在关联方法的实现很简单。下面我们先来看看一对一的做法:
不多说了,我们直接上代码:
两个实体类,tuser和tpassport:
public class tuser implements serializable{ private static final long serialversionuid = 1l; private int id; private int age; private string name; private tpassport passport; //省略get/set方法 } public class tpassport implements serializable{ private static final long serialversionuid = 1l; private int id; private string serial; private int expiry; private tuser user; //省略get/set方法 }
下面我们看一下映射文件有什么不同:
<hibernate-mapping package="org.hibernate.tutorial.domain4"> <class name="tuser" table="user4"> <id name="id" column="id"> <generator class="native" /> </id> <property name="name" type="java.lang.string" column="name"/> <property name="age" type="java.lang.integer" column="age"/> <one-to-one name="passport" class="tpassport" cascade="all" outer-join="true" /> </class> </hibernate-mapping>
这里我们看到有一个新标签,one-to-one,它表明当前类与所对应的类是一对一的关系,cascade是级联关系,all表明无论什么情况都进行级联,即当对tuser类进行操作时,tpassport也会进行相应的操作,outer-join是指是否使用outer join语句。
我们再看另外一个tpassport的映射文件:
<hibernate-mapping package="org.hibernate.tutorial.domain4"> <class name="tpassport" table="passport4"> <id name="id" column="id"> <generator class="foreign" > <param name="property">user</param> </generator> </id> <property name="serial" type="java.lang.string" column="serial"/> <property name="expiry" type="java.lang.integer" column="expiry"/> <one-to-one name="user" class="tuser" constrained="true" /> </class> </hibernate-mapping>
这里我们重点看到generator的class值,它为foreign表明参照外键,而参照哪一个是由param来进行指定,这里表明参照user类的id。而one-to-one标签中多了一个constrained属性,它是告诉hibernate当前类存在外键约束,即当前类的id根据tuser的id进行生成。
下面我们直接上测试类,这次测试类没有用junit而是直接main方法来了:
public static void main(string[] args) { configuration cfg = new configuration().configure(); sessionfactory sessionfactory = cfg.buildsessionfactory(); session session = sessionfactory.opensession(); session.begintransaction(); tuser user = new tuser(); user.setage(20); user.setname("shuntest"); tpassport passport = new tpassport(); passport.setexpiry(20); passport.setserial("123123123"); passport.setuser(user); user.setpassport(passport); session.save(user); session.gettransaction().commit(); }
代码很简单,就不说了。我们主要看这里:
session.save(user);
这里为什么我们只调用一个save呢,原因就在我们的tuser映射文件中的cascade属性,它被设为all,即表明当我们对tuser进行保存,更新,删除等操作时,tpassport也会进行相应的操作,所以这里我们不用写session.save(passport)。我们看到后台:
hibernate: insert into user4 (name, age) values (?, ?) hibernate: insert into passport4 (serial, expiry, id) values (?, ?, ?)
下面我们再来一个测试类,测试查询:
public static void main(string[] args) { configuration cfg = new configuration().configure(); sessionfactory sessionfactory = cfg.buildsessionfactory(); session session = sessionfactory.opensession(); tuser user = (tuser)session.load(tuser.class,new integer(3)); system.out.println(user.getname()+":"+user.getpassport().getserial()); }
我们可以看到hibernate的sql语句是:
hibernate:
select tuser0_.id as id0_1_, tuser0_.name as name0_1_, tuser0_.age as age0_1_, tpassport1_.id as id1_0_, tpassport1_.serial as serial1_0_, tpassport1_.expiry as expiry1_0_ from user4 tuser0_ left outer join passport4 tpassport1_ on tuser0_.id=tpassport1_.id where tuser0_.id=?
hibernate:
select tuser0_.id as id0_0_, tuser0_.name as name0_0_, tuser0_.age as age0_0_ from user4 tuser0_ where tuser0_.id=?
select tpassport0_.id as id1_0_, tpassport0_.serial as serial1_0_, tpassport0_.expiry as expiry1_0_ from passport4 tpassport0_ where tpassport0_.id=?
也许很多人会问为什么测试的时候不用tpassport查出tuser呢,其实也可以的,因为它们是一对一的关系,谁查谁都是一样的。
外键关联
现在我们看一下通过外键来进行关联的一对一关联。
还是一贯的直接上例子:我们写了两个实体类,tgroup和tuser
public class tgroup implements serializable{ private static final long serialversionuid = 1l; private int id; private string name; private tuser user; //省略get/set方法 } public class tuser implements serializable{ private static final long serialversionuid = 1l; private int id; private int age; private string name; private tgroup group; //省略get/set方法 }
实体类完了我们就看一下映射文件:
<hibernate-mapping package="org.hibernate.tutorial.domain5"> <class name="tuser" table="user5"> <id name="id" column="id"> <generator class="native" /> </id> <property name="name" type="java.lang.string" column="name"/> <property name="age" type="java.lang.integer" column="age"/> <many-to-one name="group" class="tgroup" column="group_id" unique="true" /> </class> </hibernate-mapping>
这里我们看到是用many-to-one标签而不是one-to-one,为什么呢?
这里以前用的时候也没多在注意,反正会用就行,但这次看了夏昕的书终于明白了,实际上这种通过外键进行关联方式只是多对一的一种特殊方式而已,我们通过unique="true"限定了它必须只能有一个,即实现了一对一的关联。
接下来我们看一下tgroup的映射文件:
<hibernate-mapping package="org.hibernate.tutorial.domain5"> <class name="tgroup" table="group5"> <id name="id" column="id"> <generator class="native" /> </id> <property name="name" type="java.lang.string" column="name"/> <one-to-one name="user" class="tuser" property-ref="group" /> </class> </hibernate-mapping>
这里,注意,我们又用到了one-to-one,表明当前的实体和tuser是一对一的关系,这里我们不用many-to-one,而是通过one-to-one指定了tuser实体中通过哪个属性来关联当前的类tgroup。这里我们指定了tuser是通过group属性和tuser进行关联的。property-ref指定了通过哪个属性进行关联。
下面我们看测试类:
public class hibernatetest { public static void main(string[] args) { configuration cfg = new configuration().configure(); sessionfactory sessionfactory = cfg.buildsessionfactory(); session session = sessionfactory.opensession(); session.begintransaction(); tgroup group = new tgroup(); group.setname("testgroup"); tuser user = new tuser(); user.setage(23); user.setname("test"); user.setgroup(group); group.setuser(user); session.save(group); session.save(user); session.gettransaction().commit(); session.close(); } }
注意,这次我们的代码中需要进行两次的保存,因为它们对各自都有相应的对应,只保存一个都不会对另外一个有什么操作。所以我们需要调用两次保存的操作。最后进行提交。
hibernate打印出语句:
hibernate: insert into group5 (name) values (?) hibernate: insert into user5 (name, age, group_id) values (?, ?, ?)
这说明我们正确地存入了两个对象值。
我们写多一个测试类进行查询:
public static void main(string[] args) { configuration cfg = new configuration().configure(); sessionfactory sessionfactory = cfg.buildsessionfactory(); session session = sessionfactory.opensession(); tuser user = (tuser)session.load(tuser.class,new integer(1)); system.out.println("from user get group:"+user.getgroup().getname()); tgroup group = (tgroup)session.load(tgroup.class,new integer(1)); system.out.println("from group get user:" + group.getuser().getname()); session.close(); }
我们都可以得到正确的结果,这表明我们可以通过两个对象拿出对方的值,达到了我们的目的。
这个例子中用到的tgroup和tuser只是例子而已,实际上现实生活中的user一般都对应多个group。
下一篇: Yii实现复选框批量操作实例代码