Entity的继承以及泛型Dao(一)

一、两张类似的表与两个类似的实体类

现在我有一个betspider数据库,里面放着我自己的爬虫爬来的足球赔率,以bs_odds_1x2(胜负平赔率)、bs_odds_handicap(让球赔率)这两张表作为例子,他们在数据库中的表结构是这样的:

屏幕快照 2016-03-11 下午8.36.19

屏幕快照 2016-03-11 下午8.36.45

首先不考虑实体类的继承,这两张表对应着两个独立的实体类:(请暂时忽略下面实体类属性的数据类型与mysql的表字段类型的不完全匹配 =。=#)

@Entity
@Table(name = "bs_odds_1x2")
public class Odds1x2 implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @Column(name = "id")
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    
    @Column(name = "match_id")
    private long matchId;
    
    @Column(name = "update_time")
    private long updateTime;
    
    @Column(name = "bookmaker_id")
    private long bookmakerId;
    
    @Column(name = "active")
    private boolean active;
    
    @Column(name = "home_odds")
    private double homeOdds;
    
    @Column(name = "draw_odds")
    private double drawOdds;
    
    @Column(name = "away_odds")
    private double awayOdds;
}
@Entity
@Table(name = "bs_odds_handicap")
public class OddsHandicap implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @Column(name = "id")
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    
    @Column(name = "match_id")
    private long matchId;
    
    @Column(name = "update_time")
    private long updateTime;
    
    @Column(name = "bookmaker_id")
    private long bookmakerId;
    
    @Column(name = "active")
    private boolean active;
    
    @Column(name = "granter")
    private char granter;
    
    @Column(name = "granter_number")
    private int granterNumber;
    
    @Column(name = "home_odds")
    private double homeOdds;
    
    @Column(name = "away_odds")
    private double awayOdds;
    
}

但实际上我的betspider库里有好多odss_xxx表,我可不想为每种赔率都写一大段的实体类定义。

二、提取父类

从上面的描述我们可以看到,Odds1x2与OddsHandicap这两种赔率其实很类似,他们共同的属性包括id、关联的比赛id、更新时间、博彩数据供应商id、有效性字段。

那么从面向对象的角度来看的话,我们可以把他们的共同属性提取出来,作为一个父类叫做Odds。

public class Odds {
    protected long id;
    
    protected long matchId;
    
    protected long updateTime;
    
    protected long bookmakerId;
    
    protecetd boolean active;
}

但是如何将类之间的继承关系映射到数据库的表中呢,而且先说明的是,我希望映射到数据库后,orm框架为我们自动生成的数据库表依然如上面描述的那样,只有bs_odds_1x2与bs_odds_handicap表,而不存在odds表。

那么,来看一下jpa规范中提供的实体类继承策略。

三、javax.persistence.Inheritance

jpa的@Inheritance注解规定了三种策略:JOINED、SINGLE_TABLE、TABLE_PER_CLASS

3.1 JOINED

使用这种映射策略,继承链中的每个实体类都各自映射到数据库中独立的表,子类表只拥有自身的属性,从父类继承的属性都放在父类表中,同时在父类中定义的主键、将会被所有子类共享作为主键+外键。下面是例子:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Odds {
    @Column(name = "id")
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    protected long id;
    
    protected long matchId;
    
    protected long updateTime;
    
    protected long bookmakerId;
    
    protected boolean active;
}
@Entity
@Table(name = "bs_odds_1x2")
public class Odds1x2 extends Odds {
    
    @Column(name = "home_odds")
    private double homeOdds;
    
    @Column(name = "draw_odds")
    private double drawOdds;
    
    @Column(name = "away_odds")
    private double awayOdds;
}
@Entity
@Table(name = "bs_odds_handicap")
public class OddsHandicap extends Odds {
    @Column(name = "granter")
    private char granter;
    
    @Column(name = "granter_number")
    private int granterNumber;
    
    @Column(name = "home_odds")
    private double homeOdds;
    
    @Column(name = "away_odds")
    private double awayOdds; 
}

将hibernate的hibernate.hbm2ddl.auto属性配置为create-drop。运行后看控制台的输出:

Hibernate: 
    drop table if exists Odds
Hibernate: 
    drop table if exists bs_odds_1x2
Hibernate: 
    drop table if exists bs_odds_handicap
Hibernate: 
    create table Odds (
        id bigint not null auto_increment,
        active bit not null,
        bookmakerId bigint not null,
        matchId bigint not null,
        updateTime bigint not null,
        primary key (id)
    )
Hibernate: 
    create table bs_odds_1x2 (
        away_odds double precision,
        draw_odds double precision,
        home_odds double precision,
        id bigint not null,
        primary key (id)
    )
Hibernate: 
    create table bs_odds_handicap (
        away_odds double precision,
        granter char(1),
        granter_number integer,
        home_odds double precision,
        id bigint not null,
        primary key (id)
    )
Hibernate: 
    alter table bs_odds_1x2 
        add constraint FK_cu80slpty9rvkqutehdh9j8yi 
        foreign key (id) 
        references Odds (id)
Hibernate: 
    alter table bs_odds_handicap 
        add constraint FK_h5jiior7m529wka437y4vlki5 
        foreign key (id) 
        references Odds (id)

