八、Hibernate 一对一映射、多对多映射关系实现

一、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=?
语文
数学

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值