Hibernate学习笔记③
数据库关系
多对多:通过第三张表来建立关系
第三张表至少包含两个字段作为外键且字段是其他两张表的主键
Hibernate一对多的操作
(1)一对多映射配置
步骤:
- 首先创建两个存在一对多关系的实体类
- 两个实体类需要建立一对多的关系:在多的一方使用一的一方的对象表示所属一的类;在一的一方使用Set集合表示全部属于一的多的实体类;Hibernate中规定必须使用set集合来表示属于一的多的实体类,Set的特点是:无顺序、不重复;
- 建立两个实体类的映射表【配置文件】
public class Contacter { private Integer lid; private String lName; private String lPhone; private Client lClient; //在多的一方使用一的一方的实体类建立关系 public class Client { private Integer cid; private String cName; private String cLevel; //Hibernate要求在多的里面使用set表示,不使用list //Set是无序的,不可重复的集合 private Set<Contacter> belongContecter = new HashSet<Contacter>(); //在多的一方使用Set集合包含属于一的一方的多的实体类,建立关系
配置文件【一对多中一的一方】
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入XML dtd 约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="tech.youngs.entity.Client" table="t_client"> <id name="cid" column="cid"> <generator class="native"></generator> </id> <property name="cName" column="cName"></property> <property name="cLevel" column="cLevel"></property> <!-- 使用set标签表示所有的多【联系人】 @param name 在实体类中表示多的set集合的属性名 --> <set name="belongContecter"> <!-- 一对多建表 要有外键 hibernate规定双方都要配置外键 --> <!-- @param column 外键的名称 --> <key column="fk_cl"></key> <!-- 使用one-to-many表示一对多关系中 一的那一方 --> <!-- @param class 一对多的多的实体类的全路径 --> <one-to-many class="tech.youngs.entity.Contacter"/> </set> </class> </hibernate-mapping>
配置文件【一对多中多的一方】
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入XML dtd 约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="tech.youngs.entity.Contacter" table="t_contacter"> <id name="lid" column="lid"> <generator class="native"></generator> </id> <property name="lName" column="lName"></property> <property name="lPhone" column="lPhone"></property> <!-- 表示一对多 多所属的一 --> <!-- @param name 在实体类中表示所属的一的实体类的属性名 --> <!-- @param class 一对多 多所属的一的实体类的全路径 --> <!-- @param column 外键的名称 与一的配置文件中定义的相同 --> <many-to-one name="lClient" class="tech.youngs.entity.Client" column="fk_cl"></many-to-one> </class> </hibernate-mapping>
(2)一对多级联操作
包括级联保存和级联删除
- 级联保存:创建一个客户及多个联系人
- 级联删除:删除客户以及其联系人,传统的做法需要删除所有联系人后才能删除客户
级联保存第一种方式:
//第一步 创建客户和联系人对象 Client client = new Client(); Contacter contacter = new Contacter(); client.setcName("Baidu"); client.setcLevel("vip"); contacter.setlName("Lucy"); contacter.setlPhone("15191082682"); //第二步 体现出两个实体类的一对多关系 //把联系人对象放到客户的Set集合里面去 client.getBelongContecter().add(contacter); //把客户对象方法放到联系人中去 contacter.setlClient(client); //第三步 保存到数据库中 session.save(client); session.save(contacter);
级联保存第二种方法:
第一步,在客户实体类的映射配置代码中的<set>中添加cascade属性 cascade="save-update"
<set name="belongContecter" cascade="save-update"> <!-- 一对多建表 要有外键 hibernate规定双方都要配置外键 --> <!-- @param column 外键的名称 --> <key column="fk_cl"></key> <!-- 使用one-to-many表示一对多关系中 一的那一方 --> <!-- @param class 一对多的多的实体类的全路径 --> <one-to-many class="tech.youngs.entity.Contacter"/> </set>
第二步 只需要将联系人放到客户里面就可以了,最终只保存客户就行了。
//第二步 把联系人对象放到客户的Set集合里面去 client.getBelongContecter().add(contacter); //第三步 保存到数据库中 session.save(client);
级联删除操作
第一步:在一对多的一的映射配置文件中的<set>标签的cascade属性设置为delete即可
cascade设置多属性,属性和属性之间用逗号隔开
第二步:直接删除即可
Client client = session.get(Client.class, 1); session.delete(client);
Hibernate级联删除的SQL执行过程
Hibernate: update t_contacter set fk_cl=null where fk_cl=? Hibernate: delete from t_contacter where lid=? Hibernate: delete from t_client where cid=?
一对多的修改
业务需求 将A公司的联系人lucy调整到B公司的联系人
//根据id查出调出员工和调入部门 Client client = session.get(Client.class, 1); Contacter contacter = session.get(Contacter.class, 2); //持久态实体类 直接修改客户就行了 //持久态可以自动更新数据库 client.getBelongContecter().add(contacter); contacter.setlClient(client);
注意,由于在一对多的映射文件中,外键由双方维护,在修改的时候会重复修改外键。
解决方案:在一对多的关系里,可以让一的一方放弃对外键的维护。
实现方法:在放弃外键维护的映射文件中进行配置,在set标签上使用inverse属性,设置为true,放弃关系维护。
<set name="belongContecter" cascade="save-update,delete" inverse="true">
Hibernate多对多的操作
多对多映射配置(重要,常用,复杂)
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入XML dtd 约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="tech.youngs.entity.User" table="t_user"> <id name="uid" column="uid"> <generator class="native"></generator> </id> <property name="username" column="username"></property> <!-- 使用set标签表示所有的多【联系人】 @param name 在实体类中表示多的set集合的属性名 @param table 第三张表的名称 --> <set name="roleSet" table="user_role" cascade="save-update"> <!-- @param column 配置的是当前的映射文件在第三张表中的外键的名称 --> <key column="user_id"></key> <!-- 使用many-to-many表示多对多关系 --> <!-- @param class 一对多的多的实体类的全路径 --> <!-- @param colunmn 表示的是多对多另外的多的第三张表的外键多的名称 --> <many-to-many class="tech.youngs.entity.Role" column="role_id"></many-to-many> </set> </class> </hibernate-mapping>
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入XML dtd 约束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="tech.youngs.entity.Role" table="t_role"> <id name="rid" column="rid"> <generator class="native"></generator> </id> <property name="roleName" column="roleName"></property> <!-- 使用set标签表示所有的多【联系人】 @param name 在实体类中表示多的set集合的属性名 --> <set name="userSet" table="user_role"> <!-- @param column 第三张表中这个实体类的外键的名称 --> <key column="role_id"></key> <!-- @param class 多对多的另一个多的实体类的全路径 --> <many-to-many class="tech.youngs.entity.User" column="user_id"></many-to-many> </set> </class> </hibernate-mapping>
同样的,两个实体类需要建立关系,通过Set集合来建立。
多对多的增加与一对多几乎相同。
多对多中一般不使用级联删除:
因为级联删除会将删除条目的外键的条目一并删除,在实际开发中如果要删除某个用户的某个角色我们并不希望其将角色也删除,只需要将该用户的该角色去掉即可。如果级联删除的话会将角色一并删除,其他拥有此角色的用户一并被删除,这是不现实的。
通过维护第三张表来表更多对多关系(重要,常用)
让某个角色拥有某个角色:在第三张表中增加uid和rid的对应关系即可,即增加User中role set的对象
实现代码:
User user = session.get(User.class, 1); Role role = session.get(Role.class, 2); user.getRoleSet().add(role);
同理,让某个角色失去某个角色,移除User中role set中的角色即可
实现代码:
User user = session.get(User.class, 1); Role role = session.get(Role.class, 2); user.getRoleSet().remove(role);