一、两张类似的表与两个类似的实体类
现在我有一个betspider数据库,里面放着我自己的爬虫爬来的足球赔率,以bs_odds_1x2(胜负平赔率)、bs_odds_handicap(让球赔率)这两张表作为例子,他们在数据库中的表结构是这样的:
首先不考虑实体类的继承,这两张表对应着两个独立的实体类:(请暂时忽略下面实体类属性的数据类型与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)
)
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)
)
来看一下自动创建出来的数据库表:
跟本文开头想要的一模一样(当然除了数据类型呀,因为在类定义中没有匹配好)
参考:
https://www.ibm.com/developerworks/cn/java/j-lo-hibernatejpa/





让球数grant_number写错了。。。写成了granter_number