当前位置: 首页 > 后端技术 > Java

如何在Jpa中玩一对多?如果不理解

时间:2023-04-01 15:38:07 Java

Jpa中的一对一和一对多,总会觉得有点迷茫。今天就来简单聊聊这个话题。1.一对一例如一个学校有一个地址,一个地址只有一个学校。那么我们可以这样设计类:@Data@Entity@Table(name="t_address")私有字符串省;私有字符串城市;私有字符串区域;私人字符串电话;@OneToOne(cascade=CascadeType.ALL)privateSchoolschool;}@Data@Entity@Table(name="t_school")publicclassSchool{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)privateIntegersid;私有字符串名称;@OneToOne(cascade=CascadeType.ALL)privateAddressaddress;}一对一关系可以只在School维护,也可以只在Address维护,也可以两者都维护,具体是哪一个看需要。在上面的例子中,我们通过@OneToOne注解在School和Address中保持了一对一的关系。cascade用于配置级联操作,有以下值:ALL:所有操作PERSIST:级联添加MERGE:级联更新REMOVE:级联删除REFRESH:级联刷新根据需要选择合适的。这样,在最终创建的t_school表和t_address表中,分别会多出一个字段address_aid和school_sid。这两个字段是外键,两个表中的不同记录就是通过外键关联起来的。有些人可能不习惯这个自动添加的字段,所以也可以自定义这个字段。不管怎样,这个字段总是需要的。自定义方式如下:@Data@Entity@Table(name="t_address")publicclassAddress{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)privateIntegeraid;私有字符串省;私有字符串城市;私有字符串区域;私人字符串电话;@OneToOne(cascade=CascadeType.ALL)@JoinColumn(name="sid",referencedColumnName="sid")私立学校学校;@Column(insertable=false,updatable=false)privateIntegersid;}@Data@Entity@Table(name="t_school")publicclassSchool{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)privateIntegersid;私有字符串名称;@OneToOne(cascade=CascadeType.ALL)@JoinColumn(name="aid",referencedColumnName="aid")privateAddress地址;@Column(insertable=false,updatable=false)privateIntegeraid;}在Address中自定义一个sid,并设置该字段不可添加或修改,然后通过使用@JoinColumn注解来指定关联关系。@JoinColumn注解中的name表示当前类中的属性名,referencedColumnName表示School类中对应的属性名。在School类中做类似的操作。最后启动项目观察MySQL中生成的表。2.一对多一个班有多个学生,一个学生只属于一个班级。我们可以这样定义实体类:@Data@Table(name="t_student")@EntitypublicclassStudent{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)privateIntegersid;私有字符串名称;@ManyToOne(cascade=CascadeType.ALL)privateClazzclazz;}@Data@Table(name="t_clazz")@EntitypublicclassClazz{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)privateIntegercid;私有字符串名称;@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)privateListstudents;}Student和Clazz是多对一的关系,使用@ManyToOne注解,Clazz和Student是一对一的关系对多,用@OneToMany注释。Student和Clazz之间的关系是多对一的。以后t_student表中会有一个属性clazz_cid,通过这个外键关联Student和Clazz。如果我们不想要自动生成的clazz_cid,我们也可以自定义如下:私有字符串名称;@ManyToOne(cascade=CascadeType.ALL)@JoinColumn(name="cid")privateClazzclazz;@Column(insertable=false,updatable=false)privateIntegercid;}定义一个cid属性,设置为不可用编辑,不可添加,然后通过@JoinColumn注解将cid属性配置为外键。Clazz和Student是一对多的关系,通过一张自动生成的第三张表来实现,如下:3.测试3.1添加测试首先是一对一的添加测试,如下:publicinterfaceSchoolRepositoryextendsJpaRepository{}@SpringBootTestclassJpaOneToManyApplicationTests{@AutowiredSchoolRepositoryschoolRepository;@TestvoidcontextLoads(){Schoolschool=newSchool();学校.setSid(1);school.setName("哈佛大学");地址address=newAddress();地址.setAid(1);address.setProvince("黑龙江省");address.setCity("哈尔滨");address.setArea("某处");地址.setPhone("123456");学校.setAddress(地址);schoolRepository.save(学校);}}本次测试时,关系由t_school维护,所以以后要填充的外键就是t_school中的aid。添加结果如下图所示:这是一个简单的添加案例。更新还会调用保存方法。更新时会先判断id是否存在。如果存在则更新,如果不存在则添加。我们看一下类的添加,如下:c.setCid(1);c.setName("二班三年级");Liststudents=newArrayList<>();学生s1=新学生();s1.setSid(1);s1.setName("javaboy");学生.添加(s1);学生s2=新学生();s2.setSid(2);s2.setName("张三");学生.add(s2);c.setStudents(学生);clazzRepository.save(c);}注意,添加了班级,所以班级和学生的关系是由第三张表维护的,不是学生。3.2查询测试这里再做一个简单的查询,假设我们要按省份搜索学校,如下:@Testvoidtest01(){Listlist=schoolRepository.findSchoolByAddressProvince("黑龙江");System.out.println("list="+list);}给大家说说SpringData是如何解析上面自定义查询方法的:先截取findSchoolByAddressProvince的前缀,留下AddressProvince。查看School是否有addressProvince属性,然后根据属性查询。对于我们的案例,没有addressProvince属性,所以继续下一步。从右边的驼峰开始分裂,去掉第一个驼峰后面的内容,我们这里分裂后只有Address,判断School中是否有Address属性,如果没有,继续重复这一步,继续切掉先右边一个驼峰。上例中,School中有address属性,那么接下来检查address中是否有province属性,因为我们这里只剩下一个province了,如果剩下的字符串类似provinceAaaBbb,那么继续第三步是分析。上面的写法有个小风险,假设恰好School中有一个名为addressProvince的属性,那么此时的分析就会出错。因此,对于上面的查询,我们也可以这样定义:publicinterfaceSchoolRepositoryextendsJpaRepository{ListfindSchoolByAddress_Province(Stringprovince);}这时候就不会出现歧义了,系统就会知道省份是address的一个属性。另一个类查询,如下:System.out.println("list="+list);}如果在查询过程中需要对学生进行排序,可以添加如下属性:@Data@Table(name="t_clazz")@EntitypublicclassClazz{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)privateIntegercid;私有字符串名称;@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)@OrderBy("siddesc")privateListstudents;}query可以通过@OrderBy("siddesc")studentSort设置。好了,几个小案例,希望对大家有所帮助,公众号后台回复jpa02,获取本案例的下载链接。