一、Hibernate 一对一映射关系实现:
两个对象之间一对的关系,例如:Person(人)-IdCard(身份证)
有两种策略可以实现一对一的关联映射:
1.按照主键映射:即让两个对象具有相同的主键值,以表明它们之间的一一对应的关系;数据库表不会有额外的字段来维护它们之间的关系,仅通过表的主键来关联。
IdCard.java:
package com.tao.entity; public class IdCard { private Integer id; private String cardNo; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getCardNo() { return cardNo; } public void setCardNo(String cardNo) { this.cardNo = cardNo; } }
IdCard.hbm.xml:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.tao.entity"> <class name="IdCard" table="t_idCard"> <id name="id"> <generator class="native"> </generator> </id> <property name="cardNo"></property> </class> </hibernate-mapping>
Person.java
package com.tao.entity; public class Person { private Integer id; private String name; 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 IdCard getIdCard() { return idCard; } public void setIdCard(IdCard idCard) { this.idCard = idCard; } }
Person.hbm.xml:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.tao.entity"> <class name="Person" table="t_person"> <id name="id"> <!-- 采用foreign生成策略,foreign会取得关联对象的标识 --> <generator class="foreign"> <!-- property只关联对象 --> <param name="property">idCard</param> </generator> </id> <property name="name"></property> <!--one-to-one指示hibernate如何加载其关联对象,默认根据主键加载 也就是拿到关系字段值,根据对端的主键来加载关联对象 constrained:表明当前类对应的表与被关联的表之间是否存在着外键约束 默认值为false --> <one-to-one name="idCard" constrained="true"></one-to-one> </class> </hibernate-mapping>
hibernate.cfg.xml:
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <!-- Hibernate 核心配置文件 --> <hibernate-configuration> <session-factory> <!-- 配置关于数据库连接的四个项:driverClass url username password --> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://localhost:3306/hibernate</property> <property name="connection.username">root</property> <property name="connection.password">root</property> <!-- 方言 --> <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property> <!-- 控制台显示SQL --> <property name="show_sql">true</property> <!-- 自动更新表结构 --> <property name="hbm2ddl.auto">update</property> <!-- 引入的映射文件 --> <mapping resource="com/tao/entity/IdCard.hbm.xml"/> <mapping resource="com/tao/entity/Person.hbm.xml"/> </session-factory> </hibernate-configuration>
TestOne2One:
package com.tao.test; import static org.junit.Assert.*; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import com.tao.entity.IdCard; import com.tao.entity.Person; import com.tao.util.HibernateUtil; public class TestOne2One { SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); private Session session; @Before public void setUp() throws Exception { session = sessionFactory.openSession(); session.beginTransaction(); } @After public void tearDown() throws Exception { session.getTransaction().commit(); session.close(); } @Test public void test() { IdCard idCard = new IdCard(); idCard.setCardNo("420291156208036510"); Person person = new Person(); person.setName("张三"); person.setIdCard(idCard); session.save(person); } }
生成数据库表结构:
console:
INFO: HHH000126: Indexes: [primary] 一月 28, 2019 10:43:37 上午 org.hibernate.tool.hbm2ddl.SchemaUpdate execute INFO: HHH000232: Schema update complete Hibernate: insert into t_idCard (cardNo) values (?) Hibernate: insert into t_person (name, id) values (?, ?)
数据库保存了两条记录,没有抛出TransientObjectException, 是由一对一关联映射的特性决定的,它必须先保存关联对象IdCard,这样它采用foreign映射策略才能取得关联对象的标识,也就是它默认了cascade属性.
但是我们试试查询:
@Test public void testQuery() throws Exception { // IdCard idCard = (IdCard) session.get(IdCard.class, 1); // System.out.println(idCard.getPerson().getName()); Person person = (Person) session.get(Person.class, 1); System.out.println(person.getIdCard().getCardNo()); }
当我们通过 idCard.getPerson().getName() 时,抛错“java.lang.NullPointerException”,但是我们通过person.getIdCard().getCardNo()时,可以正常查询到数据:
Hibernate: select person0_.id as id1_3_0_, person0_.name as name2_3_0_ from t_person person0_ where person0_.id=? Hibernate: select idcard0_.id as id1_2_0_, idcard0_.cardNo as cardNo2_2_0_ from t_idCard idcard0_ where idcard0_.id=? 420291156208036510
因为我们只在Person中维护了IdCard对象,只能单向查询,这种便是单向的一对一。
上述是实现了单向的 一对一映射,我没们现在改为双向一对一。
首先在IdCard.java中新增一个Person对象属性:
package com.tao.entity; public class IdCard { private Integer id; private String cardNo; private Person person; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getCardNo() { return cardNo; } public void setCardNo(String cardNo) { this.cardNo = cardNo; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } }
其次在IdCard.hbm.xml 新增:
<one-to-one name="person"></one-to-one> 映射关系 这个person 就是属性对象名称。
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.tao.entity"> <class name="IdCard" table="t_idCard"> <id name="id"> <generator class="native"> </generator> </id> <property name="cardNo"></property> <one-to-one name="person"></one-to-one> </class> </hibernate-mapping>
再次测试查询:
@Test public void testQuery() throws Exception { IdCard idCard = (IdCard) session.get(IdCard.class, 1); System.out.println(idCard.getPerson().getName()); /*Person person = (Person) session.get(Person.class, 1); System.out.println(person.getIdCard().getCardNo());*/ }
console:
INFO: HHH000126: Indexes: [primary] 一月 29, 2019 10:24:25 上午 org.hibernate.tool.hbm2ddl.SchemaUpdate execute INFO: HHH000232: Schema update complete Hibernate: select idcard0_.id as id1_2_0_, idcard0_.cardNo as cardNo2_2_0_, person1_.id as id1_3_1_, person1_.name as name2_3_1_ from t_idCard idcard0_ left outer join t_person person1_ on idcard0_.id=person1_.id where idcard0_.id=? 张三
可以通过Idcard查询实现,至此 双向一对一 按照主键映射 关系建立完成。
2.按照外键映射:外键关联,本来是用于多对一的配置,但是加上唯一的限制之后(采用<many-to-one>标签来映射,指定多的一端unique为true,这样就限制了多的一端的多重性为一),也可以用来表示一对一关联关系,其实它就是多对一的特殊情况。
IdCard.java:
package com.tao.entity; public class IdCard { private Integer id; private String cardNo; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getCardNo() { return cardNo; } public void setCardNo(String cardNo) { this.cardNo = cardNo; } }
Person.java:
package com.tao.entity; public class Person { private Integer id; private String name; 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 IdCard getIdCard() { return idCard; } public void setIdCard(IdCard idCard) { this.idCard = idCard; } }
IdCard.hbm.xml:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.tao.entity"> <class name="IdCard" table="t_idCard"> <id name="id"> <generator class="native"> </generator> </id> <property name="cardNo"></property> </class> </hibernate-mapping>
Person.hbm.xml:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.tao.entity"> <class name="Person" table="t_person"> <id name="id"> <!-- 采用foreign生成策略,foreign会取得关联对象的标识 --> <generator class="native"> <!-- property只关联对象 --> <param name="property">idCard</param> </generator> </id> <property name="name"></property> <many-to-one name="idCard" unique="true" cascade="all" /> </class> </hibernate-mapping>
TestOne2One:
@Test public void testSave() { IdCard idCard = new IdCard(); idCard.setCardNo("420291156208036510"); Person person = new Person(); person.setName("张三"); person.setIdCard(idCard); session.save(person); }
生成数据库表结构:
查询:
@Test public void testQuery() throws Exception { /*IdCard idCard = (IdCard) session.get(IdCard.class, 1); System.out.println(idCard.getPerson().getName());*/ Person person = (Person) session.get(Person.class, 1); System.out.println(person.getIdCard().getCardNo()); }
目前也是 一对一关系关系映射 按外键关联 单向实现。
我们现在实现双向一对一 按外键映射:
在IdCard.java中添加person 对象属性。(和双向一对一 按主键关联步骤一致)
package com.tao.entity; public class IdCard { private Integer id; private String cardNo; private Person person; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getCardNo() { return cardNo; } public void setCardNo(String cardNo) { this.cardNo = cardNo; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } }
在:IdCard.hbm.xml中加入: <one-to-one name="person" property-ref="idCard"></one-to-one>
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.tao.entity"> <class name="IdCard" table="t_idCard"> <id name="id"> <generator class="native"> </generator> </id> <property name="cardNo"></property> <one-to-one name="person" property-ref="idCard"></one-to-one> </class> </hibernate-mapping>
此处的 property-ref="idCard" 是 person.hbm.xml中的 <many-to-one name="idCard" unique="true" cascade="all" /> name ,也可以理解成 Person 中的 idCard 对象属性名。
测试:
@Test public void testQuery() throws Exception { IdCard idCard = (IdCard) session.get(IdCard.class, 1); System.out.println(idCard.getPerson().getName()); /*Person person = (Person) session.get(Person.class, 1); System.out.println(person.getIdCard().getCardNo());*/ }
成功查询出结果。自此 但双向一对一关系映射 按主键关联、按外键关联均已完成。
关于一对多关系 在我之前博客 四、Hibernate关联关系一对多映射 已经记录过了,此处就不记录了。写东西 一是自己忘了有地方可以找,二是 方便自己理解,我写的也很皮毛,大家各取所需。
先理解一下复合主键 和 联合主键:
复合主键: 一个主键是由多个字段复合组成 比如 学生表主键由 stuId+stuName 两个组成 来保证唯一性,因为学生姓名容易重名,但是stuId 这种代理主键(不具有业务含义的) 并不是每张表都有 所以 也有可能是stuName+stuNo 之类的 这种一个主键多个字段组成 就称做复合主键。
联合主键:主键和主键之间的联合,两个主键可以重复,但是联合起来必须唯一。
所谓的多对多,其实就是两个多对一,这个一 就是中间表。
举例:学生和课程 一个学生可以选多门课,一门课可以被多个学生选。
Student.java:
package com.tao.entity; import java.util.HashSet; import java.util.Set; public class Student { private Integer studentId; private String studentName; private Set<Course> courses = new HashSet<Course>(); public Integer getStudentId() { return studentId; } public void setStudentId(Integer studentId) { this.studentId = studentId; } public String getStudentName() { return studentName; } public void setStudentName(String studentName) { this.studentName = studentName; } public Set<Course> getCourses() { return courses; } public void setCourses(Set<Course> courses) { this.courses = courses; } }
Student.hbm.xml:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.tao.entity"> <class name="Student" table="t_student"> <id name="studentId" column="student_id"> <generator class="native"></generator> </id> <property name="studentName" column="student_name" length="50"></property> <set name="courses" table="t_student_course" cascade="all"> <key column="s_id"></key> <many-to-many class="Course" column="c_id"></many-to-many> </set> </class> </hibernate-mapping>
Course.java:
package com.tao.entity; import java.util.HashSet; import java.util.Set; public class Course { private Integer courseId; private String courseName; private Set<Student> students = new HashSet<Student>(); public Integer getCourseId() { return courseId; } public void setCourseId(Integer courseId) { this.courseId = courseId; } public String getCourseName() { return courseName; } public void setCourseName(String courseName) { this.courseName = courseName; } public Set<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } }
Course.hbm.xml:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.tao.entity"> <class name="Course" table="t_course"> <id name="courseId" column="course_id"> <generator class="native"></generator> </id> <property name="courseName" column="course_name" length="100" ></property> <set name="students" table="t_student_course" inverse="true"> <key column="c_id"></key> <many-to-many class="Student" column="s_id"></many-to-many> </set> </class> </hibernate-mapping>
Test:
@Test public void testSave() { Course course1 = new Course(); course1.setCourseName("语文"); Course course2 = new Course(); course2.setCourseName("数学"); Student student1 = new Student(); student1.setStudentName("张三"); student1.getCourses().add(course1); student1.getCourses().add(course2); Student student2 = new Student(); student2.setStudentName("李四"); student2.getCourses().add(course1); student2.getCourses().add(course2); session.save(student1); session.save(student2); } @Test public void testQuery() throws Exception { Query query = session.createQuery("from Student"); List<Student> list = query.list(); for(Student s:list){ System.out.println(s.getStudentName() + "所选课程:"); Set<Course> courses = s.getCourses(); Iterator<Course> it = courses.iterator(); while(it.hasNext()){ Course course = it.next(); System.out.println(course.getCourseName()); } } }
数据库表结构:
console:
张三所选课程: Hibernate: select courses0_.s_id as s_id1_6_0_, courses0_.c_id as c_id2_7_0_, course1_.course_id as course_i1_2_1_, course1_.course_name as course_n2_2_1_ from t_student_course courses0_ inner join t_course course1_ on courses0_.c_id=course1_.course_id where courses0_.s_id=? 语文 数学 李四所选课程: Hibernate: select courses0_.s_id as s_id1_6_0_, courses0_.c_id as c_id2_7_0_, course1_.course_id as course_i1_2_1_, course1_.course_name as course_n2_2_1_ from t_student_course courses0_ inner join t_course course1_ on courses0_.c_id=course1_.course_id where courses0_.s_id=? 语文 数学