Hibernate One-To-One 关联

现实生活中,一对一(One To One)关系的实体比比皆是。比如,人和身份证的关系,人和社会属性的关系等。

 

在Hibernate中,OneToOne关系分为两种策略:主键关联和唯一外键关联。主键关联,就是两个一对一的实体共用相同的id。比如,人(Person)和身份证(IdCard),将Person的id主键,关联到IdCard的主键。这样,两个实体,就通过主键关联在一起了。唯一外键的含义也很明确。就是在Person实体中定义一个额外的字段,比如card_id,用这个字段外键关联到IdCard的主键id。

 

下面就让我们看看主键关联:

 

Person.java

public class Person { private Integer id; private String name; private String sex; private Integer age; private Idcard idcard; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Idcard getIdcard() { return idcard; } public void setIdcard(Idcard idcard) { this.idcard = idcard; }

 

Idcard.java

public class Idcard { private Integer id; private String cardNum; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getCardNum() { return cardNum; } public void setCardNum(String cardNum) { this.cardNum = cardNum; } }

 

以上是两个实体。

 

Person.hbm.xml

 

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="demo.hibernate.domain"> <class name="Person"> <id name="id" unsaved-value="null"> <!--在单独使用的时候,可以使用序列策略,这里对应的数据库是Oracle,不同的数据库,拥有不同的主键生成策略--> <!--<generator class="sequence">--> <!--<param name="sequence">seq_person</param>--> <!--</generator>--> <!--因为人和身份证是一对一关联,避免冗余的数据生成,采用主键相同的策略--> <generator class="foreign"> <param name="property">idcard</param> </generator> </id> <property name="name"/> <property name="sex" /> <property name="age" /> <!--cascade=all :在插入一条Person信息的时候,可以插入一条Idcard。constrained=true:因为这里存在一个外键约束,person的主键,关联到idcard的主键--> <!--这里lazy=false,这样,在抓取person信息的时候,就同时抓取了idcard。否则,如果lazy=proxy,这也是hibernate默认的抓取方式,这样你在获取person后再获取idcard,就 会报no Session 异常。在web开发中,可以开启openSessionInView,确保Session的生命周期延续到前台页面,lazy=false,对性能会有影响--> <one-to-one name="idcard" cascade="none" constrained="true" lazy="false"></one-to-one> </class> </hibernate-mapping>

 

Idcard.hbm.xml

 

<?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 package="demo.hibernate.domain"> <class name="Idcard"> <id name="id" unsaved-value="null"> <generator class="sequence"> <param name="sequence">seq_person</param> </generator> </id> <property name="cardNum"/> </class> </hibernate-mapping>

 

以上是Person.java和Idcard.java对应的映射文件。

 

public class PersonDao { public Integer save(Person person){ Integer id = null; try { if(person == null){ return null; } Session s = HibernateUtils.getCurrentSession(); Transaction t = s.beginTransaction(); id = (Integer)s.save(person); t.commit(); } catch (HibernateException e) { throw new RuntimeException("保存person信息失败"); }finally { HibernateUtils.closeSession(); } return id; } public Person get(int id){ Person p = null; try { Session s = HibernateUtils.getCurrentSession(); p = (Person)s.get(Person.class, id); return p; } catch (HibernateException e) { throw new RuntimeException("获取person信息失败"); } finally { HibernateUtils.closeSession(); } } public List<Person> getPersonList(String name){ List<Person> persons = null; try { Session s = HibernateUtils.getCurrentSession(); StringBuilder hql = new StringBuilder("from Person"); Query q = null; if(name != null && !name.isEmpty()){ hql.append("where name = :name"); q = s.createQuery(hql.toString()); q.setParameter("name",name); }else { q = s.createQuery(hql.toString()); } persons = q.list(); return persons; } catch (HibernateException e) { throw new RuntimeException("获取person信息失败"); } finally { HibernateUtils.closeSession(); } } }

 

上面是PersonDao.java,封装了对Person实体的基本操作。

 

public class TestPersonDao { static Person person = null; static Idcard idcard = null; static PersonDao personDao = null; static Integer personId = 21; @BeforeClass public static void init(){ initOne2One(); } private static void initOne2One(){ idcard = new Idcard(); idcard.setCardNum("342401198911045870"); person = new Person(); person.setName("new"); person.setAge(12); person.setSex("m"); person.setIdcard(idcard); //这里注意,Person和Idcard是一对一关联。 //这里,在保存Person的时候,idcard是一个瞬时对象,未持久化。但是,配置的时候,cascade=all, //这样,在保存Person对象的时候,hibernate会首先将idcard一并保存。 personDao = new PersonDao(); } @Test public void testSave(){ personId = personDao.save(person); Assert.assertNotNull(personId); Integer id1 = personDao.save(null); Assert.assertNull(id1); } @Test public void testGet(){ Person p = personDao.get(personId); Assert.assertNotNull(p); Assert.assertNotNull(p.getIdcard()); Assert.assertNotNull(p.getIdcard().getCardNum()); } @Test public void testGetPersonList(){ List<Person> persons = personDao.getPersonList(null); //One-To-One:这里,就会出现一个N+1次查询的问题, //因为One-To-One关联时,在Idcard中并不存在和person关联的信息, //只有先查到person,然后再根据每一个person的id获取到与其关联的idcard。这样总共就做了N+1次查询。 //这样非常消耗性能 //@select * from person; 得到10条数据。 //接下来,做10次循环:@for 1 to 10 do select * from idcard d where d.id = person.id end for; } }

 

上面是PersonDao的测试类。

 

 

你可能感兴趣的:(Hibernate One-To-One 关联)