可以看到,hibernate为我们创建了三个数据库:Odds(因为我没有给Odds实体类配置表名,所以默认表名为类名)、bs_odds_1x2、bs_odds_handicap。同时用Odds的id作为外键将两个子类表与父类表关联起来。

3.2 SINGLE_TABLE

使用这种映射策略的话,实体类的继承链中,所有的类都会被叠加到一起映射到数据库的唯一的一张表中,该表名以父类对应的表名为准。另外,这张表中,会额外增加一个字段DTYPE,用来判断一行数据是属于哪一个子类

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Odds {
    @Column(name = "id")
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    protected long id;
    
    protected long matchId;
    
    protected long updateTime;
    
    protected long bookmakerId;
    
    protected boolean active;
}

Odds1x2与OddsHandicap两个子类的定义不变。运行后检查hibernate的输出:

Hibernate: 
    drop table if exists Odds
Hibernate: 
    create table Odds (
        DTYPE varchar(31) not null,
        id bigint not null auto_increment,
        active bit not null,
        bookmakerId bigint not null,
        matchId bigint not null,
        updateTime bigint not null,
        away_odds double precision,
        draw_odds double precision,
        home_odds double precision,
        granter char(1),
        granter_number integer,
        primary key (id)
    )

屏幕快照 2016-03-11 下午11.25.15

3.3 TABLE_PER_CLASS

使用这种策略,会对继承链中的每个类创建对应的表,父类的表中只有父类属性,子类的表中则包括了自身属性以及从父类继承过来的属性,而且子类表与父类表不在有任何关系。

但是使用这种策略有一个弊端,就是不能再将主键的生成策略设置为数据库自动生成(AUTO/IDENTITY/SEQUENCE),而只能使用GenerationType.TABLE来自动生成主键或者不使用自动生成主键。

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Odds {
    @Column(name = "id")
    @Id
    protected long id;
    
    protected long matchId;
    
    protected long updateTime;
    
    protected long bookmakerId;
    
    protected boolean active;
}

这样,我们将会得到三张无任何关系的表:

Hibernate: 
    create table Odds (
        id bigint not null,
        active bit not null,
        bookmakerId bigint not null,
        matchId bigint not null,
        updateTime bigint not null,
        primary key (id)
    )
Hibernate: 
    create table bs_odds_1x2 (
        id bigint not null,
        active bit not null,
        bookmakerId bigint not null,
        matchId bigint not null,
        updateTime bigint not null,
        away_odds double precision,
        draw_odds double precision,
        home_odds double precision,
        primary key (id)
    )
Hibernate: 
    create table bs_odds_handicap (
        id bigint not null,
        active bit not null,
        bookmakerId bigint not null,
        matchId bigint not null,
        updateTime bigint not null,
        away_odds double precision,
        granter char(1),
        granter_number integer,
        home_odds double precision,
        primary key (id)
    )

四、父类非实体类

三种介绍的例子,我们使用的父类都是实体类,即都用@Entity注解过。如果不使用@Entity注解父类的话,结果可想而知,实体的子类虽然依然可以继承父类属性,但这些属性将无法被映射到数据库表字段中。但从我现在的应用场景考虑,我就是需要将Odds作为非实体类,因为我不需要在数据库中有一张odds的父类表,但我又希望父类的属性能被写入到子类的表中。为了满足这种需求,jpa提供了@MappedSuperclass注解。

@MappedSuperclass
public class Odds {
    @Column(name = "id")
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    protected long id;
    
    protected long matchId;
    
    protected long updateTime;
    
    protected long bookmakerId;
    
    protected boolean active;
}
@Entity
@Table(name = "bs_odds_1x2")
public class Odds1x2 extends Odds {
    
    @Column(name = "home_odds")
    private double homeOdds;
    
    @Column(name = "draw_odds")
    private double drawOdds;
    
    @Column(name = "away_odds")
    private double awayOdds;
}
@Entity
@Table(name = "bs_odds_handicap")
public class OddsHandicap extends Odds {
    @Column(name = "granter")
    private char granter;
    
    @Column(name = "granter_number")
    private int granterNumber;
    
    @Column(name = "home_odds")
    private double homeOdds;
    
    @Column(name = "away_odds")
    private double awayOdds;
}

对于的hibernate输出为:

Hibernate: 
    drop table if exists bs_odds_1x2
Hibernate: 
    drop table if exists bs_odds_handicap
Hibernate: 
    create table bs_odds_1x2 (
        id bigint not null auto_increment,
        active bit not null,
        bookmakerId bigint not null,
        matchId bigint not null,
        updateTime bigint not null,
        away_odds double precision,
        draw_odds double precision,
        home_odds double precision,
        primary key (id)
    )
Hibernate: 
    create table bs_odds_handicap (
        id bigint not null auto_increment,
        active bit not null,
        bookmakerId bigint not null,
        matchId bigint not null,
        updateTime bigint not null,
        away_odds double precision,
        granter char(1),
        granter_number integer,
        home_odds double precision,
        primary key (id)
    )

来看一下自动创建出来的数据库表:

屏幕快照 2016-03-12 上午12.44.32 屏幕快照 2016-03-12 上午12.44.41

跟本文开头想要的一模一样(当然除了数据类型呀,因为在类定义中没有匹配好)

 

参考:

https://www.ibm.com/developerworks/cn/java/j-lo-hibernatejpa/

1 thought on “Entity的继承以及泛型Dao(一)”

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top