参考文章:Hibernate实体关联关系映射,作者:骑猪下不来的猴
单向关联是指只有A方有B方的关联信息而B方没有A方的关联信息 。
例如:
A——>B
A对象中有B对象的关联信息
B对象中没有A对象的关联信息
我们可以通过A对象中B的关联信息查询或修改B对象的信息但无法通过B对象来查询修改A对象的信息
单向关联又分为:
例如:
A<——>B
A对象中有B对象的关联信息
B对象中也有A对象的关联信息
我们可以通过A对象中B的关联信息查询或修改B对象的信息也可以通过B对象中A的关联信息来查询修改A对象的信息
双向关联又分为:
只在一方配置关联关系而另一方不进行配置。
例如:
Customer——>Order
一个顾客(Customer)对应多个订单(Order)。
当业务要求通过Customer可以获取该顾客下所有Order,但不能通过Order找到Customer时,这就需要一对多单向关联关系。
一对多单向关联关系的实现是通过在Customer类中创建Order类的集合属性,这样我们能够通过Customer获取和修改该客户对应的Order集合。(但是不能通过Order获取和修改Customer)
order表中有外键指向customer表。
注意我们在顾客类中用S集合封装了顾客名下的订单信息。
Customer类中要有订单Order的集合。
import lombok.Getter;
import lombok.Setter;
import java.util.Set;
@Getter
@Setter
public class Customer {
private int id;
private String name;
private Integer age;
//在顾客表中加入订单集合
private Set<Order> orders;
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", orders=" + orders +
'}';
}
}
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Order {
private int id;
private String orderNumber;
private Double orderPrice;
//注意这里没有定义数据库中的外键属性
@Override
public String toString() {
return "Order{" +
"id=" + id +
", orderNumber='" + orderNumber + '\'' +
", orderPrice=" + orderPrice +
'}';
}
}
<hibernate-mapping package="org.example.Entity">
<class name="Customer" table="customer">
<id name="id" column="id" type="java.lang.Integer"/>
<property name="name" column="name" type="java.lang.String"/>
<property name="age" column="age" type="java.lang.Integer"/>
<set name="orders" table="`order`">
<key column="customer_id"/>
<one-to-many class="org.example.Entity.Order"/>
set>
class>
hibernate-mapping>
<hibernate-mapping>
<class name="org.example.Entity.Order" table="`order`">
<id name="id" column="id" type="java.lang.Integer"/>
<property name="orderNumber" column="orderNumber" type="java.lang.String"/>
<property name="orderPrice" column="orderPrice" type="java.lang.Double"/>
class>
hibernate-mapping>
@Test
public void test3(){
//创建SessionFactory,从根路径下获取核心配置文件
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
//创建session
Session session = factory.openSession();
//创建并开启事务
Transaction transaction = session.beginTransaction();
//获取Customer主键为1的顾客信息及其所有订单信息
Customer customer = session.get(Customer.class,1);
//打印数据
System.out.println(customer.toString());
//提交事务
transaction.commit();
//关闭SessionFactory
factory.close();
}
例如:
Customer<——Order
多个订单(Order)属于一个顾客(Customer)。
当业务要求通过Order可以获取该订单所属Customer,但不能通过Customer找到Order时,这就需要多对一单向关联关系。
多对一单向关联关系的实现是通过在Order类中创建Customer类作为属性,这样我们能够通过Order获取和修改该客户Customer的信息。(但是不能通过Customer获取和修改Order)
order表中有外键指向customer表。
package org.example.Entity;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Customer {
private int id;
private String name;
private Integer age;
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
订单类中应该有顾客类,用来指明该订单属于哪个顾客。
订单类中要有顾客属性,类型是顾客类的类型。
package org.example.Entity;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Order {
private int id;
private String orderNumber;
private Double orderPrice;
//订单类中要有顾客Customer属性
private Customer customer;
@Override
public String toString() {
return "Order{" +
"id=" + id +
", orderNumber='" + orderNumber + '\'' +
", orderPrice=" + orderPrice +
", customer=" + customer +
'}';
}
}
<hibernate-mapping package="org.example.Entity">
<class name="Customer" table="customer">
<id name="id" column="id" type="java.lang.Integer"/>
<property name="name" column="name" type="java.lang.String"/>
<property name="age" column="age" type="java.lang.Integer"/>
class>
hibernate-mapping>
<hibernate-mapping>
<class name="org.example.Entity.Order" table="`order`">
<id name="id" column="id" type="java.lang.Integer"/>
<property name="orderNumber" column="orderNumber" type="java.lang.String"/>
<property name="orderPrice" column="orderPrice" type="java.lang.Double"/>
<many-to-one name="customer" column="customer_id" class="org.example.Entity.Customer"/>
class>
hibernate-mapping>
@Test
public void test3(){
//创建SessionFactory,从根路径下获取核心配置文件
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
//创建session
Session session = factory.openSession();
//创建并开启事务
Transaction transaction = session.beginTransaction();
//获取订单主键为111的订单信息及该订单所属的顾客信息
Order order = session.get(Order.class,111);
//打印数据
System.out.println(order.toString());
//提交事务
transaction.commit();
//关闭SessionFactory
factory.close();
}
例如:
Customer<——>Order
一个顾客(Customer)对应多个订单(Order)。
当业务要求通过Customer可以获取该顾客下所有Order,且通过Order能获取对应Customer时,这就需要一对多双向关联关系。
双向关联关系就是同时使用一对多单向关联和多对一单向关联。 所以需要在Customer类中创建Order类的集合属性,并在Order类中创建Customer属性。
双向关联时易错点inverse属性:
我们进行双向关联后,顾客类中有订单类,订单类中有顾客类,当我们想要执行插入操作时,如果双方都维护关联关系,那么会出现重复执行SQL语句的情况,使得效率低下。
所以我们需要配置一个属性,让双向关联的某一方交出维护权。
inverse=“true” 表示将关联关系交由对方控制。inverse=“false” 表示自己控制关联关系。
补充cascade属性:
该属性表示级联操作,cascade=“delete” 设置级联删除,设置后当删除某一行时,会级联删除绑定该行外键的其他行。
order表中有外键指向customer表。
注意我们在顾客类中用S集合封装了顾客名下的订单信息。
Customer类中要有订单Order的集合。
import lombok.Getter;
import lombok.Setter;
import java.util.Set;
@Getter
@Setter
public class Customer {
private int id;
private String name;
private Integer age;
//在顾客表中加入订单集合
private Set<Order> orders;
}
订单类中应该有顾客类,用来指明该订单属于哪个顾客。
订单类中要有顾客属性,类型是顾客类的类型。
package org.example.Entity;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Order {
private int id;
private String orderNumber;
private Double orderPrice;
//订单类中要有顾客Customer属性
private Customer customer;
}
<hibernate-mapping package="org.example.Entity">
<class name="Customer" table="customer">
<id name="id" column="id" type="java.lang.Integer"/>
<property name="name" column="name" type="java.lang.String"/>
<property name="age" column="age" type="java.lang.Integer"/>
<set name="orders" table="`order`" inverse="true" cascade="delete">
<key column="customer_id"/>
<one-to-many class="org.example.Entity.Order"/>
set>
class>
hibernate-mapping>
<hibernate-mapping>
<class name="org.example.Entity.Order" table="`order`">
<id name="id" column="id" type="java.lang.Integer"/>
<property name="orderNumber" column="orderNumber" type="java.lang.String"/>
<property name="orderPrice" column="orderPrice" type="java.lang.Double"/>
<many-to-one name="customer" column="customer_id" class="org.example.Entity.Customer"/>
class>
hibernate-mapping>
@Test
public void test3(){
//创建SessionFactory,从根路径下获取核心配置文件
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
//创建session
Session session = factory.openSession();
//创建并开启事务
Transaction transaction = session.beginTransaction();
//获取订单主键为111的订单信息及该订单所属的顾客信息
Order order = session.get(Order.class,111);
//打印数据
System.out.println(order.getCustomer().getName());
//提交事务
transaction.commit();
//关闭SessionFactory
factory.close();
}
public class AppTest{
@Test
public void test(){
//创建SessionFactory,从根路径下获取核心配置文件
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
//创建session
Session session = factory.openSession();
//创建事务
Transaction transaction = session.beginTransaction();
//创建顾客类对象并给相应属性赋值
CustomerEntity customer = new CustomerEntity();
customer.setId(4);
customer.setName("44");
customer.setAge(18);
//创建订单类对象并给相应属性赋值
OrdersEntity order = new OrdersEntity();
order.setId(4);
order.setOrderNumber("4");
order.setOrderPrice(4.0);
//建立关联关系,若不建立关联关系order表的数据为空
order.setCustomer(customer);
//保存,所有对象都要保存
session.save(customer);
session.save(order);
//提交事务
transaction.commit();
//关闭SessionFactory
factory.close();
}
}
例如:
Pupil===>Course
一个学生(Pupil)有多门课程(Course)。
当业务要求通过Pupil学生可以获取该学生的所有课程Course,但通过Course课程不能获取对应Pupil学生时,这就需要多对多单向关联关系。
多对多关系通过一个中间表来维护。
pupil_course表中有两个外键分别指向pupil表和course表的主键。
Pupil类中有课程Course的集合。
import lombok.Getter;
import lombok.Setter;
import java.util.Set;
@Getter
@Setter
public class Pupil {
private int aid;
private String aname;
private Set<Course> courses;
@Override
public String toString() {
return "Pupil{" +
"aid=" + aid +
", aname='" + aname + '\'' +
", courses=" + courses +
'}';
}
}
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Course {
private int cid;
private String cname;
@Override
public String toString() {
return "Course{" +
"cid=" + cid +
", cname='" + cname + '\'' +
'}';
}
}
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class PupilCourse {
private int id;
private Integer aid;
private Integer cid;
}
<hibernate-mapping>
<class name="org.example.Entity.Pupil" table="pupil">
<id name="aid" column="aid" type="java.lang.Integer"/>
<property name="aname" column="aname" type="java.lang.String"/>
<set name="courses" table="pupil_course" >
<key column="aid"/>
<many-to-many class="org.example.Entity.Course" column="cid"/>
set>
class>
hibernate-mapping>
<hibernate-mapping>
<class name="org.example.Entity.Course" table="course">
<id name="cid" column="cid" type="java.lang.Integer"/>
<property name="cname" column="cname" type="java.lang.String"/>
class>
hibernate-mapping>
<hibernate-mapping>
<class name="org.example.Entity.PupilCourse" table="pupil_course">
<id name="id" column="id" type="java.lang.Integer"/>
<property name="aid" column="aid" type="java.lang.Integer"/>
<property name="cid" column="cid" type="java.lang.Integer"/>
class>
hibernate-mapping>
@Test
public void test8(){
//创建SessionFactory,从根路径下获取核心配置文件
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
//创建session
Session session = factory.openSession();
//创建事务
Transaction transaction = session.beginTransaction();
//获取主键ID为0的学生信息及其选课信息
Pupil pupil = session.get(Pupil.class,0);
//输出结果
System.out.println(pupil.toString());
//提交事务
transaction.commit();
//关闭SessionFactory
factory.close();
}
例如:
Pupil<===>Course
一个学生(Pupil)有多门课程(Course),一门课程有多个学生选择。
当业务要求通过Pupil学生可以获取该学生的所有课程Course,且通过Course课程能获取对应Pupil学生时,这就需要多对多双向关联关系。
多对多关系通过一个中间表来维护。
易错:双向关联关系注意设置inverse和cascade属性。
pupil_course表中有两个外键分别指向pupil表和course表的主键。
Pupil类中有课程Course的集合。
import lombok.Getter;
import lombok.Setter;
import java.util.Set;
@Getter
@Setter
public class Pupil {
private int aid;
private String aname;
private Set<Course> courses;
@Override
public String toString() {
return "Pupil{" +
"aid=" + aid +
", aname='" + aname + '\'' +
", courses=" + courses +
'}';
}
}
Course类中有课程Pupil的集合。
import lombok.Getter;
import lombok.Setter;
import java.util.Set;
@Getter
@Setter
public class Course {
private int cid;
private String cname;
private Set<Pupil> pupils;
}
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class PupilCourse {
private int id;
private Integer aid;
private Integer cid;
}
<hibernate-mapping>
<class name="org.example.Entity.Pupil" table="pupil">
<id name="aid" column="aid" type="java.lang.Integer"/>
<property name="aname" column="aname" type="java.lang.String"/>
<set name="courses" table="pupil_course">
<key column="aid"/>
<many-to-many class="org.example.Entity.Course" column="cid"/>
set>
class>
hibernate-mapping>
<hibernate-mapping>
<class name="org.example.Entity.Course" table="course">
<id name="cid" column="cid" type="java.lang.Integer"/>
<property name="cname" column="cname" type="java.lang.String"/>
<set name="pupils" table="pupil_course">
<key column="cid">key>
<many-to-many class="org.example.Entity.Pupil" column="aid">many-to-many>
set>
class>
hibernate-mapping>
<hibernate-mapping>
<class name="org.example.Entity.PupilCourse" table="pupil_course">
<id name="id" column="id" type="java.lang.Integer"/>
<property name="aid" column="aid" type="java.lang.Integer"/>
<property name="cid" column="cid" type="java.lang.Integer"/>
class>
hibernate-mapping>
@Test
public void test8(){
//创建SessionFactory,从根路径下获取核心配置文件
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
//创建session
Session session = factory.openSession();
//创建事务
Transaction transaction = session.beginTransaction();
//获取主键ID为0的课程信息及课程内的学生
Course course = session.get(Course.class,0);
//输出结果
for (Pupil pupil : course.getPupils()) {
System.out.println(pupil.getAname());
}
//提交事务
transaction.commit();
//关闭SessionFactory
factory.close();
}
@Test
public void test2(){
//创建SessionFactory,从根路径下获取核心配置文件
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
//创建session
Session session = factory.openSession();
//创建事务
Transaction transaction = session.beginTransaction();
//创建Courses类对象并给相应属性赋值
CoursesEntity course = new CoursesEntity();
course.setCname("JAVA");
//创建Accounts类对象并给相应属性赋值
AccountsEntity account = new AccountsEntity();
account.setAname("张三");
//建立关联关系
Set<CoursesEntity> courses = new HashSet<>();
courses.add(course);
account.setCourses(courses);
//保存,所有对象都要保存
session.save(course);
session.save(account);
//提交事务
transaction.commit();
//关闭SessionFactory
factory.close();
}
}
一对一单向关联没有测出来,所以没写,有会实现的的欢迎与我交流。
例如:
Person<——>Address
一个人(Person)对应一个家(Address)。
当业务要求通过Person可以获取他的家庭住址Address,且能通过Address找到Person时,这就需要一对一双向关联关系。
一对一双向关联关系的实现是通过在Person类中创建Address类作为属性,并在Address类中创建Person类作为属性
Person类中要有地址Address类作为属性。
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Person {
private int personId;
private String personName;
private String personPhone;
//Person类中要有地址Address类
private Address address;
@Override
public String toString() {
return "Person{" +
"personId=" + personId +
", personName='" + personName + '\'' +
", personPhone='" + personPhone + '\'' +
", address=" + address +
'}';
}
}
Address类中要有Person类作为属性。
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Address {
private int addressId;
private String addressDetail;
//Address类中要有地址Person类
private Person person;
@Override
public String toString() {
return "Address{" +
"addressId=" + addressId +
", addressDetail='" + addressDetail + '\'' +
'}';
}
}
<hibernate-mapping>
<class name="org.example.Entity1.Person" table="person">
<id name="personId" column="person_id"/>
<property name="personName" column="person_name"/>
<property name="personPhone" column="person_phone"/>
<one-to-one name="address" class="org.example.Entity1.Address" property-ref="person" />
class>
hibernate-mapping>
<hibernate-mapping>
<class name="org.example.Entity1.Address" table="address">
<id name="addressId" column="address_id"/>
<property name="addressDetail" column="address_detail"/>
<many-to-one name="person" column="personid" unique="true" class="org.example.Entity1.Person"/>
class>
hibernate-mapping>
@Test
public void test1(){
//创建SessionFactory,从根路径下获取核心配置文件
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
//创建session
Session session = factory.openSession();
//创建事务
Transaction transaction = session.beginTransaction();
//创建实体类对象并给相应属性赋值
Person person = session.get(Person.class,1);
System.out.println(person);
//提交事务
transaction.commit();
//关闭SessionFactory
factory.close();
}
懒加载条件下查询顾客信息和订单信息。
具体配置同上。
需要在customer类的映射文件中为order的set集合配置 lazy="true"
,设置order的懒加载,这样在查询customer信息时会根据需求判断是否查询order。
需要在order类的映射文件中为customer的many-to-one设置lazy=proxy"
,设置customer的懒加载,这样在查询order信息时会根据需求判断是否查询customer。
默认条件下所有Hibernate映射文件都会开启懒加载。
@Test
public void test3(){
//创建SessionFactory,从根路径下获取核心配置文件
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
//创建session
Session session = factory.openSession();
//创建事务
Transaction transaction = session.beginTransaction();
//查询主键为4的顾客信息
CustomerEntity customer = session.get(CustomerEntity.class,4);
System.out.println(customer);
//提交事务
transaction.commit();
//关闭SessionFactory
factory.close();
}
因为会调用toString方法,toString会打印学生信息和订单信息,所以有两条SQL语句。
如果只打印customer.name(),则只会有第一条SQL,这就是懒加载。
@Test
public void test4(){
//创建SessionFactory,从根路径下获取核心配置文件
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
//创建session
Session session = factory.openSession();
//创建事务
Transaction transaction = session.beginTransaction();
//查询主键为4的订单信息
OrdersEntity orders = session.get(OrdersEntity.class,4);
System.out.println(orders);
//提交事务
transaction.commit();
//关闭SessionFactory
factory.close();
}