Hibernate的关联映射——单向N-1关联

Hibernate的关联映射——单向N-1关联

  N-1是非常常见的关联关系,最常见的父子关系也是N-1关联,单向的N-1关联只需从N的一端可以访问1的一端。

  为了让两个持久化类能够支持这种关联映射,程序应该在N的一端的持久化类中增加一个属性,该属性引用1的一端的关联实体。

  对于N-1关联(不论是单向关联还是双向关联),都需要在N的一端使用@ManyToOne修饰代表关联实体的属性。

1.无连接表的N-1关联

  对于无连接表的N-1关联而言,程序只要在N的一端增加一列外键,让外键值记录该对象所属的实体即可,Hibernate可以使用@JoinColumn来修饰代表关联实体的属性,@JoinColumn用于映射底层的外键列。

  直接使用@JoinColumn注解来映射N-1关联时,Hibernate将无需使用连接表,直接使用外键关联策略来处理这种关联映射。

(1)N的一端,不同的Person实体可以对应同一个Address实体

package com.mytest.test1;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;

/**
 * Person类中增加了一个Address类型的属性,引用关联的Address实体。
 * 为了让Hibernate理解该Address类型的属性是关联实体,程序需要使用@ManyToOne、@JoinColumn修饰该属性
* 除此之外,还使用了@Cascade注解,该注解用于指定级联操作策略,此处的级联策略是CascadeType.ALL——这表明对Person实体的所有持久化操作都会
 * 级联到它关联的Address实体 */ @Entity //标识这是一个持久化类 @Table(name="person1") //该持久化类对应表为person1 public class Person { @Id @Column(name="p_id") //设置为主键 此变量对应表中的p_id字段 @GeneratedValue(strategy=GenerationType.IDENTITY) //设置主键生成策略为自增长 private Integer id; @Column(name="p_name") //此变量对应表中的p_name字段 private String name; @Column(name="p_age") private Integer age; //此变量对应表中的p_age字段 //定义该Person实体关联的Address实体 @ManyToOne(targetEntity=Address.class) //映射外键列,指定外键列的列名为address_id,不允许为空 @JoinColumn(name="address_id",nullable=false) @Cascade(CascadeType.ALL) private Address address; 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 Integer getAge() { return age; } public void setAge(int age) { this.age = age; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } }

(2)1的一端,同一个Address可以被多个Person对应

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * 程序无需从Address访问Person,所以Address无须增加Person属性
 */
@Entity    //指定该类为一个持久化类
@Table(name="add1")    //对应的表名为add1
public class Address {
    @Id @Column(name="address_id")    //设置此属性为主键 对应表中的address_id字段
    @GeneratedValue(strategy=GenerationType.IDENTITY)    //设置主键的生成策略为自增长
    private Integer addressId;
    @Column(name="address_detail")    //设置此属性对应表中的address_detail字段
    private String addressDetail;
    //无参数的构造器
    public Address(){

    }
    //初始化所有成员变量的构造器
    public Address(String addressDetail){
        this.addressDetail = addressDetail;
    }
    public Integer getAddressId() {
        return addressId;
    }
    public void setAddressId(Integer addressId) {
        this.addressId = addressId;
    }
    public String getAddressDetail() {
        return addressDetail;
    }
    public void setAddressDetail(String addressDetail) {
        this.addressDetail = addressDetail;
    }
}

(3)测试

import org.hibernate.Session;
import org.hibernate.Transaction;

public class Test1 {
    public static void main(String[] args) {
        Session session = HibernateUtil.currentSession();
        Transaction tx = session.beginTransaction();
        //创建一个Person对象
        Person person = new Person();
        //创建一个瞬态的Address对象
        Address a = new Address("addressA");
        //设置Person的各个属性
        person.setName("liujiang");
        person.setAge(24);
        //设置Persion和Address之间的关联关系
        person.setAddress(a);
        //持久化Person对象
        session.persist(person);
        //创建一个瞬态的Address
        Address b = new Address("addressB");
        //修改持久化状态的Person对象
        person.setAddress(b);
        tx.commit();
        HibernateUtil.closeSession();
    }
}

2.有连接表的N-1关联

  对于绝大部分单向N-1关联而言,使用基于外键关联的映射已经足够了。但由于底层数据库建模时也可以使用连接表来建立这种关联关系,因此Hibernate也为这种关联关系提供了支持。

  如果需要使用连接表来映射单向N-1关联,程序需要显式使用@JoinTable注解来映射连接表。

  @JoinTable专门用于映射底层连接表的信息。

多个Person实体可以通过连接表对应同一个Address实体 Address持久化类不变,再写一个Person1类,作为N一端的持久化类。

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Table;

/**
 *    有连接表的单向N-1
 *    Person为N的一端,多个Person实例可以对应同一个Address
 *    除了使用@ManyToOne以外修饰代表关联实体的Address以外
 *    还需要用@JoinTable注解显式的指定连接表
 *    由于此映射N-1关联,因此程序还未该@JoinColumn增加了unique=true
 */
@Entity    //指定该类是一个持久化类
@Table(name="person1")    //指定该持久化类对应person1数据表
public class Person1 {
    @Id    @Column(name="p_id")    //指定该属性为主键字段,对应数据表中的p_id字段
    @GeneratedValue(strategy=GenerationType.IDENTITY)    //设置主键的生成策略 这里为自增长
    private Integer id;    //id
    @Column(name="p_name")
    private String name;    //name
    @Column(name="age")
    private Integer age;    //age
    //显式使用@JoinTable映射连接表
    @JoinTable(
            name="person1_address",    //    指定连接表的表名为“person_address”
            //指定连接表中person_id外键列,参照到当前实体对应表的主键列
            joinColumns=@JoinColumn(
                    name="p_id",
                    referencedColumnName="p_id",
                    unique=true
                    ),
            //指定连接表中address_id为外键列,参照到当前实体的关联实体对应表的主键列
            inverseJoinColumns=@JoinColumn(
                    name="address_id",
                    referencedColumnName="address_id"
                    )
            )
    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 Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }

}

  在这种映射策略下,p_id是person1表的主键,同时也是person1_address表的主键,这保证了person1_address数据表中的p_id列不能出现重复值,也就保证了一个Person1实体最多只能关联一个Address实体。

  对于使用连接表的N-1关联而言,由于两个实体对应的数据表都无需增加外键列,因此两个实体对应的数据表不存在主、从关系,程序完全可以想先持久化那个实体,就先持久化那个实体。无论顺序如何,程序都不会引发性能问题。

上一篇:《BREW进阶与精通——3G移动增值业务的运营、定制与开发》连载之54---BREW SDK 九大功能之其他服务


下一篇:css知识汇总