一对多:java
客户和联系人是一对多关系:mysql
联系人在人际交流中担任着重要的角色,在销售过程当中,咱们一般不是在最开始就联系到有决策权的高管,而有多是接电话的前台A、营销人员B、客服C、技术人员D等等。这些人都是咱们与企业保持交流的联系人。他们对产品的支持态度决定了产品是否可以顺利推送到高管面前。web
一般销售人员在跟进一个客户时,会有一个或者多个联系人,这些联系人所处的职位决定了他们的角色。算法
也就是说客户和联系人的关系是:
公司和公司员工的关系。sql
多对多:网络
一对一:session
一对多映射配置:架构
以客户和联系人为例:客户是一,联系人是多。app
第一步:建立两个实体类,客户和联系人:dom
第二步:让两个实体类之间互相表示。
一个客户能够对应多个联系人。
这里装载联系人,装载的容器用的是Set集合,而不是LinkedList,由于Set集合的特色是,值不可以重复。
一个联系人只能属于一个客户。
//客户的实体类:
public class Customer {
private Integer cid;
private String custName;
private String custLevel;
private String custSource;
private String custPhone;
private String custMobile;
private Set<LinkMan> linkMans=new HashSet<LinkMan>();
public Set<LinkMan> getLinkMans() {
return linkMans;
}
public void setLinkMans(Set<LinkMan> linkMans) {
this.linkMans = linkMans;
}
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
public String getCustMobile() {
return custMobile;
}
public void setCustMobile(String custMobile) {
this.custMobile = custMobile;
}
public Customer(String custName, String custLevel, String custSource, String custPhone,
String custMobile) {
super();
this.custName = custName;
this.custLevel = custLevel;
this.custSource = custSource;
this.custPhone = custPhone;
this.custMobile = custMobile;
}
public Customer() {
super();
}
@Override
public String toString() {
return "Customer [cid=" + cid + ", custName=" + custName + ", custLevel=" + custLevel + ", custSource="
+ custSource + ", custPhone=" + custPhone + ", custMobile=" + custMobile + "]";
}
}
//联系人实体类
public class LinkMan {
private Integer lid;
private String lname;
private String lgender;
private String lphone;
private Customer customer;
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public Integer getLid() {
return lid;
}
public void setLid(Integer lid) {
this.lid = lid;
}
public String getLname() {
return lname;
}
public void setLname(String lname) {
this.lname = lname;
}
public String getLgender() {
return lgender;
}
public void setLgender(String lgender) {
this.lgender = lgender;
}
public String getLphone() {
return lphone;
}
public void setLphone(String lphone) {
this.lphone = lphone;
}
public LinkMan() {
super();
// TODO Auto-generated constructor stub
}
public LinkMan(String lname, String lgender, String lphone) {
super();
this.lname = lname;
this.lgender = lgender;
this.lphone = lphone;
}
@Override
public String toString() {
return "LinkMan [lid=" + lid + ", lname=" + lname + ", lgender=" + lgender + ", lphone=" + lphone + "]";
}
}
第三步:配置映射关系
在客户的映射文件中,表示全部的联系人
<class name="类的全路径" table="表名">
<id name="id" column="cid">//实体类属性名以及表字段名
<generator class="native"></generator>
</id>
<property name="custName"></property>
<property name="custLevel"></property>
<property name="custSource"></property>
<property name="custPhone"></property>
<property name="custMobile"></property>
<!-- 在客户映射文件中,表示全部联系人 使用set标签表示全部联系人 set标签里面有name属性:属性值写,在客户实体类里面表示联系人的set集合名称 -->
<set name="setLinkMan">
<!-- 一对多建表,有外键 hibernate机制:双向维护外键,在一和多两方都配置外键。 column属性值:外键名称。这里的外键名称能够随便写。 -->
<key column="clid"></key>
<!-- 客户全部的联系人,class里面写联系人实体类全路径 -->
<one-to-many class="联系人实体类全路径" />
</set>
</class>
在联系人映射文件中,表示所属客户
<class name="实体类的全路径" table="表名">
<id name="lid"><!-- 当column不写时,名称和name的名称同样 -->
<generator class="native"></generator>
</id>
<property name="lname"></property>
<property name="lgender"></property>
<property name="lphone"></property>
<!-- 表示联系人所属客户 name属性:联系人实体类被装载到Customer中,这里写customer名称 class属性:customer全路径 column属性:外键名称 -->
<many-to-one name="customer" class="customer全路径" column="clid"></many-to-one>
</class>
第四步:建立核心配置文件,把映射文件引入到核心配置文件中。
<mapping resource="cn/domarvel/entity/Customer.hbm.xml"/>
<mapping resource="cn/domarvel/entity/LinkMan.hbm.xml"/>
第五步:写测试类:
最后:运行项目,让表被建立。
能够看到结果,客户表和联系人表进行了关联,联系人引入客户表的id做为外键。
这些操做Hibernate都帮你封装好了。
级联操做
级联保存:
//在没有进行简化前,须要将联系人和客户的关系在代码行中进行说明。
@Test
public void showCreate(){
Transaction transaction=null;
try {
Session session=HibernateUtils.getCurrentSession();
transaction=session.beginTransaction();
Customer customer=new Customer("百度", "vip", "网络", "110", "120");
LinkMan linkMan=new LinkMan("侯松", "男", "123");
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);//注意在没有进行配置简化配置的时候还须要让联系人实体类和客户实体类关联。
session.save(customer);
session.save(linkMan);
transaction.commit();
} catch (Exception e) {
transaction.rollback();
}
}
简化操做:
在映射文件的客户方,也就是一对多的一这方进行配置:
<set name="linkMans" cascade="save-update"><!-- 这里配置了一个属性cascade,值为save-update,表示对保存和更新操做,作了简化,不用对多的那方 设置和一的那方的关系。 -->
<key column="clid"></key>
<one-to-many class="cn.domarvel.entity.LinkMan"/>
</set>
//简化后代码
@Test
public void showCreate02(){
Transaction transaction=null;
try {
Session session=HibernateUtils.getCurrentSession();
transaction=session.beginTransaction();
Customer customer=new Customer("腾讯", "svip", "网络", "110", "120");
LinkMan linkMan=new LinkMan("FireLang", "女", "123");
customer.getLinkMans().add(linkMan);//注意在这里就只须要把联系人(多的那方)放入到客户方(一的那方),就能够了。
session.save(customer);//最后保存的时候就只须要保存客户方(一的那方)
transaction.commit();
} catch (Exception e) {
transaction.rollback();
}
}
级联删除:
这里就直接上简化后的代码了。
<!-- 配置映射文件 -->
<!-- 在一的那方配置删除的简化,当要想直接删除一的那方,Hibernate自动帮你删除相关联的多的那方 -->
<set name="linkMans" cascade="save-update,delete"><!-- 在cascade里面配置delete,多个值用","分割 -->
<key column="clid"></key>
<one-to-many class="cn.domarvel.entity.LinkMan"/>
</set>
//测试代码
@Test
public void showDelete(){
//在之前咱们要删除一的那方,是先根据一的那方删除多的那方,再删除一的那方。
//如今Hibernate已经帮咱们封装好了,咱们只须要配置好,再直接删除一的那方就好了。
Transaction transaction=null;
try {
Session session=HibernateUtils.getCurrentSession();
transaction=session.beginTransaction();
Customer customer=session.get(Customer.class, 2);
session.delete(customer);
transaction.commit();
} catch (Exception e) {
transaction.rollback();
}
}
级联修改:
操做:如今是让一个联系人的客户变成另一个。咱们须要修改的就是某一个员工的外键值。
咱们的普通更新操做:
@Test
public void showUpdate(){
//下面经过一个例子引入Hibernate中做更新操做时可以使用的一个关键字,目的是可以简化更新操做
Transaction transaction=null;
try {
Session session=HibernateUtils.getCurrentSession();
transaction=session.beginTransaction();
Customer customer=session.get(Customer.class, 1);
LinkMan linkMan=session.get(LinkMan.class, 3);
linkMan.setCustomer(customer);//由于这里的数据是持久态的因此咱们直接设置值后,Hibernate会帮咱们自动更新数据。
customer.getLinkMans().add(linkMan);
transaction.commit();
} catch (Exception e) {
transaction.rollback();
}
}
//经过输出底层sql代码能够知道
Hibernate:
select
customer0_.cid as cid1_1_0_,
customer0_.custName as custName2_1_0_,
customer0_.custLevel as custLeve3_1_0_,
customer0_.custSource as custSour4_1_0_,
customer0_.custPhone as custPhon5_1_0_,
customer0_.custMobile as custMobi6_1_0_
from
customer customer0_
where
customer0_.cid=?
Hibernate:
select
linkman0_.lid as lid1_2_0_,
linkman0_.lname as lname2_2_0_,
linkman0_.lgender as lgender3_2_0_,
linkman0_.lphone as lphone4_2_0_,
linkman0_.clid as clid5_2_0_
from
linkman linkman0_
where
linkman0_.lid=?
Hibernate:
select
linkmans0_.clid as clid5_2_0_,
linkmans0_.lid as lid1_2_0_,
linkmans0_.lid as lid1_2_1_,
linkmans0_.lname as lname2_2_1_,
linkmans0_.lgender as lgender3_2_1_,
linkmans0_.lphone as lphone4_2_1_,
linkmans0_.clid as clid5_2_1_
from
linkman linkmans0_
where
linkmans0_.clid=?
Hibernate:
update
linkman
set
lname=?,
lgender=?,
lphone=?,
clid=?
where
lid=?
Hibernate:
update
linkman
set
clid=?
where
lid=?
//在这里进行了重复的修改外键的操做,修改两次的缘由是Hibernate双向维护外键。
inverse属性
linkMan.setCustomer(customer);
。达到了更新联系人所属关系。可是从上面的sql语句输出的状况能够看出。有两次的update操做。这两次是怎么来的呢???一次是咱们本身写的。linkMan.setCustomer(customer);
另一次是Hibernate帮咱们修改外键关系的。因此这里就重复了update语句。使得修改关系时效率下降了。(注意:当咱们修改多的那方的时候,不是修改的所属关系,而是其它的数据,那么就不会有两次更新。)那么咱们该怎么解决更新两次的问题呢???那这里就得靠inverse属性了。解决方式:让其中的一方不维护外键。
具体实现:
在放弃关系维护映射文件中,进行配置,在set标签上使用inverse属性。(inverse:反向)
inverse:
//在这里不须要对java代码进行修改,只须要进行配置就能够了。这里仍是用的是上面的问题代码。
//在一的一方进行配置,目的,不让一的一方反向维护
<set name="linkMans" cascade="save-update,delete" inverse="true">
<key column="clid"></key>
<one-to-many class="cn.domarvel.entity.LinkMan"/>
</set>
再次做关系更新操做,看一下运行结果:
Hibernate:
select customer0_.cid as cid1_1_0_, customer0_.custName as custName2_1_0_, customer0_.custLevel as custLeve3_1_0_, customer0_.custSource as custSour4_1_0_, customer0_.custPhone as custPhon5_1_0_, customer0_.custMobile as custMobi6_1_0_ from customer customer0_ where customer0_.cid=? Hibernate: select linkman0_.lid as lid1_2_0_, linkman0_.lname as lname2_2_0_, linkman0_.lgender as lgender3_2_0_, linkman0_.lphone as lphone4_2_0_, linkman0_.clid as clid5_2_0_ from linkman linkman0_ where linkman0_.lid=? Hibernate: select linkmans0_.clid as clid5_2_0_, linkmans0_.lid as lid1_2_0_, linkmans0_.lid as lid1_2_1_, linkmans0_.lname as lname2_2_1_, linkmans0_.lgender as lgender3_2_1_, linkmans0_.lphone as lphone4_2_1_, linkmans0_.clid as clid5_2_1_ from linkman linkmans0_ where linkmans0_.clid=? Hibernate: update linkman set lname=?, lgender=?, lphone=?, clid=? where lid=? //能够看到这里update只更新了一次 //这里要注意:咱们设置inverse=true,只是解决对更新所属关系时带来的效率问题。
这里有篇文章你们必定要看看(inverse和cascade的区别,以及一对多添加操做时外键为Null的问题)
多对多映射配置
第一步:建立实体类,用户和角色。
第二步:让两个实体类之间互相表示。
public class User {
private Integer uid;
private String uname;
private String pword;
private Set<Role> roleSet=new HashSet<Role>();
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getPword() {
return pword;
}
public void setPword(String pword) {
this.pword = pword;
}
public Set<Role> getRoleSet() {
return roleSet;
}
public void setRoleSet(Set<Role> roleSet) {
this.roleSet = roleSet;
}
@Override
public String toString() {
return "User [uid=" + uid + ", uname=" + uname + ", pword=" + pword + ", roleSet=" + roleSet + "]";
}
public User() {
super();
// TODO Auto-generated constructor stub
}
public User( String uname, String pword, Role role) {
super();
this.uname = uname;
this.pword = pword;
roleSet.add(role);
}
}
public class Role {
private Integer rid;
private String rname;
private String rmemo;
private Set<Role> userSet=new HashSet<Role>();
public Set<Role> getUserSet() {
return userSet;
}
public void setUserSet(Set<Role> userSet) {
this.userSet = userSet;
}
public Integer getRid() {
return rid;
}
public void setRid(Integer rid) {
this.rid = rid;
}
public String getRname() {
return rname;
}
public void setRname(String rname) {
this.rname = rname;
}
public String getRmemo() {
return rmemo;
}
public void setRmemo(String rmemo) {
this.rmemo = rmemo;
}
@Override
public String toString() {
return "Role [rid=" + rid + ", rname=" + rname + ", rmemo=" + rmemo + "]";
}
public Role(String rname, String rmemo) {
super();
this.rname = rname;
this.rmemo = rmemo;
}
public Role() {
super();
// TODO Auto-generated constructor stub
}
}
第三步:配置映射关系
配置多对多关系
在用户里面表示全部角色,使用set标签
<?xml version="1.0" encoding="UTF-8"?>
<!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="cn.domarvel.manytomany.Userb" table="userb">
<!-- table是要生成的表名 -->
<id name="uid">
<generator class="native"></generator>
</id>
<property name="uname"/>
<property name="pword"/>
<!-- property里面的column属性能够不用写,不写就默认和property里面的name属性值相同 -->
<set name="roleSet" table="user_role" cascade="save-update">
<!-- 在用户里面表示角色,使用set标签 name属性:角色set集合名称 table属性:第三张表名称 -->
<!-- 注意若是尚未在其它映射文件中配置表名,那么第一次配置就能够乱写, 可是若是有一张表已经声明说表名叫什么了,那就不可以再乱写 -->
<key column="fuid"/>
<!-- key标签里面配置: 配置当前映射文件在第三张表外键名称 -->
<many-to-many class="cn.domarvel.manytomany.Role" column="frid"></many-to-many>
<!-- class:角色实体类全路径 column:角色在第三张表外键名称 -->
</set>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<!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="cn.domarvel.entity.Role" table="role">
<id name="rid">
<generator class="native"></generator>
</id>
<property name="rname"/>
<property name="rmemo"/>
<set name="userSet" table="user_role">
<key column="frid"></key>
<many-to-many class="cn.domarvel.entity.User" column="fuid"></many-to-many>
</set>
</class>
</hibernate-mapping>
第四步:在核心配置文件中引入映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///hibernate</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.current_session_context_class">thread</property>
<mapping resource="cn/domarvel/entity/Role.hbm.xml"/>
<mapping resource="cn/domarvel/entity/User.hbm.xml"/><!-- 引入映射配置文件到核心配置文件 -->
</session-factory>
</hibernate-configuration>
多对多级联保存
根据用户保存角色:
第一步:在用户配置文件中set标签进行配置,cascade值save-update,意思是对级联操做进行了简化,好比说:当我一对多的时候,咱们之前是先删除多的那方再删除一的那方,或者先保存多的那方再保存一的那方。可是如今不用了若是咱们设置了save-update或者delete,那咱们只须要删除一的那方或者保存一的那方就好了,但前提是必需要先把多的那方保存到一的那方里面才可使用。这也就是简化操做。
<set name="roleSet" table="user_role" cascade="save-update"><!-- 注意cascade是配置到哪儿里后,在保存的时候就保存哪儿一个被配置的实体。而且 另一个实体就保存到被配置的实体里面。这里配置的是User因此就是根据User保存角色 -->
第二步:写代码实现
@Test
public void showAddManyToM(){
Transaction transaction=null;
try {
Session session=HibernateUtils.getCurrentSession();
transaction=session.beginTransaction();
User user=new User("LangSheng", "123456",new Role("开发工程师", "专一于网页先后台开发,和图像算法!!"));
user.getRoleSet().add(new Role("架构师", "进行程序开发架构!"));
User user2=new User("FireLang", "456123", new Role("狼神总部副总裁", "国际知名企业!!"));
user2.getRoleSet().add(new Role("图像算法师", "经过代码对程序的算法处理!以及识别!!"));
session.save(user);
session.save(user2);
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
transaction.rollback();
}
}
多对多级联删除
对于级联删除
在完成上面级联删除的操做后,对于级联删除就变得很简单了。
第一步:在set标签中配置cascade属性,值为delete;若是有多个值记得用”,”号隔开,好比cascade=”save-update,delete”;
第二步:直接进行删除。
对于级联删除,在咱们一对多的时候还能够用,也挺方便的。可是在咱们的多对多的里面就显得有些鸡肋了,由于,假如咱们删除用户表里面的某个用户,那么就会把该用户和角色表里面属于该用户角色属性的角色以及第三张表涉及到该用户的都会删掉。而咱们通常角色是不可以删除的,由于有可能其它用户还有该角色,若是你把别人的角色删掉了,那么其它用户和该角色在第三张表中的关系也会被删掉的。
因此咱们不推荐使用级联删除来操做多对多关系。
咱们应该作的是维护第三张表,也就是用户和角色的主键组成的一个外建表。
维护第三张表关系
用户和角色多对多关系,维护关系经过第三张表维护
让某个用户有某个角色
第一步:根据id查询用户和角色
第二步:把角色放到用户里面
@Test
public void showCreateRelation(){
Transaction transaction=null;
try {
Session session=HibernateUtils.getCurrentSession();
transaction=session.beginTransaction();
User user=session.get(User.class, 4);
Role role=session.get(Role.class, 6);
user.getRoleSet().add(role);
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
transaction.rollback();
}
}
让某个用户没有某个角色
@Test
public void showRemoveReation(){
Transaction transaction=null;
try {
Session session=HibernateUtils.getCurrentSession();
transaction=session.beginTransaction();
User user=session.get(User.class, 4);
Role role=session.get(Role.class, 6);
user.getRoleSet().remove(role);
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
transaction.rollback();
}
}