首先按照【SSH (七)】 struts2整合hibernate3 搭建一个SSH的空框架出来。
下面说一下hibernate的多对一映射。
数据库实体之间通常会存在关联,其中一种就是多对一关联,比如用户User和邮件地址Address两个实体,一个用户会有多个邮件地址,而一个邮件地址只会属于一个用户,也就是说用户会对应多个地址。在这里,用户就是“一”的一方,而地址是“多”的一方。关于多和一的区别,最好是画一个图出来,那么用户会有很多箭头指向不同的地址,用户就是一,地址就是多。
多对一关联进一步可分为单向和双向,先看单向。
1,单向多对一:在数据库建表层面,肯定是两张表一个是User一个是Address,然后Address里面存一个指向User的外键,数据库的表结构肯定是这样的。
然而实体的映射文件却有两种方式,为什么这里会有两种方式呢?这涉及到一个维护映射关系的概念,这个上升到了面向对象层面。比如我这里要为user1添加一个地址,我可以在User类中加一个set,然后set.add(address),也可以在address类中添加一个user的引用,然后就是address.setuser(user),前者是由user来维护关联,后者是address来维护关联,下面具体结合程序来说明:
首先建立数据库:刚才说了,不管采用什么关联方式,数据库底层的表是不变的,关联关系是应用层的话题:
user表:只需要有user自己的信息即可。
address表:不仅要有自己的信息,还有外键。id,address是address表本身的信息字段,userId是外键字段。
(1)由User来维护关联:
User实体类:需要添加一个集合存储拥有的地址。
package com.bean;
import java.util.HashSet;
import java.util.Set;
public class User {
private int id;
private String username;
private String password;
private String info;
private Set<Address> addresses = new HashSet<>();
public User(){
}
public User(String username,String password,String info){
this.username = username;
this.password = password;
this.info = info;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String toString(){
return this.username+" "+this.password+" "+this.info;
}
public Set<Address> getAddresses() {
return addresses;
}
public void setAddresses(Set<Address> addresses) {
this.addresses = addresses;
}
}
映射文件:
<?xml version="1.0" encoding='GBK'?>
<!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.bean">
<class name="User" table="[user]">
<id name="id" column="id">
<generator class="identity"></generator>
</id>
<property name="username" column="username" type="java.lang.String"
length="16"></property>
<property name="password" column="password" type="java.lang.String"
length="16"></property>
<property name="info" column="info" type="java.lang.String"
length="16"></property>
<set name="addresses" cascade="all">
<key column="userId" ></key>
<one-to-many class="com.bean.Address"></one-to-many>
</set>
</class>
</hibernate-mapping>
<set>就是对集合属性的映射,<key>表示外键的名称,cascade级联设置记得。
Address实体类:既然让User实体类维护关联,那么Address类就只要包含自己本身的信息就可以,好像并不知道关联这回事。
package com.bean;
public class Address {
private int id;
private String address;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
映射文件:
<?xml version="1.0" encoding='GBK'?>
<!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.bean">
<class name="Address" table="address">
<id name="id" column="id">
<generator class="identity"></generator>
</id>
<property name="address" column="address" type="java.lang.String"
length="16"></property>
</class>
</hibernate-mapping>
Address address1 = new Address();
address1.setAddress("xianlin");
Address address2 = new Address();
address2.setAddress("zhujianglu");
User user1 = new User();
user1.setUsername("hehehehe");
//因为由user来维护关联关系,所以,这里要让user自己添加address实例
user1.getAddresses().add(address1);
user1.getAddresses().add(address2);
session.save(user1);
transaction.commit();
session.close();
sessionFactory.close();
运行结果:
hibernate语句:
(2)由address类维护关联:
User实体类:不维护关联,所以都是自己本身的字段。
public class User{
private int id;
private String username;
private String password;
private String info;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
配置文件:
<class name="User" table="[user]">
<id name="id" column="id">
<generator class="identity"></generator>
</id>
<property name="username" column="username" type="java.lang.String"
length="16"></property>
<property name="password" column="password" type="java.lang.String"
length="16"></property>
<property name="info" column="info" type="java.lang.String"
length="16"></property>
</class>
Address实体类:
public class Address {
private int id;
private String address;
private User user;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
配置文件:
<class name="Address" table="address">
<id name="id" column="id">
<generator class="identity"></generator>
</id>
<property name="address" column="address" type="java.lang.String"
length="16"></property>
<many-to-one name="user" column="userId" cascade="all"></many-to-one>
</class>
应用层调用方法:
User user1 = new User();
user1.setUsername("hehehehe");
Address address1 = new Address();
address1.setAddress("xianlin");
//因为由address自己维护,所以address自己设置user
address1.setUser(user1);
Address address2 = new Address();
address2.setAddress("zhujianglu");
address2.setUser(user1);
session.save(address1);
session.save(address2);
运行结果:
总结:
对于单向的多对一关联,画图确定哪一方是多哪一方是1。
多对一对于数据库而言只有一种实现方式,就是在多的一方添加外键,至于关联的维护方这是应用层层面要考虑的问题,只是应用层存储关联关系时的方式不同而已。这也是因为hibernate ORM的原因,hibernate是将原生的sql api封装起来,提供了面向对象的访问方式,这才导致了有两种方式的多对一,如果不用ORM,用原生的sql,我们就不会遇到这样的问题了。
两种方式也被称为多对一和一对多。
项目源码:http://pan.baidu.com/s/1c0LReLE
至于双向,就是二者的结合了,配置文件一个加one-to-many另一个加many-to-one,实体类一个加set一个加引用。
更加详细的类图和对比可以参照http://blog.youkuaiyun.com/yanmei_yao/article/details/7596163