JPA object inheritance relationship

In the process of entity modeling, some entities will have many kinds of deformations, most of which are common, only a small part is unique. At this time, the more elegant design is to abstract the shared attributes to form a base class, and then the implementation class extends the unique attributes. Domain services can abstract common services to form basic services, and then extend specific services. The Repository design, in general, is to abstract the foundation first, and then expand the specific methods. The generic support is generally provided when calling, and the specific Repository is called according to the type of the implementation class. Today I'll show you how to simplify the design of a Repository by using @ Inheritance annotation to make a Repository support all implementation classes.

1, Object modeling

@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 is used to configure the parent class
    • Inheritancetype.single'table maps all fields of all implementation classes into one table.
    • Inheritancetype.table'per'class maps the fields of each implementation class merging base class to a separate table, each table is independent and has no association.
    • InheritanceType.JOINED maps the base class and each implementation class to separate tables, and uses the primary key for association. The implementation class only contains its own unique fields.
  • @Discriminatorcolumn (name = "member" type ") is used as a field to implement class identification. If name is not specified, a new dtype field will be created automatically. This field will be assigned automatically. It does not need to be specified manually and cannot exist as an attribute.
  • @DiscriminatorValue("wexin") implements class specific identification values. jpa will persist data to the corresponding table according to specific identification values, and query statements can also automatically identify types

2, Repository design

1. query

@Repository
public interface MemberRepository extends JpaRepository<Member, String> {
    WeXinMember findFirstByNickName(String openId);
    WeXinMember findFirstByMemberCode(String memberCode);
    StoreMember findTop1ByMemberCode(String memberCode);
    Member findFirstByMemberName(String memberName);
}

  • As you can see from the above, all implementation classes can be regarded as a whole. You can directly use the properties of a certain subclass as query criteria. You can have the return value of a subclass or set the return value of a base class.
  • If the return value is the base class Member, jpa will return the proxy class of hibernate. Hibernate.unproxy(member) is required to get the specific implementation class
Member member = memberRepository.findFirstByMemberName("WeChat members");
if (Hibernate.unproxy(member) instanceof WeXinMember) {
    WeXinMember weXinMember = (WeXinMember) (Hibernate.unproxy(member));
    System.err.println(weXinMember);
}
  • If the return value is an implementation class, you can use the
WeXinMember weXinMember = memberRepository.findFirstByMemberCode("W001");

2. Write and delete

For write and delete operations, jpa is regarded as a whole, and the default method of member repository can be used directly

WeXinMember weXinMember = new WeXinMember();
weXinMember.setMemberCode("W001");
weXinMember.setMemberName("WeChat members");
weXinMember.setMemberType("wexin");
weXinMember.setNickName("twoDog");
weXinMember.setOpenId(UUID.randomUUID().toString());

memberRepository.save(weXinMember);

...
memberRepository.save(weXinMember);
...
memberRepository.delete(weXinMember);

3, Analysis of Repository query statement

Which way to build, mainly considering the table structure relationship of the database

1. Inheritancetype. Single table

Different return value types and different SQL statements

**Base class: * * query all fields, with unnecessary performance overhead

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'

**Implementation class: * * only query the fields of the implementation class

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'

2. Inheritancetype.table'per'class build independent table mode

Base class: union all the sub tables and query again. This method is not desirable and has high performance overhead

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'

**Implementation class: * * only query the fields of the implementation class

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'

3. InheritanceType.JOINED build association table pattern

**Base class: * * query after associating all the sub tables. There are many sub tables with high performance overhead

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'

**Implementation class: * * associated main table and current type sub table query

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'

To sum up: if there is a clear type, the return value of the query method should be set to a specific real class, so as to optimize the query statement

4, Applicable scenarios

This function is quite novel. It is suitable for scenarios with multiple deformation class operations. This method is more convenient and easier to process data than using generic processing directly. However, we need to pay attention to the performance impact of database structure and query statements. It is recommended to use InheritanceType.JOINED mode

5, Source code

https://gitee.com/hypier/barry-jpa/tree/master/jpa-section-4

Tags: Programming Hibernate Database Attribute SQL

Posted on Wed, 25 Mar 2020 05:18:10 -0700 by dancahill