在实体建模过程中,有些实体会有多种变形,其中大部分的属性都是共用的,只有一小部分是特有的。这时较优雅的设计是将共用的属性抽象出来形成基类,实现类再去扩展特有属性。领域服务可将通用服务抽象出来形成基类服务,再扩展特有服务。而Repository设计,一般情况也是先抽象基础,再扩展特有方法,调用时一般提供泛型支持,根据实现类的类型调用具体的Repository。
今天介绍使用 @Inheritance
注解让一个Repository支持所有实现类,从而简化Repository的设计。
@Inheritance(strategy = InheritanceType.JOINED) @DiscriminatorColumn(name = "member_type") public class Member { @Id private String memberCode; private String memberName; } @DiscriminatorValue("store") public class StoreMember extends Member { private String memberCard; private Integer memberLevel; } @DiscriminatorValue("wexin") public class WeXinMember extends Member{ private String openId; private String nickName; }
@Inheritance
用来配置父类
- `InheritanceType.SINGLE_TABLE` 将所有实现类的所有字段映射到一个表里。 - `InheritanceType.TABLE_PER_CLASS` 将每个实现类合并基类的字段映射到单独的表里,每个表相关独立且没有关联。 - `InheritanceType.JOINED` 将基类和每个实现类分别映射到独立的表里,并使用主键进行关联,实现类只包含自己独有的字段。
@DiscriminatorColumn(name = "member_type")
用来做实现类区别标识的字段,如果不指定name,则会自动新建dtype字段。此字段系统会自动赋值,不需要人为指定,且不能作为属性存在。 @DiscriminatorValue("wexin")
实现类区别的标识值,jpa会根据具体标识值将数据持久化到对应的表中,查询语句也可自动识别类型 @Repository public interface MemberRepository extends JpaRepository<Member, String> { WeXinMember findFirstByNickName(String openId); WeXinMember findFirstByMemberCode(String memberCode); StoreMember findTop1ByMemberCode(String memberCode); Member findFirstByMemberName(String memberName); }
Hibernate.unproxy(member)
才能得到具体的实现类 Member member = memberRepository.findFirstByMemberName("微信会员"); if (Hibernate.unproxy(member) instanceof WeXinMember) { WeXinMember weXinMember = (WeXinMember) (Hibernate.unproxy(member)); System.err.println(weXinMember); }
WeXinMember weXinMember = memberRepository.findFirstByMemberCode("W001");
写入和删除操作,jpa都视为一个整体,可以直接使用memberRepository默认的方法
WeXinMember weXinMember = new WeXinMember(); weXinMember.setMemberCode("W001"); weXinMember.setMemberName("微信会员"); weXinMember.setMemberType("wexin"); weXinMember.setNickName("twoDog"); weXinMember.setOpenId(UUID.randomUUID().toString()); memberRepository.save(weXinMember); ... memberRepository.save(weXinMember); ... memberRepository.delete(weXinMember);
使用哪种方式构建,主要考虑数据库的表结构关系
InheritanceType.SINGLE_TABLE
构建单表模式 不同的返回值类型,SQL语句有差别
基类:将所有字段都查询出来,有不必要的性能开销
Member findFirstByMemberCode(String memberCode);
SELECT member0_.member_code AS member_c2_0_0_, member0_.member_name AS member_n3_0_0_, member0_.member_card AS member_c4_0_0_, member0_.member_level AS member_l5_0_0_, member0_.nick_name AS nick_nam6_0_0_, member0_.open_id AS open_id7_0_0_, member0_.member_type AS member_t1_0_0_ FROM member member0_ WHERE member0_.member_code = 'W001'
实现类:只查询实现类的字段
WeXinMember findFirstByMemberCode(String memberCode);
SELECT wexinmembe0_.member_code AS member_c2_0_, wexinmembe0_.member_name AS member_n3_0_, wexinmembe0_.nick_name AS nick_nam6_0_, wexinmembe0_.open_id AS open_id7_0_ FROM member wexinmembe0_ WHERE wexinmembe0_.member_type = 'wexin' AND wexinmembe0_.member_code = 'W001'
InheritanceType.TABLE_PER_CLASS
构建独立表模式 基类:将所有子表都 union 进来再查询,此方法不可取,性能开销大
Member findFirstByMemberCode(String memberCode);
SELECT member0_.member_code AS member_c1_0_0_, member0_.member_name AS member_n2_0_0_, member0_.member_card AS member_c1_1_0_, member0_.member_level AS member_l2_1_0_, member0_.nick_name AS nick_nam1_2_0_, member0_.open_id AS open_id2_2_0_, member0_.clazz_ AS clazz_0_ FROM (SELECT member_code, member_name, NULL AS member_card, NULL AS member_level, NULL AS nick_name, NULL AS open_id, 0 AS clazz_ FROM member UNION ALL SELECT member_code, member_name, member_card, member_level, NULL AS nick_name, NULL AS open_id, 1 AS clazz_ FROM store_member UNION ALL SELECT member_code, member_name, NULL AS member_card, NULL AS member_level, nick_name, open_id, 2 AS clazz_ FROM we_xin_member) member0_ WHERE member0_.member_code = 'W001'
实现类:只查询实现类的字段
WeXinMember findFirstByMemberCode(String memberCode);
SELECT wexinmembe0_.member_code AS member_c1_0_, wexinmembe0_.member_name AS member_n2_0_, wexinmembe0_.nick_name AS nick_nam1_2_, wexinmembe0_.open_id AS open_id2_2_ FROM we_xin_member wexinmembe0_ WHERE wexinmembe0_.member_code = 'W001'
InheritanceType.JOINED
构建关联表模式 基类:将所有子表进行关联后再查询,子表多了性能开销大
Member findFirstByMemberCode(String memberCode);
SELECT member0_.member_code AS member_c2_0_0_, member0_.member_name AS member_n3_0_0_, member0_1_.member_card AS member_c1_1_0_, member0_1_.member_level AS member_l2_1_0_, member0_2_.nick_name AS nick_nam1_2_0_, member0_2_.open_id AS open_id2_2_0_, member0_.member_type AS member_t1_0_0_ FROM member member0_ LEFT OUTER JOIN store_member member0_1_ ON member0_.member_code = member0_1_.member_code LEFT OUTER JOIN we_xin_member member0_2_ ON member0_.member_code = member0_2_.member_code WHERE member0_.member_code = 'W001'
实现类:关联主表和当前类型子表查询
WeXinMember findFirstByMemberCode(String memberCode);
SELECT wexinmembe0_.member_code AS member_c2_0_, wexinmembe0_1_.member_name AS member_n3_0_, wexinmembe0_.nick_name AS nick_nam1_2_, wexinmembe0_.open_id AS open_id2_2_ FROM we_xin_member wexinmembe0_ INNER JOIN member wexinmembe0_1_ ON wexinmembe0_.member_code = wexinmembe0_1_.member_code WHERE wexinmembe0_.member_code = 'W001'
综上:如果有明确的类型时,查询方法的返回值应该设置为具体现实类,以便于优化查询语句
此功能还是挺新奇的,适用于包含多种变形类操作的场景,此方法比直接使用泛型处理更方便,更容易处理数据,但需要关注数据库结构与查询语句的性能影响,建议使用 InheritanceType.JOINED 模式
https://gitee.com/hypier/barr...