Hibernate 单向N-1关联

对于N-1关联,不管是单向还是双向,都需要在N的一端使用@ManyToOne注释修饰代表关联实体的属性。
在所有基于外键约束的关联关系中,要么总先持久化主表记录对应的实体,要么设置级联操作; 否则会抛出异常。


无连接表的N-1关联

对于无连接表的N-1关联,只要在N的一端增加一个外键列,让外键列的值记录该对象所属的实体即可。使用@JoinColumn修饰关联实体的属性。
例子: 多个人可以对应一个地点。
Person类:

//无连接表的N-1
@Entity
@Table(name = "person_inf")
public class PersonSingleN1 {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    private int age;

    //定义该Person实体关联的Address实体
    @ManyToOne(targetEntity = Address.class, fetch = FetchType.LAZY)
    //映射外键列,指定外键列的名称为address_id,非空
    @JoinColumn(name = "address_id", nullable = false)
    //指定级联策略,ALL表明对Person实体的所有持久化操作都会级联到它关联的Address实体
    @Cascade(CascadeType.ALL)
    private Address address;

    //省略get、set、构造函数
    ...
}
  • @ManyToOne(targetEntity = Address.class, fetch = FetchType.LAZY):指定关联的实体类型,延迟加载。
  • @JoinColumn(name = "address_id", nullable = false): 指定指向关联实体的外键列的名称。
  • @Cascade(CascadeType.ALL): 指定加载策略。

Address类:

@Entity
@Table(name = "address_inf")
public class Address {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int addressId;
    private String addressDetail;
    //省略get、set、构造函数
    ...
}
  • Address实体是一个普通的持久化类,不需要关心Person类。

测试代码:
数据表中原已经存在一行Address数据,一行Person数据。

            session.beginTransaction();
            Address address = new Address("浙江杭州");
            PersonSingleN1 personSingleN1 = new PersonSingleN1();
            personSingleN1.setName("sweat");
            personSingleN1.setAge(21);
            personSingleN1.setAddress(address);
            session.save(personSingleN1);
            session.getTransaction().commit();

数据库数据:

mysql> select * from person_inf;
+----+-----+-------+------------+
| id | age | name  | address_id |
+----+-----+-------+------------+
|  1 |  21 | sweat |          1 |
+----+-----+-------+------------+
1 row in set (0.00 sec)

mysql> select * from address_inf;
+-----------+---------------+
| addressId | addressDetail |
+-----------+---------------+
|         1 | 浙江杭州      |
+-----------+---------------+
1 row in set (0.00 sec)

可以看到Person列中多了一列指向关联实体的外键列。

这里最需要注意的就是Person实体类中的@Cascade(CascadeType.ALL)注解。该注解修饰关联实体的属性,意味着会先自动级联插入主表记录,就是会先持久化Address对象,然后在持久化Person对象。如果没有该注解, 按照测试代码,会抛出TransientPropertyValueException异常。 这是因为Hibernate会先尝试插入Person的数据,但是数据表中开始是没有Address的对应数据的,所以造成异常。但是使用该注解就会先插入Address数据,然后在插入Person数据。


有连接表的N-1关联

通过额外的添加一个连接表来关联实体。
先看Person类:

@Entity
@Table(name = "person_inf")
public class PersonSingleN1_2 {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    private int age;

    @ManyToOne(targetEntity = Address.class)
    @JoinTable(name = "person_address",
            joinColumns = @JoinColumn(name = "person_id", unique = true),
            inverseJoinColumns = @JoinColumn(name = "address_id",
                            referencedColumnName = "addressId"))
    private Address address;
    //省略set、get、构造函数
    ...
}
  • @ManyToOne(targetEntity = Address.class): 修饰关联的属性,表示这是多对一。
  • @JoinTable(name = "person_address", joinColumns = @JoinColumn(name = "person_id", unique = true), inverseJoinColumns = @JoinColumn(name = "address_id", referencedColumnName = "addressId")): @JoinTable注解指定使用连接表,name指定连接表的名称; joinColumns中的@JoinColumn指定关联表对应当前实体(Person)的外键列名称。inverseJoinColumns@JoinColumn指定关联表对应关联实体(Address)的外键列名称。

Address类:

@Entity
@Table(name = "address_inf")
public class Address {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int addressId;
    private String addressDetail;
    //省略get、set、构造函数
    ...
}

数据库数据:

+-------------------+
| Tables_in_servlet |
+-------------------+
| address_inf       |
| person_address    |
| person_inf        |
+-------------------+
3 rows in set (0.00 sec)

总共有3个表。

mysql> select * from person_inf;
+----+-----+-------+
| id | age | name  |
+----+-----+-------+
|  1 |  21 | sweat |
+----+-----+-------+
1 row in set (0.00 sec)

mysql> select * from person_address;
+------------+-----------+
| address_id | person_id |
+------------+-----------+
|          1 |         1 |
+------------+-----------+
1 row in set (0.00 sec)

mysql> select * from address_inf;
+-----------+---------------+
| addressId | addressDetail |
+-----------+---------------+
|         1 | 浙江杭州      |
+-----------+---------------+
1 row in set (0.00 sec)

连接表的描述:

mysql> desc person_address;
+------------+---------+------+-----+---------+-------+
| Field      | Type    | Null | Key | Default | Extra |
+------------+---------+------+-----+---------+-------+
| address_id | int(11) | YES  | MUL | NULL    |       |
| person_id  | int(11) | NO   | PRI | NULL    |       |
+------------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)

person_id是表person_address主键,同时时person_inf表的外键。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值