Hibernate3一对一关联剖析和设置

在关联中,一般来说都存在着单向关联和双向关联。然后,在一对一关联中,我觉得是不存在这样单向一对一关联的,因为一对一关联双方需要相互感知。在Hibernate中,一对一关联的设置存在两种方式:共享主键和唯一外键。

共享主键

关系的拥有者负责生成这样的一个主键,然后让关系的另一方知道,下面用关于居民和护照的一个例子来说明。

首先,居民拥有三个属性(id、name、passport),护照也拥有三个属性(id、serialNo、person)。

数据库设计

由于是共享主键,那么数据库的设计如下:

t_person

person_idperson_name
t_passport

passport_idpassport_serialNo
为什么这样子设计呢,因为一个居民和一个护照,如果这护照属于这个居民,那么他们的主键值是一样的,我只要知道这个居民的主键值,那就知道它对应的护照的主键值,即有person_id = passport_id。

java类设计

Person

@SuppressWarnings("serial")
public class Person implements Serializable {

	private Integer id;
	
	private String name;
	
	private Passport passport;
}
Passport

@SuppressWarnings("serial")
public class Passport implements Serializable {

	private Integer id;
	
	private String serialNo;
	
	private Person person;

}
关系映射文件配置

从上述意义上来说,Person是这个一对一关联关系的拥有者,那么自然是由Person来生成主键,然后再将这个主键值赋值给Passport。

Person.hbm.xml

    <hibernate-mapping>
    	<class name="com.daniel.model.persistence.Person" table="t_person">
    		<id name="id" column="person_id" type="java.lang.Integer">
    			<generator class="native"></generator>
    		</id>
    		
    		<property name="name" column="person_name" type="java.lang.String">
    		</property>
    		
    		<one-to-one name="passport" cascade="all" class="com.daniel.model.persistence.Passport">
    		</one-to-one>
    	</class>
    </hibernate-mapping>
级联方式自然是选择all,因为无论增删改都需要进行表之间的关联。

Passport.hbm.xml

    <hibernate-mapping>
    	<class name="com.daniel.model.persistence.Passport" table="t_passport">
    		<id name="id" column="passport_id" type="java.lang.Integer">
    			<generator class="foreign">
    				<param name="property">person</param>
    			</generator>
    		</id>
    		
    		<property name="serialNo" column="passport_serialNo" type="java.lang.String">
    		</property>
    		
    		<one-to-one name="person" class="com.daniel.model.persistence.Person" constrained="true">
    		</one-to-one>
    	</class>
    </hibernate-mapping>
注意,当使用foreign这种主键生成策略的时候,表明需要传递一个参数给当前对象作为主键,上面配置的是使用person属性的主键作为参数。

运行结果

		Session session = HibernateSessionFactory.getSession();
		Transaction tx = null;
		try
		{
			tx= session.beginTransaction();
			
			Person per = new Person();
			per.setName("张三");
			
			Passport pass = new Passport();
			pass.setSerialNo("wdqwdqdqw12312312");
			
			/*
			 * 必须同时设置,否则只会插入一条记录,正确来说应该是插入两条记录
			 */
			per.setPassport(pass);
			pass.setPerson(per);
			
			session.save(per);
			
			tx.commit();
		}
		catch(Exception e)
		{
			tx.rollback();
		}
		finally
		{
			HibernateSessionFactory.closeSession();
		}
可以看到执行了的sql语句

Hibernate: insert into t_person (person_name) values (?)
Hibernate: insert into t_passport (passport_serialNo, passport_id) values (?, ?)
可以看到前一句sql中,t_person中的记录主键是自动生成的,而后一句的sql中,t_passport的记录主键是赋值的。

如果我们执行查询

Person per = (Person) session.get(Person.class, 5);
看到这时候只执行了一条sql查询语句,是使用左连接的

Hibernate: select person0_.person_id as person1_1_1_, person0_.person_name as person2_1_1_, passport1_.passport_id as passport1_0_0_, 
passport1_.passport_serialNo as passport2_0_0_ from t_person person0_ 
left outer join t_passport passport1_ on person0_.person_id=passport1_.passport_id where person0_.person_id=?

唯一外键关联

这种方式需要在关系拥有者所在的表中加入一个外键列,因为两者的主键不再是一一对应,所有需要在关系的拥有者处加入一个列来记录关系的另一方的主键。

数据库的设计

t_person1

person_idperson_namepassport_id
t_passport1

passport_idpassport_serialNo
java类的设计

跟共享主键的类设计一样。

映射文件配置

Person.hbm.xml

    <hibernate-mapping>
    	<class name="com.daniel.model.persistence.Person" table="t_person1">
    		<id name="id" column="person_id" type="java.lang.Integer">
    			<generator class="native"></generator>
    		</id>
    		
    		<property name="name" column="person_name" type="java.lang.String">
    		</property>
    		
    		<many-to-one name="passport" cascade="all" class="com.daniel.model.persistence.Passport">
    			<column name="passport_id"></column>
    		</many-to-one>
    	</class>
    </hibernate-mapping>
这时候,用<many-to-one>来配置关系的拥有者,因为唯一外键关联是一对多的特殊形式,column指定外键列名。

Passport.hbm.xml

    	<class name="com.daniel.model.persistence.Passport" table="t_passport1">
    		<id name="id" column="passport_id" type="java.lang.Integer">
    			<generator class="native"></generator>
    		</id>
    		
    		<property name="serialNo" column="passport_serialNo" type="java.lang.String">
    		</property>
    		
    		<one-to-one name="person" class="com.daniel.model.persistence.Person">
    		</one-to-one>
    	</class>
运行结果
		Session session = HibernateSessionFactory.getSession();
		Transaction tx = null;
		try
		{
			tx= session.beginTransaction();
			
			Person per = new Person();
			per.setName("李四");
			
			Passport pass = new Passport();
			pass.setSerialNo("fqwefwef1332");
			
			per.setPassport(pass);
			
			session.save(per);
			
			tx.commit();
		}
		catch(Exception e)
		{
			tx.rollback();
		}
		finally
		{
			HibernateSessionFactory.closeSession();
		}
这时候,不需要两方同时设置,只需要设置一方就可以了。可以看到执行的sql语句,先插入一条t_passport1记录,再插入一条t_person1记录。

Hibernate: insert into t_passport1 (passport_serialNo) values (?)
Hibernate: insert into t_person1 (person_name, passport_id) values (?, ?)
执行查询

			Person per = (Person) session.get(Person.class, 1);
			
			System.out.println(per.getPassport().getId());
输出的sql语句

Hibernate: select person0_.person_id as person1_1_0_, person0_.person_name as person2_1_0_, person0_.passport_id as passport3_1_0_ from
 t_person1 person0_ where person0_.person_id=?
Hibernate: select passport0_.passport_id as passport1_0_1_, passport0_.passport_serialNo as passport2_0_1_, person1_.person_id as person1_1_0_, 
person1_.person_name as person2_1_0_, person1_.passport_id as passport3_1_0_ from t_passport1 passport0_ left outer join t_person1 person1_ on 
passport0_.passport_id=person1_.person_id where passport0_.passport_id=?


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值