Hibernate4关联映射基础-生成的表结构
程序员文章站
2022-04-19 11:06:22
...
Hibernate关联映射是Hibernate中比较难用好知识点, 一方面两个实体之间的关系类型比较多, 有单向映射、双向映射; 还有ManyToOne, OneToOne, OneToMany, ManyToMany, 这样算下来会有2*4=8种, 通过不同逻辑映射产生的物理表也比较多, 当然其中一部分是类似的; 复杂的另一方面体现在对关联实体的操作可能对被关联实体产生的影响上面, 包括级联属性(CASCADE)、抓取属性(FETCH)等, 如果配置不好会适得其反. 因此在这里我们通过逐个实现每一种关联大致了解关联映射的概念和基本使用.
实体类: Person和Address, 在不同场景下两个实体的关系不同.
Hibernate实现:Hibernate4 + JPA, 基于注解实现.
Hibernate实现:Hibernate4 + JPA, 基于注解实现.
包的组织形式如下图所示, 其中Tester是测试类, hibernate.cfg.xml是配置文件.
hibernate配置文件如下, 后续的用于测试不同关联映射的包使用相同的配置, 只是映射的实体类不同而已.
<?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-configuration> <session-factory> <!-- Database connection settings --> <property name="connection.driver_class">com.ibm.db2.jcc.DB2Driver</property> <property name="connection.url">jdbc:db2://127.0.0.1:60003/HIBER</property> <property name="connection.username">*****</property> <property name="connection.password">******</property> <!-- JDBC connection pool (use the built-in) --> <property name="connection.pool_size">3</property> <!-- SQL dialect --> <property name="dialect">org.hibernate.dialect.DB2Dialect</property> <!-- Enable Hibernate's automatic session context management --> <property name="current_session_context_class">thread</property> <!-- Disable the second-level cache --> <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property> <!-- Echo all executed SQL to stdout --> <property name="show_sql">true</property> <!-- Drop and re-create the database schema on startup --> <property name="hbm2ddl.auto">update</property> <!-- <mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml" /> --> <mapping class="azure.hibernate4.associations.unidi.many2one.Person" /> <mapping class="azure.hibernate4.associations.unidi.many2one.Address" /> </session-factory> </hibernate-configuration>
在Hibernate4中创建SessionFactory的方式与Hibernate3有一些不一样, 但这里不是讨论的重点, 一下几个包中的Tester.java使用相同的取得SessionFactory方式, 代码如下:
public SessionFactory sessionFactory; public Session getCurrentSession() { String filePath = this.getClass().getPackage().getName() .replaceAll("\\.", "/"); if (sessionFactory == null) { Configuration cfg = new Configuration().configure(Thread .currentThread().getContextClassLoader() .getResource(filePath + "/hibernate.cfg.xml")); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder() .applySettings(cfg.getProperties()).buildServiceRegistry(); sessionFactory = cfg.buildSessionFactory(serviceRegistry); } return sessionFactory.getCurrentSession(); }
下面我们一一测试每一种关联映射。
1. 单向关联
1.1 单向ManyToOne关联
//Person.java package azure.hibernate4.associations.unidimany2one; import java.io.Serializable; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; @Entity @Table(name = "PERSON") public class Person implements Serializable { private static final long serialVersionUID = -1097173835933422124L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int personId; @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "ADDRESS_ID") private Address address; public int getPersonId() { return personId; } public void setPersonId(int personId) { this.personId = personId; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } }
//Address.java package azure.hibernate4.associations.unidimany2one; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "ADDRESS") public class Address implements Serializable { private static final long serialVersionUID = 3794514024483047170L; @Id @Column(name = "ADDRESS_ID") private int addressId; @Column(name = "ADDRESS_NAME") private String name; public int getAddressId() { return addressId; } public void setAddressId(int addressId) { this.addressId = addressId; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
由Hibernate生成的表物理视图如下, 在ManyToOne关系中是在主表中生成一个外键.
1.2 单向OneToOne关联
映射部分代码Person.java
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int personId; @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "ADDRESS_ID") private Address address;
Address.java
@Id @Column(name = "ADDRESS_ID") private int addressId; @Column(name = "ADDRESS_NAME") private String name;
生成的建表SQL语句如下:
Hibernate: create table ADDRESS (ADDRESS_ID integer not null, ADDRESS_NAME varchar(255), primary key (ADDRESS_ID)) Hibernate: create table PERSON (personId integer generated by default as identity, ADDRESS_ID integer, primary key (personId)) Hibernate: alter table PERSON add constraint FK8C768F5591A4FE5E foreign key (ADDRESS_ID) references ADDRESS
生成的表的物理视图如下:
1.3 单向OneToMany
关联部分代码Person.java
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int personId; @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) private Set<Address> address;
Address.java
@Id @Column(name = "ADDRESS_ID") private int addressId; @Column(name = "ADDRESS_NAME") private String name;
生成的建表SQL语句如下:
Hibernate: create table ADDRESS (ADDRESS_ID integer not null, ADDRESS_NAME varchar(255), primary key (ADDRESS_ID)) Hibernate: create table PERSON (personId integer generated by default as identity, primary key (personId)) Hibernate: create table PERSON_ADDRESS (PERSON_personId integer not null, address_ADDRESS_ID integer not null, primary key (PERSON_personId, address_ADDRESS_ID)) Hibernate: alter table PERSON_ADDRESS add constraint UK_9E2338EA65485A71 unique (address_ADDRESS_ID) Hibernate: alter table PERSON_ADDRESS add constraint FK9E2338EAD006FE6A foreign key (address_ADDRESS_ID) references ADDRESS Hibernate: alter table PERSON_ADDRESS add constraint FK9E2338EA495AB8A foreign key (PERSON_personId) references PERSON
物理表视图如下, 看到Hibernate使用连接表来实现单向OneToMany, 根据Hibernate文档的说明:
写道
A unidirectional one-to-many association on a foreign key is an unusual case, and is not recommended.
1.4 单向ManyToMany
Person.java
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int personId; @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) private Set<Address> address;Address.java
@Id @Column(name = "ADDRESS_ID") private int addressId; @Column(name = "ADDRESS_NAME") private String name;
建表SQL
Hibernate: create table ADDRESS (ADDRESS_ID integer not null, ADDRESS_NAME varchar(255), primary key (ADDRESS_ID)) Hibernate: create table PERSON (personId integer generated by default as identity, primary key (personId)) Hibernate: create table PERSON_ADDRESS (PERSON_personId integer not null, address_ADDRESS_ID integer not null, primary key (PERSON_personId, address_ADDRESS_ID)) Hibernate: alter table PERSON_ADDRESS add constraint FK9E2338EADD54AFC1 foreign key (address_ADDRESS_ID) references ADDRESS Hibernate: alter table PERSON_ADDRESS add constraint FK9E2338EABAB0F353 foreign key (PERSON_personId) references PERSON物理表
2. 双向关联
2.1 双向ManyToMany
Person.java
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int personId; @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) private Set<Address> address;Address.java
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int personId; @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) private Set<Address> address;建表SQL
Hibernate: create table ADDRESS (ADDRESS_ID integer not null, ADDRESS_NAME varchar(255), primary key (ADDRESS_ID)) Hibernate: create table ADDRESS_PERSON (ADDRESS_ADDRESS_ID integer not null, person_personId integer not null, primary key (ADDRESS_ADDRESS_ID, person_personId)) Hibernate: create table PERSON (personId integer generated by default as identity, primary key (personId)) Hibernate: create table PERSON_ADDRESS (PERSON_personId integer not null, address_ADDRESS_ID integer not null, primary key (PERSON_personId, address_ADDRESS_ID)) Hibernate: alter table ADDRESS_PERSON add constraint FK3672AE20593E71E foreign key (person_personId) references PERSON Hibernate: alter table ADDRESS_PERSON add constraint FK3672AE20EED03556 foreign key (ADDRESS_ADDRESS_ID) references ADDRESS Hibernate: alter table PERSON_ADDRESS add constraint FK9E2338EAEED03556 foreign key (address_ADDRESS_ID) references ADDRESS Hibernate: alter table PERSON_ADDRESS add constraint FK9E2338EA593E71E foreign key (PERSON_personId) references PERSON物理表视图如下
很遗憾, 这并不是我们所期望的的表结构. 对于双向ManyToMany, 使用一个中间表就够了, 因此我们需要对实体类的配置作如下修改
Person.java
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int personId; @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinTable(name = "PERSON_ADDRESS", joinColumns = { @JoinColumn(name = "ADDRESS_ID") }, inverseJoinColumns = { @JoinColumn(name = "PERSON_ID") }) private Set<Address> address;Address.java
@Id @Column(name = "ADDRESS_ID") private int addressId; @Column(name = "ADDRESS_NAME") private String name; @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinTable(name = "PERSON_ADDRESS", joinColumns = { @JoinColumn(name = "PERSON_ID") }, inverseJoinColumns = { @JoinColumn(name = "ADDRESS_ID") }) private Set<Person> person;这时的建表SQL语句如下:
Hibernate: create table ADDRESS (ADDRESS_ID integer not null, ADDRESS_NAME varchar(255), primary key (ADDRESS_ID)) Hibernate: create table PERSON (personId integer generated by default as identity, primary key (personId)) Hibernate: create table PERSON_ADDRESS (PERSON_ID integer not null, ADDRESS_ID integer not null, primary key (ADDRESS_ID, PERSON_ID)) Hibernate: alter table PERSON_ADDRESS add constraint FK9E2338EA97561B8A foreign key (ADDRESS_ID) references PERSON Hibernate: alter table PERSON_ADDRESS add constraint FK9E2338EABC62752A foreign key (PERSON_ID) references ADDRESS
物理表如下:
2.2 双向ManyToOne关联
双向ManyToOne关联, 在1的一端需要有到N的一段的集合字段, 在N的一端需要到1的一端的字段. 在双向一对多关联中建议不用连接表, 而直接在多的一端加外键.
Person.java
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int personId; @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "ADDRESS_ID") private Address address;
Address.java
@Id @Column(name = "ADDRESS_ID") private int addressId; @Column(name = "ADDRESS_NAME") private String name; @OneToMany(mappedBy = "address", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private Set<Person> persons;
建表SQL如下:
Hibernate: create table ADDRESS (ADDRESS_ID integer not null, ADDRESS_NAME varchar(255), primary key (ADDRESS_ID)) 2013-5-22 16:42:45 org.hibernate.tool.hbm2ddl.SchemaExport perform Hibernate: create table PERSON (personId integer generated by default as identity, ADDRESS_ID integer, primary key (personId)) Hibernate: alter table PERSON add constraint FK8C768F5592A339F2 foreign key (ADDRESS_ID) references ADDRESS
物理表结构:
2.3 双向OneToOne关联
通过双向OneToOne关联可以从任意一端导航到另一端, 在两个实体类里面需要同时定义到另一端的属性. 物理表结构方面, 在任意一端建立一个外键.
Person.java
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int personId; @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) private Address address;
Address.java
@Id @Column(name = "ADDRESS_ID") private int addressId; @Column(name = "ADDRESS_NAME") private String name; @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER) private Person person;
建表语句如下:
Hibernate: create table ADDRESS (ADDRESS_ID integer not null, ADDRESS_NAME varchar(255), person_personId integer, primary key (ADDRESS_ID)) Hibernate: create table PERSON (personId integer generated by default as identity, address_ADDRESS_ID integer, primary key (personId)) Hibernate: alter table ADDRESS add constraint FKE66327D4A65E7D16 foreign key (person_personId) references PERSON Hibernate: alter table PERSON add constraint FK8C768F5567585E5E foreign key (address_ADDRESS_ID) references ADDRESS
物理表结构如下:
可以看到Hibernate同时在两张表上都添加了外键.