== Hibernate_ManytoMany_(AssociationMapping之一)
(一) 关联映射准备步骤:
1)Table加中间表,加关联字段。无需建立主外键约束。 建立<usr表>、<authorization表>、及中间表<userauth表>。
2)关联映射实现:
方案一)此时已有两个1对*,可以用【两个1对*】配置来实现。
缺点:配置复杂。连表查询时,select语句生成多; 不建议采用。
方案二)用【一个 *---*】,来直接配置实现。
特点:只写一套映射配置----<usr.hbm.xml> + Usr PO────→<authorization.hbm.xml> + Authorization PO只涉及两个PO类、两个《 .hbm.xml》文件。
加了中间表,但并不加<中间表.hbm.xml> 和 PO类;
3)先用工具,生成两个多端的单表配置;
4)两端PO添加关联属性集合
private Set auths = new HashSet();
private Set users = new HashSet();
5)再添加 <表名.hbm.xml>中的关联映射配置:
特点: 多对多 和 一对一 一样,两边配置都十分对称。
本质:可以看到多对多其实就是一对多;<set、lazy=、cascade=、inverse=、<key等都是按1对多来走的;
一对一、多对多 两边配置都十分对称。
<set name="auths" table="userauth"---中间表名
azy="true"
cascade="all"---六种
inverse="false"
<key colume="userid"/>-----表中有关联字段时,设置此项,中间表关联本端的字段名
<many-to-many class="包名.Authorization" column="authid"/>
</set>
◇◇中间表的id,最好设置为自动增长;否则级联插入时,中间表的主键id,因为没法人工设置,只能自动使用缺省值,就只能插入一条;
(二)多对多的操作:
(1)级联插入
---DAO:
/**
* 插入User PO记录,级联插入该用户拥有的权限PO记录;
* @param usr---临时态User PO对象;
*/
public void addUsr(Usr usr){
Transaction tr=null;
Session session=null;
try{
session=HibernateSessionFactory.getSession();
tr=session.beginTransaction();
session.save(usr);//将级联插入该用户拥有的所有权限authorization PO记录
tr.commit();
}catch(Exception e){
e.printStackTrace();
try{
tr.rollback();
}catch(HibernateException he){
he.printStackTrace();
}
}finally{
session.close();
}
}
---BO:张磊磊。
public static void main(String[] args) {
Usr usr=new Usr(); ------------先做好一个新用户;(临时态)
usr.setAddress("changchun");
usr.setName("zhangsan");
usr.setMale("male");
usr.setId(225);//主键,App设置;DB中为varchar 或 int;
Authorization aut=new Authorization(); -----再做好几个该用户拥有的新权限;(临时态)
aut.setColumnId(111);
aut.setAuthorize(new Integer(123));
aut.setInit(new Integer(124));
aut.setId(1008); //主键,
Authorization aut1=new Authorization();-----新权限;
aut1.setColumnId(112);
aut1.setAuthorize(new Integer(113));
aut1.setInit(new Integer(114));
aut1.setId(1009);
//建立两种PO的关联;
Set set1=usr.getAuthorization(); ---先获得关联属性集合Set;(HashSet)
set1.add(aut); //权限集合中加入权限PO;建立关联
set1.add(aut1);
UsrDAO usrdao=new UsrDAO();---调用DAO方法,实现级联插入;
usrdao.addUsr(usr);
}
结果:插左PO记录,级联插入中间表记录,以及右PO记录;
▲ 结论:多对多和一对一 一样,可以只配置单向关联。
=========联表查询=====
//没关联记录时,没显示。
//故意插入几个关联记录,再试试。
问:可以只配单边映射配置,只实现单向查询吗?
i)写DAO内:《HibernateService.java》
[数据访问方法] ducongan.
public List listAllUsrCascadeAuthorization() throws HibernateException{
List list = null;
Session session = null;
Transaction tx = null;
..............................
list = session.createQuery("from Usr").list(); //查出Usr PO对象,自动联表查出Authorization PO对象。
━━━━━━━━━━━
.............................. //是否级联操作、联表查询,从上层Hibernate API无法看出,关键在.hbm.xml中。
return list;
}
注意:查出的list的意义。
5)BO (main)中,写调用该DAO方法:
List list= DAO对象.listAllUsrCascadeAuthorization();
Usr userpo=(Usr)list.get(0); //这个是一个Usr PO对象。
Set st=userpo.getAuths(); //取出该Usr PO对象中的关联属性Set
//遍历HashSet,it.next(),取出的是该Usr PO的一个权限PO。 ----晒晒老知识:Set只能遍历,不易于单个读取。
//注意:检索策略;User----Authorization;默认使用延迟检索;---所以,不要关闭Session;
如果改为立即检索;---则可能提取出所有的权限对象;
如果再配有反向的Authorization----User;且为立即检索,---则连锁效应可能提取大量无用的关联对象;
作业:撰写代码实现查询,查出《usr表》中name='张局长' 的user用户,其所拥有的各个(或第一个)权限,和其他哪些用户同时拥有这些权限?
要求:必须列出张局长拥有哪些权限,及其权限名;
还要列出每个权限所有拥有者的用户名;
答案:
---BO:
UserDao userDao = new UserDao();
User findUserByName = userDao.findUserByName("张局长");
System.out.println("张局长的ID号: "+findUserByName.getId());
Set auths = findUserByName.getAuths(); //▲延迟加载,关联集合其中PO为代理对象;
//▲注意:因为是延迟加载,所以自动使用Hibernate实现的PersistenceSet,不能强制转换回HashSet;
System.out.println("权限个数: "+auths.size()); //▲用到此PO时,临时加载;
Iterator it = auths.iterator();
while(it.hasNext()){
Authorization az = (Authorization)it.next(); //◆取出一个权限对象; 用到此PO时,临时加载;
System.out.println("==权限ID号为:"+az.getId()); //用到此PO属性时,临时加载属性值;
System.out.println("权限号为:"+az.getAuth());
//提取该权限的所有用户; 也是延迟加载;
Set users = az.getUsers();//拿到该权限的用户集合;
Iterator iterator = users.iterator();//遍历该用户集合;
while(iterator.hasNext()){
User user = (User)iterator.next(); //◆取出一个拥有该权限的用户; 用到此PO时,临时加载;
System.out.println("\t拥有该权限的用户:"+user.getName()+"\t用户ID号: "+user.getId()); //用到此PO时,临时加载;
}
}
//关闭Session;
//如果后续还要使用该Session,进行其他Dao操作,则仅关闭该Session缓存,但并不使其消失;
userDao.sessionClose();
所有操作完毕后,无需Session,此时应关闭Session使其消失;
userDao.closeSession();
数据例子: 《user表》中,1号的张局长,《userauth表》中,拥有:10号、22号权限;
4号 赵科长, 也拥有:10号;
5号 刘。。, ......:10号;
《userauth》表
┌───┬───┬───┐
│ id │ uid │ auid │
├───┼───┼───┤
│ 1 │ │ │
├───┼───┼───┤
│ 2 │ 1 │ 10 │
├───┼───┼───┤
│ 3 │ 2 │ 15 │
├───┼───┼───┤
│ 4 │ 1 │ 22 │
├───┼───┼───┤
│ 5 │ │ │
├───┼───┼───┤
│ │ │ │
├───┼───┼───┤
│ │ 5 │ 10 │
├───┼───┼───┤
│ │ 2 │ 22 │
└───┴───┴───┘
结果:全部延迟加载,连表查询,用的是单表select语句实现。
总结:*-*的检索策略,与1-*的检索策略,相同;
━━━━━━━━━━━━━━━━━━
延迟加载:lazy="true"
立即加载:lazy="false" fetch="select"
预先抓取:lazy="false" fetch="join"