Hibernate关联映射1:一对一主键关联
2张表之间通过主键形成一对一映射关系,如一个人只能有一张身份证:
t_identity_card表建表语句:
CREATE TABLE `t_identity_card` ( `id` int(11) NOT NULL, `identity` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=gb2312;
t_person表建表语句:
CREATE TABLE `t_person` ( `id` int(11) NOT NULL, `name` varchar(255) NOT NULL, `age` int(11) NOT NULL, PRIMARY KEY (`id`), CONSTRAINT `FK_ID` FOREIGN KEY (`id`) REFERENCES `t_identity_card` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=gb2312;
hibernate.cfg.xml:
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://localhost:3306/mydb</property> <property name="connection.username">root</property> <property name="connection.password">196428</property> <property name="hibernate.show_sql">true</property> <mapping resource="com/po/IdentityCard.hbm.xml" /> <mapping resource="com/po/Person.hbm.xml" /> </session-factory> </hibernate-configuration>
单向一对一主键关联:
t_identity_card表:
public class IdentityCard implements Serializable { private static final long serialVersionUID = 1L; private int id; private int identity; private Person person; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getIdentity() { return identity; } public void setIdentity(int identity) { this.identity = identity; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } }
由于t_identity_card表使用foreign主键生成策略,故在t_identity_card表的配置文件中必须配置主键生成表t_person,这样在向t_identity_card表中插入数据时才能找到所需的主键值,所以单向一对一主键关联中只有t_person<-t_identity_card,没有t_person->t_identity_card
<?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="com.po.IdentityCard" table="t_identity_card"> <id name="id" column="id" type="integer"> <generator class="foreign"> <param name="property">person</param> </generator> </id> <property name="identity" column="identity" type="integer"/> <one-to-one name="person" class="com.po.Person"/> </class> </hibernate-mapping>
t_person表:
public class Person implements Serializable { private static final long serialVersionUID = 1L; private int id; private String name; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
<?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="com.po.Person" table="t_person"> <id name="id" column="id" type="integer"> <generator class="assigned"/> </id> <property name="name" column="name" type="string"/> <property name="age" column="age" type="integer"/> </class> </hibernate-mapping>
测试一下:
public class Test { public static void main(String[] args) { Configuration conf = new Configuration(); SessionFactory sessionFactory = conf.configure().buildSessionFactory(); Session session = sessionFactory.openSession(); IdentityCard identityCard = new IdentityCard(); identityCard.setIdentity(123456); Person person = new Person(); person.setId(1); person.setName("sean"); person.setAge(25); identityCard.setPerson(person); Transaction tran = session.beginTransaction(); session.save(identityCard); tran.commit(); IdentityCard identityCard2 = (IdentityCard)session.get(IdentityCard.class, 1); Person person2 = identityCard2.getPerson(); System.out.println(person2.getName()); session.close(); } }
测试结果为:
Hibernate: insert into t_identity_card (identity, id) values (?, ?) sean
只向t_identity_card表中插入了数据,t_person表为空,由于没有设置关联关系的cascade属性,在持久化一张表的时候,并不会级联的持久化另一张表
将t_identity_card表的映射文件中的关联关系修改为:<one-to-one name="person" class="com.po.Person"cascade="all" />
Hibernate: select person_.id, person_.name as name1_, person_.age as age1_ from t_person person_ where person_.id=? Hibernate: insert into t_identity_card (identity, id) values (?, ?) Hibernate: insert into t_person (name, age, id) values (?, ?, ?) sean
双向一对一主键关联:
这时需要对t_person表的配置做一些修改:
public class Person implements Serializable { private static final long serialVersionUID = 1L; private int id; private String name; private int age; private IdentityCard identityCard;//新增 public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public IdentityCard getIdentityCard() { return identityCard; } public void setIdentityCard(IdentityCard identityCard) { this.identityCard = identityCard; } }
<?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="com.po.Person" table="t_person"> <id name="id" column="id" type="integer"> <generator class="native"/> </id> <property name="name" column="name" type="string"/> <property name="age" column="age" type="integer"/> <!--新增--> <one-to-one name="identityCard" class="com.po.IdentityCard" cascade="all" /> </class> </hibernate-mapping>
测试一下:
public class Test { public static void main(String[] args) { Configuration conf = new Configuration(); SessionFactory sessionFactory = conf.configure().buildSessionFactory(); Session session = sessionFactory.openSession(); IdentityCard identityCard = new IdentityCard(); identityCard.setIdentity(123456); Person person = new Person(); person.setId(1); person.setName("sean"); person.setAge(25); identityCard.setPerson(person); person.setIdentityCard(identityCard); Transaction tran = session.beginTransaction(); session.save(identityCard); tran.commit(); Person person_tmp = (Person)session.get(Person.class, 1); IdentityCard id_card_tmp = person_tmp.getIdentityCard(); System.out.println(id_card_tmp.getIdentity()); id_card_tmp = (IdentityCard)session.get(IdentityCard.class, 1); person_tmp = id_card_tmp.getPerson(); System.out.println(person_tmp.getName()); session.close(); } }
测试结果为:
Hibernate: select person_.id, person_.name as name1_, person_.age as age1_ from t_person person_ where person_.id=? Hibernate: insert into t_identity_card (identity, id) values (?, ?) Hibernate: insert into t_person (name, age, id) values (?, ?, ?) 123456 sean
Hibernate首先持久化了identityCard对象,将测试代码中的session.save(identityCard)修改为session.save(person),再次测试:
Hibernate: insert into t_person (name, age, id) values (?, ?, ?) Exception in thread "main" org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update ...... Caused by: java.sql.BatchUpdateException: Cannot add or update a child row: a foreign key constraint fails (`mydb`.`t_person`, CONSTRAINT `FK_ID` FOREIGN KEY (`id`) REFERENCES `t_identity_card` (`id`)) ......
Hibernate这次首先持久化了person对象,由于t_person表中的id字段建立了外键关系,故持久化失败,删除掉t_person表中id字段的外键关系之后,插入正常:
Hibernate: insert into t_person (name, age, id) values (?, ?, ?) Hibernate: insert into t_identity_card (identity, id) values (?, ?) 123456 sean
可见Hibernate有时不是很智能,save的对象将会首先被持久化,这时要特别注意表上建立的关联关系