前言
上节已经学会了Hibernate之CRUD操作了,那么有这么一个需求,当用户购买商品的时候会填写收货地址,有时候收货地址不止一个,那么数据库是如何来保存一个用户对应多个地址的呢,而使用Hibernate如何映射用户和地址的关系呢。我们来看看具体实现吧。
数据库之表结构设计
如果数据库还不是很好的同学,可能会这么设计数据库吧。
如果把用户信息和地址放在同一个表中,用户有的可能有三四个收货地址,有的可能只有一个收货地址,那么数据库将会产生大量的冗余,不符合数据库的三大范式,所以不推荐以上设计。
应当采用如下的设计:
建立用户与地址表,然后将地址表的外键设置为用户id即可。
那么数据库表建好了,如何通过Hibernate存入数据呢。之前只是存单个表的数据我们会了,现在是一对多的关系,应当使用集合映射来存储。因为集合可以存多个地址,只要在xml文件中配置映射关系即可。
案例代码
User.java
// javabean设计
public class User {
private int userId;
private String userName;
// 一个用户,对应的多个地址
private Set<String> address;
public void setAddressList(List<String> addressList) {
this.addressList = addressList;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Set<String> getAddress() {
return address;
}
public void setAddress(Set<String> address) {
this.address = address;
}
}
映射文件User.hbm.xml
<hibernate-mapping package="com.nwpu.geeker.collection">
<class name="User" table="t_user">
<id name="userId" column="id">
<generator class="native"></generator>
</id>
<property name="userName"></property>
<!--
set集合属性的映射
name 指定要映射的set集合的属性
table 集合属性要映射到的表
key 指定集合表(t_address)的外键字段
element 指定集合表的其他字段
type 元素类型,一定要指定
-->
<set name="address" table="t_address">
<key column="uid"></key>
<element column="address" type="string"></element>
</set>
</class>
</hibernate-mapping>
测试代码:APP.java
public class App {
private static SessionFactory sf;
static {
sf = new Configuration()
.configure()
.addClass(User.class) // 测试时候使用
.buildSessionFactory();
}
// 保存set
@Test
public void testSaveSet() throws Exception {
Session session = sf.openSession();
session.beginTransaction();
//-- 保存
Set<String> addressSet = new HashSet<String>();
addressSet.add("广州");
addressSet.add("深圳");
// 用户对象
User user = new User();
user.setUserName("Jack");
user.setAddress(addressSet);
// 保存
session.save(user);
session.getTransaction().commit();
session.close();
}
}
数据库保存两张表的数据成功!
当然集合映射不只有set集合可以,也可以是数组array,list,map等容器存储多的一方。
修改上面代码:
User.java
public class User {
private int userId;
private String userName;
// 一个用户,对应的多个地址
private Set<String> address;
private List<String> addressList = new ArrayList<String>();
//private String[] addressArray; // 映射方式和list一样 <array name=""></array>
private Map<String,String> addressMap = new HashMap<String, String>();
public Map<String, String> getAddressMap() {
return addressMap;
}
public void setAddressMap(Map<String, String> addressMap) {
this.addressMap = addressMap;
}
public List<String> getAddressList() {
return addressList;
}
public void setAddressList(List<String> addressList) {
this.addressList = addressList;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Set<String> getAddress() {
return address;
}
public void setAddress(Set<String> address) {
this.address = address;
}
}
配置文件
<hibernate-mapping package="com.nwpu.geeker.collection">
<class name="User" table="t_user">
<id name="userId" column="id">
<generator class="native"></generator>
</id>
<property name="userName"></property>
<!--
set集合属性的映射
name 指定要映射的set集合的属性
table 集合属性要映射到的表
key 指定集合表(t_address)的外键字段
element 指定集合表的其他字段
type 元素类型,一定要指定
-->
<set name="address" table="t_address">
<key column="uid"></key>
<element column="address" type="string"></element>
</set>
<!--
list集合映射
list-index 指定的是排序列的名称 (因为要保证list集合的有序)
-->
<list name="addressList" table="t_addressList">
<key column="uid"></key>
<list-index column="idx"></list-index>
<element column="address" type="string"></element>
</list>
<!--
map集合的映射
key 指定外键字段
map-key 指定map的key
element 指定map的value
-->
<map name="addressMap" table="t_addressMap">
<key column="uid"></key>
<map-key column="shortName" type="string" ></map-key>
<element column="address" type="string" ></element>
</map>
</class>
</hibernate-mapping>
App.java
public class App {
private static SessionFactory sf;
static {
sf = new Configuration()
.configure()
.addClass(User.class) // 测试时候使用
.buildSessionFactory();
}
// 保存set
@Test
public void testSaveSet() throws Exception {
Session session = sf.openSession();
session.beginTransaction();
//-- 保存
Set<String> addressSet = new HashSet<String>();
addressSet.add("广州");
addressSet.add("深圳");
// 用户对象
User user = new User();
user.setUserName("Jack");
user.setAddress(addressSet);
// 保存
session.save(user);
session.getTransaction().commit();
session.close();
}
// 保存list/map
@Test
public void testSaveList() throws Exception {
Session session = sf.openSession();
session.beginTransaction();
User user = new User();
user.setUserName("Tom");
// // 用户对象 -- list
// user.getAddressList().add("广州");
// user.getAddressList().add("深圳");
// // 保存
// session.save(user);
// 用户对象 -- Map
user.getAddressMap().put("A0001", "广州");
user.getAddressMap().put("A0002", "深圳");
// 保存
session.save(user);
session.getTransaction().commit();
session.close();
}
}
注意:set保存的数据是无序的,数组和list是有序的,但是只能保存一个字符串,也就是只能保存一个值,map强一点能保存两个值,不过,当我们需要保存多个数据,比如一个对象的时候,就需要将对象保存在集合中了,而不是简单的只保存一个地址。本文到这里就结束了,后续将把如何保存对象,即一对多,多方保存对象的案例总结出来!