企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
我们在上节中,按前面设定的ER图,将teacherId存入了Klass表,做为了Klass表中对应Teacher表的外键。Hibernate又是如何处理表与表之间的关系的呢?使用Hibernate后,我们该如何去定义外键呢? 在本节中,我们以 班级表与教师表的多对一关系为例,来共同学习Hibernate实体间的关联关系。 ![https://box.kancloud.cn/946a1713ae6fd9a1c471c3555b9cff1c_669x180.png](https://box.kancloud.cn/946a1713ae6fd9a1c471c3555b9cff1c_669x180.png) # 多对一(manyToOne many-to-one many-one) 前面我们讲过,Hibernate在配置实体时,有两种方式。其一是使用xml的方式,其二是使用注解的方式。在本教程中,我们使用的注解方式。 <hr /> 我们在google中,搜索 Hibernate ManyToOne annotations关键字,大概能找到如下官方文档。 [https://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/](https://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/) 当然,我们也可以打开我们前面下载过的Hibernate文档的single_html,然后在页面中搜索ManyToOne,也能找到注解的相关信息。 [http://127.0.0.1:8081/manual/en-US/html_single](http://127.0.0.1:8081/manual/en-US/html_single) <hr /> ## 加入Teacher实体 ManyToOne是指:每个Klass实体中,都有一个Teacher实体。 所以,我们首先在Klass实体中,增加一个Teacher实体。并增加get/set方法. ``` private Long teacherId; private Teacher teacher; public Long getKlassId() { return klassId; } ... public Teacher getTeacher() { return teacher; } public void setTeacher(Teacher teacher) { this.teacher = teacher; } ``` ## 增加注解 用注解的方法,来表示ManyToOne关系。同其它的注解一样,我们可以将注解写在字段定义的上方,也可以将注解写在set方法的上方。 ``` import javax.persistence.ManyToOne; ... @ManyToOne private Teacher teacher; ``` 然后,我们执行在上一小节中Klass实体对应的单元测试的create方法,看看控制台和数据表将发生什么。 控制台: ``` Hibernate: insert into Klass (name, teacher_teacherId, teacherId) values (?, ?, ?) ``` 查看数据表,我们发现增加了teacher_teacherId字段。很明显,这是由于我们刚刚设置的ManyToOne注解为我们自动生成的。默认的,Hibernate会为我们生成 表名_主键名 的外键字段。 ![https://box.kancloud.cn/ae5118d7a2cd2e80fa86c38b2e17bb95_1256x258.png](https://box.kancloud.cn/ae5118d7a2cd2e80fa86c38b2e17bb95_1256x258.png) 但这好像,并不符合我们的预期。因为我们更愿意看到teacherId做为外键出现。当我们需要指定外键名时,我们则需要JoinColumn注解(当然了,在实现的项目开发中,我们不推荐这么做,也不会这么做)。 然后,我们再运行单元测试。 如果不出意外,我们将在Junit控制面板中,得到如下错误: ``` org.hibernate.MappingException: Repeated column in mapping for entity: com.mengyunzhi.javaee.entity.Klass column: teacherId (should be mapped with insert="false" update="false") at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:682) ... ``` 出现错误时,我们第一要做的事情永远是:阅读错误信息!如果错误信息是英文的呢?那么就应该是:翻译错误信息。 ``` 异常类型:org.hibernate.MappingException: 在实体映射表中出现了重复的字段(列): com.mengyunzhi.javaee.entity.Klass 字段(列)名: teacherId (应该在映射时增加insert="false" update="false") ``` 翻译错误后,我们得知,原来是这样。不错,的确是这样,我们在实体类中,已经声明了teacherId, 然后再声明关联实体后,它又会为我们自动生成teacherId。而一个表中的字段是唯一的,这就造成的冲突。解决的方法很简单,我们删除原来的TeacherId就可以了。 我们删除teacherId,然后结合eclipse,重构其对应的其它方法,再对teacher对应在定义时进行实例化。最终实体类Klass代码如下: ``` package com.mengyunzhi.javaee.entity; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name="Klass") public class Klass implements IdEntity { /** * 班级 * */ private static final long serialVersionUID = 1L; @Id @GenericGenerator(name="idGenerator",strategy="native") @GeneratedValue(generator="idGenerator") private Long klassId; private String name; @ManyToOne @JoinColumn(name="teacherId") private Teacher teacher = new Teacher(); public Long getKlassId() { return klassId; } public void setKlassId(Long klassId) { this.klassId = klassId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public static long getSerialversionuid() { return serialVersionUID; } public Teacher getTeacher() { return teacher; } public void setTeacher(Teacher teacher) { this.teacher = teacher; } /** * 没错,现在teacherId直接存在于关联对象中 * @return {[type]} [description] * @author 梦云智 http://www.mengyunzhi.com * @DateTime 2017-03-02T11:12:31+0800 */ public Long getTeacherId() { return (Long) this.teacher.getId(); } /** * 道理一样,直接把teacherId存在关联对象中 * @param {[type]} Long teacherId [description] * @author 梦云智 http://www.mengyunzhi.com * @DateTime 2017-03-02T11:13:03+0800 */ public void setTeacherId(Long teacherId) { this.teacher.setId(teacherId); } public Klass() { } public Klass(String name, long teacherId) { this.name = name; // 这里的外理方式变对给关联对象赋值了 this.teacher.setId(teacherId); } /** * @see com.mengyunzhi.javaee.entity.IdEntity#getId() */ public Serializable getId() { return this.getKlassId(); } /** * @see com.mengyunzhi.javaee.entity.IdEntity#setId(java.io.Serializable) */ public void setId(Serializable id) { this.setKlassId((Long) id); } } ``` 此时,我们先在navicat中删除数据表Klass(只所以这样做,是由于hibernate再检查数据表时,不会删除冗余字段) 再次运行单元测试: ``` Hibernate: insert into Klass (name, teacherId) values (?, ?) ``` ![https://box.kancloud.cn/dc42ba8b0b7d1c0939e60b611c4455e5_638x140.png](https://box.kancloud.cn/dc42ba8b0b7d1c0939e60b611c4455e5_638x140.png) > git checkout -f step13.3