其实很不情愿坐下来敲一行行的文字,其一是我写东西很慢,有时候一句话要斟酌半天,而且打字速度也很慢。其二我感觉我有写文章的时间还不如看一些其他的知识。有种被逼无奈的赶脚额。然而回过头来仔细想一想会总结的人才会有大进步。。。
自己以前也想过做一个通用的大容器,什么对象都能扔进去持久化,这必然会牵扯到反射的内容,hibernate具有访问实体类属性和方法的能力,所以每个持久类都需要有一个无参的默认构造方法,然后可以对对象成员进行setter。可以怎么才能让数据库和实体类相互联系起来呢?
1、关联:将数据库表之间的关系转化为对象之间的关系;在Hibernate中总指实体之间的关系。
2、映射:完成java对象到数据库表的双向转换。
3、级联(可选):将数据库中的级联转化为对象中的级联(两者(对象和数据库表)没关系)。
4、Hibernate的表和对象的映射:
1、实体类型映射:
4.1.1、主键之间的映射
4.1.2、类属性与表字段的映射
4.1.3、组件映射
4.1.4、集合映射
2、实体关联关系映射:
4.2.1、关联关系映射
hibernate的难点主要是有两点:
一、关系映射
用户表:
uuid | NUMBER(10) | NOT NULL |
NAME | VARCHAR2(100) | |
PROVINCE | VARCHAR2(100) | |
用户信息表:
uuid | number(10) | not null |
REALNAME | VARCHAR2(10) | |
BIRTHDAY | NUMBER(10) |
- CONSTRAINT PK_USER_GENERAL PRIMARY KEY(UUID),
- CONSTRAINT FK_USER_GENERAL FOREIGN KEY(UUID)
- REFERENCES TBL_USER(UUID));
农场表(一个用户有多个农场):
uuid | number(10) | |
name | VARCHAR2(10) |
- FK_USER_ID NUMBER(10),
- CONSTRAINT PK_FARM PRIMARY KEY(UUID),
- CONSTRAINT FK_USER_FARM FOREIGN KEY(FK_USER_ID)
- REFERENCES TBL_USER(UUID));
package cn.javass.h3test.model;
public class AddressModel implements java.io.Serializable {
private String province;//省
private String city;//市
private String street;//街道
}
用户model:
package cn.javass.h3test.model;
import java.util.HashSet;
import java.util.Set;
public class UserModel implements java.io.Serializable {
private int uuid;
private String name;//名称
private int age;//年龄
private AddressModel address;//地址
private UserGeneralModel userGeneral;//用户普通信息
private Set<FarmModel> farms = new HashSet<FarmModel>();//拥有的农场
}
用户普通信息
Model:
package cn.javass.h3test.model;
public class UserGeneralModel implements java.io.Serializable {
private int uuid;
private String realname;//真实姓名
private String gender;//性别
private String birthday;//生日
private int weight;//体重
private int height;//身高
private UserModel user;//所属用户
}
农场
Model:
package cn.javass.h3test.model;
public class FarmModel implements java.io.Serializable {
private int uuid;
private String name;//农场的名称
private UserModel user;//所属用户
}
为了方便。数据库里省去了很多不必要的属性,请体谅
5.2、配置
5.2.1、实体类型映射:
5.2.1.1、主键的映射(UserModel.hbm.xml)
- <id name="uuid">
- <generator class="sequence">
- <param name="sequence">user_uuid</param>
- </generator>
- </id>
5.2.1.2、类属性与表字段的映射(UserModel.hbm.xml)
5.2.1.3、组件映射(UserModel.hbm.xml)
- <component name="address" class="cn.javass.h3test.model.AddressModel">
- <property name="province"/>
- <property name="city"/>
- <property name="street"/>
- </component>
5.2.1.4、集合映射(Set、List、Map) (都是通过外键连接的,,,默认延迟抓取)
Set:
- <set name="farmSet" table="TBL_FARM" >
- <key column="fk_user_id"/><!—该外键是tbl_farm的-->
- <element type="string" column="name"/>
- </set>
- <list name="farmList" table="TBL_FARM">
- <key column="fk_user_id"/>
- <list-index column="uuid"></list-index>
- <element type="string" column="name"/>
- </list>
- <map name="farmMap" table="TBL_FARM">
- <key column="fk_user_id"/>
- <map-key type="int" column="uuid"/>
- <element type="string" column="name"></element>
- </map>
对于集合类型默认是延迟加载的,且只能单向导航,不能双向。
5.2.2、实体关联关系映射:
5.2.2.1、单向关联关系映射,不演示。
5.2.2.2、双向关联关系映射
- 单向
- 定义:不知道另一端什么情况,获取一端另一端自动获取,因为单向,你不知道另一侧是什么。
- 如 class A{ B b;}
- class B{ }
- 只能从A导航到B,不能从B导航到A
- 关系维护:另一端维护,如B维护
- 双向
- 定义:知道另一端(两个单向),从一端获取另一端,从另一端也能获取一端
- 如 class A{ B b;}
- class B{ A a;}
- 只能从A导航到B,也能从B导航到A
- 关系维护:两端,对关联的一侧所作的改变,会立即影响到另一侧
- 关联的多样性:
- 从一侧看是多对一,从另一侧看是一对多
- 另外还有一对一、多对多
- EJB CMP:天生双向,对关联的一侧所作的改变,会立即影响到另一侧,
- 如userGeneral.set(user),则自动调用user.setUserGeneral(userGeneral)
- Hibernate、JPA:天生单向,两侧关系的维护是不同的关联,必须手工维护
- 如userGeneral.set(user),则需要手工调用user.setUserGeneral(userGeneral)。
5.2.2.3、一对一主键关系映射(非延迟抓取)
配置1(UserModel.hbm.xml)
配置2(UserGeneralModel.hbm.xml)
- <id name="uuid">
- <generator class="foreign">
- <param name="property">user</param>
- </generator>
- </id>
- <one-to-one name="user"
- class="cn.javass.h3test.model.UserModel"/>
关联的对象所对应的数据库表之间,通过一个外键引用对主键进行约束。
测试:保存对象,只需保存user,自动级联保存用户信息Model
- UserModel user = new UserModel();
- user.setName("昵称");
- UserGeneralModel userGeneral = new UserGeneralModel();
- userGeneral.setRealname("真实姓名");
- userGeneral.setUser(user);
- user.setUserGeneral(userGeneral);
- session.save(user);
- //若没有cascade="all",这句必须
- //session.save(userGeneral);
1、一对一必须手工维护双向关系。
2、cascade="all":表示保存user时自动保存userGeneral,否则还需要一条save(userGeneral)
3、constrained:添加把userGeneral表的主键映射到user主键的外键约束
5.2.2.4、一对多关系映射(父/子关系映射)
配置1(UserModel.hbm.xml)
- <set name="farms" cascade="all">
- <key column="fk_user_id"/>
- <one-to-many class="cn.javass.h3test.model.FarmModel"/>
- </set>
配置2(FarmModel.hbm.xml)
测试:保存对象,只需保存user,自动级联保存用户信息Model
- UserModel user = new UserModel();
- user.setName("昵称");
- UserGeneralModel userGeneral = new UserGeneralModel();
- userGeneral.setRealname("真实姓名");
- userGeneral.setUser(user);
- user.setUserGeneral(userGeneral);
- FarmModel farm = new FarmModel();
- farm.setName("farm1");
- farm.setUser(user);
- user.getFarms().add(farm);
- //session.save(farm);//若没有cascade=all的话需要这条语句
- session.save(user);
以上配置有问题:
- insert into TBL_USER (name, age, province, city, street, uuid) values (?, ?, ?, ?, ?, ?)
- insert into TBL_USER_GENERAL (realname, gender, birthday, weight, height, uuid) values (?, ?, ?, ?, ?, ?)
- insert into TBL_FARM (name, fk_user_id, uuid) values (?, ?, ?)
- update TBL_FARM set fk_user_id=? where uuid=?
1、持久化user(UserModel);
2、持久化user的一对一关系,即userGeneral(UserGeneralModel);
3、持久化user的一对多关系,即farms(Set<FarmModel>);
3.1、首先发现farm是TO,级联save;(因为在这可能是PO,PO的话就应该update,而不是save);
3.2、其次发现farm在farms集合中,因此需要更新外键(fk_user_id),即执行“update TBL_FARM set fk_user_id=? where uuid=? “。
解决这个问题:
告诉Hibernate应该只有一端来维护关系(外键),另一端不维护;通过指定<set>端的inverse=”true”,表示关系应该由farm端维护。即更新外键(fk_user_id)将由farm端维护。
配置修改(UserModel.hbm.xml)
- <set name="farms" cascade="all" inverse="true">
- <key column="fk_user_id"/>
- <one-to-many class="cn.javass.h3test.model.FarmModel"/>
- </set>
再测试:保存对象,只需保存user,自动级联保存用户信息Model
- UserModel user = new UserModel();
- user.setName("昵称");
- UserGeneralModel userGeneral = new UserGeneralModel();
- userGeneral.setRealname("真实姓名");
- userGeneral.setUser(user);
- user.setUserGeneral(userGeneral);
- FarmModel farm = new FarmModel();
- farm.setName("farm1");
- farm.setUser(user);
- user.getFarms().add(farm);
- //session.save(farm);//若没有cascade=all的话需要这条语句
- session.save(user);
更新外键,需要修改FarmModel的外键并update:
- insert into TBL_USER (name, age, province, city, street, uuid) values (?, ?, ?, ?, ?, ?)
- insert into TBL_USER_GENERAL (realname, gender, birthday, weight, height, uuid) values (?, ?, ?, ?, ?, ?)
- insert into TBL_FARM (name, fk_user_id, uuid) values (?, ?, ?)
级联删除
1、当删除user时自动删除user下的farm
结果:
- Hibernate: delete from TBL_USER_GENERAL where uuid=?
- Hibernate: delete from TBL_FARM where uuid=?
- Hibernate: delete from TBL_USER where uuid=?
2、删除user中的farms的一个元素
- UserModel user =
- (UserModel) session.get(UserModel.class, 118);
- FarmModel farm = (FarmModel) user.getFarms().toArray()[user.getFarms().size() - 1];
- user.getFarms().remove(farm);//1.必须先从集合删除
- session.delete(farm);//2.然后才能删除
结果:
如果将子对象从集合中移除,实际上我们是想删除它。要实现这种要求,就必须使用cascade="all-delete-orphan"
。无需再调用session.delete(farm)
5.2.2.5、多对多关系映射:不用
为什么不使用多对多:当添加新字段时给谁?
那实际项目如何用:拆成两个一对多。