通常在Hibernate或JPA中,配置集合多涉及到关联关系的配置,(当泛型为关联对象的类时)
常见的如:
Set List 存在的类中(泛型T不为基础数据类型的包装类)
----------这意味着需要配置一对多或多对多的关联
大多数的情况下,我们能在一对一,一对多,多对多的关系中满足需求,
但有时候则必须配置Map,或Set<基础数据类型>,List<基础数据类型>的映射
所以在此记录一下,以供不时之需
1.Hibernate配置基础类型Set,List
这里自动忽略Set<非基础类型的配置>,因为这些是关联关系配置的范畴,
请参考Hibernate关联配置详解测试类
public class User {
private Integer ID;
private String UserName;
private Set<String> NickNameSet;
//省略了getter&setter
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="HibernateTest.User" table="t_user" lazy="true">
<id name="ID" column="user_id">
<generator class="native"/>
</id>
<property name="UserName" type="string" />
<set name="NickNameSet" table="t_user_nicknames">
<!--本质上是建了另一张表,是一种一对多的关系-->
<!--设置外键名,这个外键会参照上面的主键user_id-->
<key column="userid" ></key>
<!--设置set的元素类型,和列名-->
<element type="string" column="nickname" ></element>
</set>
</class>
</hibernate-mapping>
测试运行:
public class TestMain {
/**
* @param args
*/
public static void main(String[] args) {
Configuration config = new Configuration();
config.configure();
config.addClass(User.class);
SessionFactory factory = config.buildSessionFactory();
Session session = factory.openSession();
session.beginTransaction();
User user = new User();
user.setUserName("黃建雄傻逼");
user.getNickNameSet().add("傻逼");
user.getNickNameSet().add("廢物");
user.getNickNameSet().add("壓力怪");
session.save(user);
session.getTransaction().commit();
session.close();
}
}
生成表的代码:
Hibernate:
create table t_user (
user_id int identity not null,
UserName varchar(255),
primary key (user_id)
)
Hibernate:
create table t_user_nicknames (
userid int not null,
nickname varchar(255)
)
Hibernate:
--在自动生成的表中添加了外键关联
alter table t_user_nicknames
add constraint FKbfndcewgpmvw7xculx6be6le0
foreign key (userid)
references t_user
插入的SQL语句:
Hibernate:
insert
into
t_user
(UserName)
values
(?)
Hibernate:
insert
into
t_user_nicknames
(userid, nickname)
values
(?, ?)
Hibernate:
insert
into
t_user_nicknames
(userid, nickname)
values
(?, ?)
Hibernate:
insert
into
t_user_nicknames
(userid, nickname)
values
(?, ?)
List也是同理
只需要稍微修改一下
public class User {
private Integer ID;
private String UserName;
private Set<String> NickNameSet;
private List<String> AddressList;
//省略了getter&setter
}
在hbm.xml中添加配置
<!-- 配置list和上面的set很像,但多了多了一个list-index标签必填---它用来记录list中元素在list中的位置(index) -->
<list name="AddressList" table="t_user_address">
<key column="userid"></key>
<list-index column="list_index"></list-index>
<element type="string" column="address" ></element>
</list>
测试中添加:
user.getAddressList().add("江西南昌");
user.getAddressList().add("抚州南城");
结果:
2.Hibernate配置Map
因为Map的Key,和Value为两种泛型,
所以映射Map类型比上面复杂
上述的配置归根结底还是配置特殊的一对多的关系,
但配置Map会根据key,Value的泛型改变关联
简单来说就是
Key,Value泛型决定如何配置Map,
一.Key,Value皆为基础数据类型(包装类)
这是Map映射最简单的情况
同样可以被看做一种特殊的一对多
即一个对象------------多个键值对的一对多的关系
继续上面的用例:
添加map属性
private Map<String, Double> Scores;
//省略getter&setter
配置
<map name="Scores" table="t_user_scores">
<key column="userid"></key>
<map-key column="Subject" type="string" ></map-key>
<element column="Score" type="double"></element>
</map>
测试(先把表删了再重新运行):
public class TestMain {
public static void main(String[] args) {
Configuration config = new Configuration();
config.configure();
config.addClass(User.class);
SessionFactory factory = config.buildSessionFactory();
Session session = factory.openSession();
session.beginTransaction();
User user = new User();
user.setUserName("黃建雄傻逼");
user.getNickNameSet().add("傻逼");
user.getNickNameSet().add("廢物");
user.getNickNameSet().add("壓力怪");
user.getAddressList().add("江西南昌");
user.getAddressList().add("抚州南城");
user.getScores().put("语文", 59.9);
user.getScores().put("数学", 59.5);
user.getScores().put("英语",48.9);
session.save(user);
session.getTransaction().commit();
session.close();
}
}
载看看建表语句
Hibernate:
create table t_user_scores (
userid int not null,
Subject varchar(255) not null,
Score double precision,
primary key (userid, Subject)
)
也成功将map记录插入到表中
Hibernate:
insert
into
t_user_scores
(userid, Subject, Score)
values
(?, ?, ?)
Hibernate:
insert
into
t_user_scores
(userid, Subject, Score)
values
(?, ?, ?)
Hibernate:
insert
into
t_user_scores
(userid, Subject, Score)
values
(?, ?, ?)
二.Key为基础数据类型,Value为引用数据类型
例如:
Map<String,User> map;
这种方式稍微的复杂一些,但任然可以看做是一种特殊的一对多(前提是引用类型中不存在双向关联)
即可以这样理解
Map<String,User> map == Set<User+String属性> set;
所以这就引生出了他的配置方式
首先,我们先添加一个普通的类作为测试的工具
public class Pet {
private Integer ID;
private String Name;
//省略Getter&Setter
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="HibernateTest.Pet" table="t_pet" lazy="true">
<id name="ID" column="Pet_id">
<generator class="native"/>
</id>
<property name="Name"></property>
</class>
</hibernate-mapping>
然后,修改配置添加到上面的例子里去
<!-- Map<String,Object>的配置方法,key设置管理类表的外键,使用one-to-many直接配置Value类型 -->
<map name="Pets" table="t_pet" cascade="all">
<!-- 注意:table属性的表名一定要和,该类配置文件配置的表名相同,即Pet的表为t_pet,所以这里的table属性也要为t_pet -->
<key column="user_pet_id"></key>
<map-key column="t_user_pet" type="string"></map-key>
<one-to-many class="HibernateTest.Pet"/>
</map>
添加该属性到测试类中
public class User {
private Integer ID;
private String UserName;
private Set<String> NickNameSet;
private List<String> AddressList;
private Map<String, Double> Scores;
//新增该属性
private Map<String,Pet> Pets;
//省略Getter&Setter
}
完整的测试代码:
public class TestMain {
public static void main(String[] args) {
Configuration config = new Configuration();
config.configure();
config.addClass(Pet.class);
config.addClass(User.class);
SessionFactory factory = config.buildSessionFactory();
Session session = factory.openSession();
session.beginTransaction();
User user = new User();
user.setUserName("黃建雄傻逼");
user.getNickNameSet().add("傻逼");
user.getNickNameSet().add("廢物");
user.getNickNameSet().add("壓力怪");
user.getAddressList().add("江西南昌");
user.getAddressList().add("抚州南城");
user.getScores().put("语文", 59.9);
user.getScores().put("数学", 59.5);
user.getScores().put("英语",48.9);
Pet p1 = new Pet();p1.setName("旺财");
Pet p2 = new Pet();p2.setName("小黑");
user.getPets().put("Cat", p2);
user.getPets().put("dog", p1);
session.save(user);
session.getTransaction().commit();
session.close();
}
}
--维护User和Pet关系发出的SQL 语句
Hibernate:
update
t_pet
set
user_pet_id=?,
t_user_pet=?
where
Pet_id=?
Hibernate:
update
t_pet
set
user_pet_id=?,
t_user_pet=?
where
Pet_id=?
注意:
上面的用例设计没有什么意义,我们直接把Key属性设计到Pet里就行了,为什么要多此一举配置一个Map呢
所以正确的设计应该避免在关系映射中使用Map集合
三.Key和Value皆为引用数据类型
当数据Key和Value的数据类型为引用类型式时,
Hibernate配置文件也提供了
的标签,
但笔者查了很多资料都没有看到如何在这种情况进行配置
这里分享一个StackOverFlow上的该问题的回答
https://stackoverflow.com/questions/12296356/hibernate-mapping-of-map-where-key-is-a-part-of-complex-value 所以总结一下,如果能不使用Map形式配置就不使用,因为这样会让实体关系变复杂,
好的设计应该去避免复杂不清楚的关系