在这个模型中,一个联系人专门负责一个客户,一个客户对应多个联系人
Customer字段如下
private Long cust_id; //客户ID
private String cust_name; //客户公司名
private Set linkmens = new HashSet<>(); //多个联系人
Linkman字段如下
private Long lkm_id; //联系人ID
private String lkm_name; //联系人姓名
private Customer customer; //负责的公司名
两个实体化类都各自生成set/get方法,重写toString方法
Customer.hbm.xml
其他字段的配置和一对一相同,在这里不写了
<set name="linkmens" >
<key column="lkm_cust_id">key>
<one-to-many class="com.cuixiaoming.domain.Linkman"/>
set>
Linkman.hbm.xml
所有字段都和一对一相同,不在展示
需要注意的是,实体类中封装了Customer对象,但是和上一个映射文件不同,只需在对象关系中体现就可以了
<many-to-one name="customer" class="com.itheima.domain.Customer" column="lkm_cust_id">many-to-one>
至此,最基本的一对多关系就被创建好了,
因为核心配置文件中添加了
<property name="hibernate.hbm2ddl.auto">updateproperty>
所以只需要运行下面代码就可以自动创建表
工具类见上一篇文章
@Test
public void test(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
transaction.commit();
}
能创建的原因是,一旦获取的Session,核心配置文件就会被加载,两个映射文件也会被加载,所以就会把数据库中与映射文件不同的列创建出来,因为此时数据库为空,所以完全创建两个新表;
下面添加一组一对多的数据
@Test
//存一组一对多数据
public void test01(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
//创建Customer,并设置名字
Customer customer = new Customer();
customer.setCust_name("某公司1");
//创建Linkman,并设置名字
Linkman man1 = new Linkman();
man1.setLkm_name("联系人1-1");
Linkman man2 = new Linkman();
man2.setLkm_name("联系人1-2");
//维护Customer和Linkman的关系
customer.getLinkmens().add(man1);
customer.getLinkmens().add(man2);
//维护Linkman和Customer的关系
man1.setCustomer(customer);
man2.setCustomer(customer);
//分别将三条数据存入数据库
session.save(customer);
session.save(man1);
session.save(man2);
transaction.commit();
}
但是,我们发现,这一组一对多的数据需要Session save三次才可以,太麻烦了
我们想,两个Linkman的信息,已经封装到了Customer的集合中了,我们能不能只对Customer进行操作而对所有数据进行操作呢?
我们可以在Customer.hbm.xml中的set标签中配置set标签上配置 cascade=”save-update” ,即 “层叠的存储和更新”
于是,我们就可以
@Test
//存数据
public void test02(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
//创建Customer
Customer customer = new Customer();
customer.setCust_name("某公司2");
//创建Linkman
Linkman man1 = new Linkman();
man1.setLkm_name("联系人2-1");
Linkman man2 = new Linkman();
man2.setLkm_name("联系人2-2");
//维护Customer和Linkman的关系
customer.getLinkmens().add(man1);
customer.getLinkmens().add(man2);
man1.setCustomer(customer);
man2.setCustomer(customer);
session.save(customer);
//session.save(man1);
//session.save(man2);
transaction.commit();
}
类似的,我们也可以Linkman中的many-to-one标签中配置 cascade=”save-update”,从而在只操作Linkman的时候,一起将Customer数据也一并操作
如果我们在Customer中set标签中加入cascade=”delete” 标签,我们就可以进行级联删除,在只删除Customer的情况下,对相关联的Linkman数据也进行删除
@Test
//测试删除操作
public void test4(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
//先查询在删除
//因为在映射文件种配置了customer与linkman的关系 所以查询出的customer本身就与linkman有关系的
Customer customer = session.get(Customer.class, 2L);
//删除2号customer与3号linkman和号4linkman存在关系
session.delete(customer);
transaction.commit();
}
当我们给Customer和Linkman都配置了cascade之后,运行下面这个代码
@Test
public void test6(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
//创建Customer
Customer customer = new Customer();
customer.setCust_name("某公司3");
//创建Linkman
Linkman man1 = new Linkman();
man1.setLkm_name("联系人3-1");
Linkman man2 = new Linkman();
man2.setLkm_name("联系人3-2");
//级联保存的关系
customer.getLinkmens().add(man1);
customer.getLinkmens().add(man2);
//维护外键
man1.setCustomer(customer);
man2.setCustomer(customer);
session.save(customer);//保存customer的同时 保存man1和man2
transaction.commit();
}
我们打开控制台,发现它向数据库发送了5条SQL指令
Hibernate: insert into cst_customer (cust_name) values (?)
Hibernate: insert into cst_linkman (lkm_name, lkm_cust_id) values (?, ?)
Hibernate: insert into cst_linkman (lkm_name, lkm_cust_id) values (?, ?)
Hibernate: update cst_linkman set lkm_cust_id=? where lkm_id=?
Hibernate: update cst_linkman set lkm_cust_id=? where lkm_id=?
我通过多种方式进行了测试,发现前三条语句已经可以将三条数据完整地添加到数据库了,后两条完全是多余的
说一下它这样做的原因吧
在Customer中的Set标签里配置inverse=true,中断它的外键维护权限
因为两个实例化对象都封装了另一个实例化对象,
所以,我们在查询到其中一个对象的时候,可以通过get方法,获取另一个对象
@Test
//对象导航查询操作
public void test8(){
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
Linkman linkman = session.get(Linkman.class, 2L);//第一条查询语句
System.out.println(linkman);
Customer customer = linkman.getCustomer();//第二条查询语句
System.out.println(customer);
transaction.commit();
session.close();
}
在上面导航查询的时候,我们使用debug模式运行,发现在我们获取打印Linkman对象的时候,只发送了一条查询Linkman的SQL,并没有同一时间获取Customer对象
在获取打印Customer的时候,才会发送SQL查询Customer数据
这种现象称为延迟查询,如果我们想关闭延迟查询,从而使在查询第一个对象的时候就获取所有关联对象的信息的话
我们可以通过在映射配置中中添加 lazy=”false”
3月17日更新:在一对多多对多关系中,最好都配置lazy=”false”,可以避免发生HHH000142: Javassist Enhancement failed
这里的修改说的是修改映射关系,
修改都是先查后改,获取想要修改的对象后,替换要更改的映射的对象,这个对象可以是已经存在的(需要获取),或者以前不存在的(需要创建)
@Test
//修改操作
public void test9(){
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
//获得1号的Linkman
Linkman linkman = session.get(Linkman.class, 1L);
//获得8号的Customer
Customer customer = session.get(Customer.class, 8L);
//修改
linkman.setCustomer(customer);
transaction.commit();
session.close();
}