Hibernate继承注解
hibernate应用中,继承的用途或目的主要有两点:
- 组件化:故明思义,把重复性的代码抽取成组件,以便重用和维护。hibernate应用中,一些重复的字段,重复的映射配置,就需要抽取成组件。
- 多态性:类的多态性是指下层业务所需一个父类对象,而上层业务根据所需的父类对象,传递一个子类对象。hibernate应用中,下层业务操作父类对象进行持久操作,如增删改查,上层业务则传递一个子类对象。
所以,在应用hibernate的继承时,需要明确设计所需,即究竟是组件化需求,还是多态性需求。
- @MappedSuperclass:组件化需求的继承注解。虽然它可以应用于类的多态性业务中,但它不能应用于hibernate持久操作的多态性业务中。
- @Inheritance:多态性需求的继承注解。虽然它可以达到组件化的目的,但它要比@MappedSuperclass多负出一些代价。
@MappedSuperclass定义:
@Documented @Target({TYPE}) @Retention(RUNTIME) public @interface MappedSuperclass { }
@Inheritance定义:
@Target({TYPE}) @Retention(RUNTIME) public @interface Inheritance { InheritanceType strategy() default SINGLE_TABLE; }
InheritanceType定义:
public enum InheritanceType { SINGLE_TABLE, TABLE_PER_CLASS, JOINED }
从定义看出,@Inheritance的默认继承策略为SINGLE_TABLE,三种继承策略的区别在于:
- SINGLE_TABLE:公共属性公共表,独立属性公共表。
需要使用监别器区分具体的子类,注解@DiscriminatorColumn设置监别器列,注解@DiscriminatorValue设置监别器值。
子类的属性映射配置时,需要设置为允许为空或默认值。 - JOINED:公共属性公共表,独立属性独立表。
子类的独立表生成后,其主键是一个共享主键,意味着这是一对一的关联,默认名称与父类的主键一致,使用注解@PrimaryKeyJoinColumn可改变名称。 - TABLE_PER_CLASS:公共属性独立表,独立属性独立表。
主键生成策略不能使用GenerationType.IDENTITY。
对于子类而言,公共属性就是父类的属性,公共表就是父类对应的表,而独立属性就是自己定义的属性,独立表就是自己对应的表。
示例1:继承注解@MappedSuperclass
User.java
@MappedSuperclass public class User<ID extends Serializable> { //--------------------------------------------------------------- // Field //--------------------------------------------------------------- @Id @Column(name = "id") @GeneratedValue(strategy = GenerationType.IDENTITY) private ID id; @Column(name = "loginName", length = 20, unique = true, nullable = false, updatable = false) private String loginName; @Column(name = "loginPass", length = 20, nullable = false) private String loginPass; //--------------------------------------------------------------- // Method //--------------------------------------------------------------- public ID getId() { return id; } public void setId(ID id) { this.id = id; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getLoginPass() { return loginPass; } public void setLoginPass(String loginPass) { this.loginPass = loginPass; } }
Admin.java
@Entity @Table public class Admin extends User<Integer> { @Column(name = "role", length = 20, nullable = false) private String role; public String getRole() { return role; } public void setRole(String role) { this.role = role; } }
Member.java
@Entity @Table public class Member extends User<Long> { @Column(name = "name", length = 20, nullable = false) private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
test.java
public class Test { public static void main(String[] params){ // 继承注解:@MappedSuperclass new Test().test(); /* Hibernate: drop table if exists Admin Hibernate: drop table if exists Member Hibernate: create table Admin ( id integer not null auto_increment, loginName varchar(20) not null, loginPass varchar(20) not null, role varchar(20) not null, primary key (id) ) Hibernate: create table Member ( id bigint not null auto_increment, loginName varchar(20) not null, loginPass varchar(20) not null, name varchar(20) not null, primary key (id) ) Hibernate: alter table Admin add constraint UK_bdsh7mq6s6xad6ohm08u18uxr unique (loginName) Hibernate: alter table Member add constraint UK_jxtse8o1c6l89j39lilw5e2rs unique (loginName) Hibernate: insert into Admin (loginName, loginPass, role) values (?, ?, ?) Hibernate: select last_insert_id() Hibernate: drop table if exists Admin Hibernate: drop table if exists Member */ } public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml", this.getClass()); SessionFactory factory = null; boolean success = true; Session session = null; try { factory = (SessionFactory) context.getBean("sessionFactory"); session = factory.openSession(); success = false; session.beginTransaction(); User<?> user = new Admin(); user.setLoginName("loginName"); user.setLoginPass("loginPass"); ((Admin)user).setRole("role"); session.save(user); session.getTransaction().commit(); success = true; } finally { if(session != null){ if(!success) session.getTransaction().rollback(); session.close(); session = null; } if(factory != null){ factory.close(); factory = null; } } } }
示例2:继承注解@Inheritance,继承策略InheritanceType.SINGLE_TABLE。
User.java
@Entity @Table @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn( name = "type", discriminatorType = DiscriminatorType.STRING, length = 30) @DiscriminatorValue("User") public class User { //--------------------------------------------------------------- // Field //--------------------------------------------------------------- @Id @Column(name = "id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "loginName", length = 20, unique = true, nullable = false, updatable = false) private String loginName; @Column(name = "loginPass", length = 20, nullable = false) private String loginPass; //--------------------------------------------------------------- // Method //--------------------------------------------------------------- public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getLoginPass() { return loginPass; } public void setLoginPass(String loginPass) { this.loginPass = loginPass; } }
Admin.java
@Entity @DiscriminatorValue("Admin") public class Admin extends User { // 需要允许为空或设置默认值 @Column(name = "role", length = 20, nullable = true) private String role; public String getRole() { return role; } public void setRole(String role) { this.role = role; } }
Member.java
@Entity @DiscriminatorValue("Member") public class Member extends User { // 需要允许为空或设置默认值 @Column(name = "name", length = 20, nullable = false, columnDefinition="VARCHAR(20) DEFAULT 'admin'") private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
Test.java
public class Test { public static void main(String[] params){ // 继承注解:@Inheritance // 继承策略:InheritanceType.SINGLE_TABLE // 公共属性公共表,独立属性公共表 new Test().test(); /* Hibernate: drop table if exists User Hibernate: create table User ( type varchar(30) not null, id bigint not null auto_increment, loginName varchar(20) not null, loginPass varchar(20) not null, role VARCHAR(20) DEFAULT 'member' not null, name VARCHAR(20) DEFAULT 'admin' not null, primary key (id) ) Hibernate: alter table User add constraint UK_5iebj9qwxsnoshbaq6w834t0y unique (loginName) Hibernate: insert into User (loginName, loginPass, role, type) values (?, ?, ?, 'Admin') Hibernate: select last_insert_id() Hibernate: select user0_.id as id2_0_0_, user0_.loginName as loginNam3_0_0_, user0_.loginPass as loginPas4_0_0_, user0_.role as role5_0_0_, user0_.name as name6_0_0_, user0_.type as type1_0_0_ from User user0_ where user0_.id=? 多态性读取成功:study.t2.Admin@dc3aae Hibernate: drop table if exists User */ } public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml", this.getClass()); SessionFactory factory = null; try { factory = (SessionFactory) context.getBean("sessionFactory"); // 多态性添加 boolean success = true; Session session = null; Long id = null; try { session = factory.openSession(); success = false; session.beginTransaction(); User user = new Admin(); user.setLoginName("loginName"); user.setLoginPass("loginPass"); ((Admin)user).setRole("role"); session.save(user); session.getTransaction().commit(); id = user.getId(); success = true; } finally { if(session != null){ if(!success) session.getTransaction().rollback(); session.close(); session = null; } } // 多态性读取 if(success){ try { session = factory.openSession(); session.beginTransaction(); success = false; User user = session.load(User.class, id); System.out.println("多态性读取成功:" + user); session.getTransaction().commit(); success = true; } finally { if(session != null){ if(!success) session.getTransaction().rollback(); session.close(); session = null; } } } } finally { if(factory != null){ factory.close(); factory = null; } } } }
示例3:继承注解@Inheritance,继承策略InheritanceType.JOINED。
User.java
@Entity @Table @Inheritance(strategy = InheritanceType.JOINED) public class User { //--------------------------------------------------------------- // Field //--------------------------------------------------------------- @Id @Column(name = "id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "loginName", length = 20, unique = true, nullable = false, updatable = false) private String loginName; @Column(name = "loginPass", length = 20, nullable = false) private String loginPass; //--------------------------------------------------------------- // Method //--------------------------------------------------------------- public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getLoginPass() { return loginPass; } public void setLoginPass(String loginPass) { this.loginPass = loginPass; } }
Admin.java
@Entity @Table // 可选,设置共享主键的名称,默认的主键名称与父类一致。 @PrimaryKeyJoinColumn(name = "userId") public class Admin extends User { @Column(name = "role", length = 20, nullable = false) private String role; public String getRole() { return role; } public void setRole(String role) { this.role = role; } }
Member.java
@Entity @Table // 可选,设置共享主键的名称,默认的主键名称与父类一致。 @PrimaryKeyJoinColumn(name = "userId") public class Member extends User { @Column(name = "name", length = 20, nullable = false) private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
Test.java
public class Test { public static void main(String[] params){ // 继承注解:@Inheritance // 继承策略:InheritanceType.JOINED // 公共属性公共表,独立属性独立表 new Test().test(); /* Hibernate: alter table Admin drop foreign key FKjoav33p64suikub3369fpajy4 注:这里可能报一个异常,因为表Admin不存在! Hibernate: alter table Member drop foreign key FKnj6wlxlj0cc3993su1qaykn42 注:这里可能报一个异常,因为表Member不存在! Hibernate: drop table if exists Admin Hibernate: drop table if exists Member Hibernate: drop table if exists User Hibernate: create table Admin ( role varchar(20) not null, userId bigint not null, primary key (userId) ) Hibernate: create table Member ( name varchar(20) not null, userId bigint not null, primary key (userId) ) Hibernate: create table User ( id bigint not null auto_increment, loginName varchar(20) not null, loginPass varchar(20) not null, primary key (id) ) Hibernate: alter table User add constraint UK_5iebj9qwxsnoshbaq6w834t0y unique (loginName) Hibernate: alter table Admin add constraint FKjoav33p64suikub3369fpajy4 foreign key (userId) references User (id) Hibernate: alter table Member add constraint FKnj6wlxlj0cc3993su1qaykn42 foreign key (userId) references User (id) Hibernate: insert into User (loginName, loginPass) values (?, ?) Hibernate: select last_insert_id() Hibernate: insert into Admin (role, userId) values (?, ?) Hibernate: select user0_.id as id1_2_0_, user0_.loginName as loginNam2_2_0_, user0_.loginPass as loginPas3_2_0_, user0_1_.role as role1_0_0_, user0_2_.name as name1_1_0_, case when user0_1_.userId is not null then 1 when user0_2_.userId is not null then 2 when user0_.id is not null then 0 end as clazz_0_ from User user0_ left outer join Admin user0_1_ on user0_.id=user0_1_.userId left outer join Member user0_2_ on user0_.id=user0_2_.userId where user0_.id=? 多态性读取成功:study.t3.Admin@1f9407e Hibernate: alter table Admin drop foreign key FKjoav33p64suikub3369fpajy4 Hibernate: alter table Member drop foreign key FKnj6wlxlj0cc3993su1qaykn42 Hibernate: drop table if exists Admin Hibernate: drop table if exists Member Hibernate: drop table if exists User */ } public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml", this.getClass()); SessionFactory factory = null; try { factory = (SessionFactory) context.getBean("sessionFactory"); // 多态性添加 boolean success = true; Session session = null; Long id = null; try { session = factory.openSession(); success = false; session.beginTransaction(); User user = new Admin(); user.setLoginName("loginName"); user.setLoginPass("loginPass"); ((Admin)user).setRole("role"); session.save(user); session.getTransaction().commit(); id = user.getId(); success = true; } finally { if(session != null){ if(!success) session.getTransaction().rollback(); session.close(); session = null; } } // 多态性读取 if(success){ try { session = factory.openSession(); session.beginTransaction(); success = false; User user = session.load(User.class, id); System.out.println("多态性读取成功:" + user); session.getTransaction().commit(); success = true; } finally { if(session != null){ if(!success) session.getTransaction().rollback(); session.close(); session = null; } } } } finally { if(factory != null){ factory.close(); factory = null; } } } }
示例4:继承注解@Inheritance,继承策略InheritanceType.TABLE_PER_CLASS。
User.java
@Entity @Table @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public class User { //--------------------------------------------------------------- // Field //--------------------------------------------------------------- @Id @Column(name = "id") // 不能使用 GenerationType.IDENTITY,可以使用AUTO,或其它。 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="genId") @TableGenerator( name = "genId", table="GeneratorId", pkColumnName="genName", valueColumnName="genValue", pkColumnValue="nextUserId", allocationSize=1 ) private Long id; @Column(name = "loginName", length = 20, unique = true, nullable = false, updatable = false) private String loginName; @Column(name = "loginPass", length = 20, nullable = false) private String loginPass; //--------------------------------------------------------------- // Method //--------------------------------------------------------------- public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getLoginPass() { return loginPass; } public void setLoginPass(String loginPass) { this.loginPass = loginPass; } }
Admin.java
@Entity @Table public class Admin extends User { @Column(name = "role", length = 20, nullable = false) private String role; public String getRole() { return role; } public void setRole(String role) { this.role = role; } }
Member.java
@Entity @Table public class Member extends User { @Column(name = "name", length = 20, nullable = false) private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
Test.java
public class Test { public static void main(String[] params){ // 继承注解:@Inheritance // 继承策略:InheritanceType.TABLE_PER_CLASS // 公共属性独立表,独立属性独立表 new Test().test(); /* Hibernate: drop table if exists Admin Hibernate: drop table if exists GeneratorId Hibernate: drop table if exists Member Hibernate: drop table if exists User Hibernate: create table Admin ( id bigint not null, loginName varchar(20) not null, loginPass varchar(20) not null, role varchar(20) not null, primary key (id) ) Hibernate: create table GeneratorId ( genName varchar(255) not null, genValue bigint, primary key (genName) ) Hibernate: create table Member ( id bigint not null, loginName varchar(20) not null, loginPass varchar(20) not null, name varchar(20) not null, primary key (id) ) Hibernate: create table User ( id bigint not null, loginName varchar(20) not null, loginPass varchar(20) not null, primary key (id) ) Hibernate: alter table Admin add constraint UK_bdsh7mq6s6xad6ohm08u18uxr unique (loginName) Hibernate: alter table Member add constraint UK_jxtse8o1c6l89j39lilw5e2rs unique (loginName) Hibernate: alter table User add constraint UK_5iebj9qwxsnoshbaq6w834t0y unique (loginName) Hibernate: select tbl.genValue from GeneratorId tbl where tbl.genName=? for update Hibernate: insert into GeneratorId (genName, genValue) values (?,?) Hibernate: update GeneratorId set genValue=? where genValue=? and genName=? Hibernate: insert into Admin (loginName, loginPass, role, id) values (?, ?, ?, ?) Hibernate: select user0_.id as id1_2_0_, user0_.loginName as loginNam2_2_0_, user0_.loginPass as loginPas3_2_0_, user0_.role as role1_0_0_, user0_.name as name1_1_0_, user0_.clazz_ as clazz_0_ from ( select id, loginName, loginPass, null as role, null as name, 0 as clazz_ from User union select id, loginName, loginPass, role, null as name, 1 as clazz_ from Admin union select id, loginName, loginPass, null as role, name, 2 as clazz_ from Member ) user0_ where user0_.id=? 多态性读取成功:study.t4.Admin@12bf0e2 Hibernate: drop table if exists Admin Hibernate: drop table if exists GeneratorId Hibernate: drop table if exists Member Hibernate: drop table if exists User */ } public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml", this.getClass()); SessionFactory factory = null; try { factory = (SessionFactory) context.getBean("sessionFactory"); // 多态性添加 boolean success = true; Session session = null; Long id = null; try { session = factory.openSession(); success = false; session.beginTransaction(); User user = new Admin(); user.setLoginName("loginName"); user.setLoginPass("loginPass"); ((Admin)user).setRole("role"); session.save(user); session.getTransaction().commit(); id = user.getId(); success = true; } finally { if(session != null){ if(!success) session.getTransaction().rollback(); session.close(); session = null; } } // 多态性读取 if(success){ try { session = factory.openSession(); session.beginTransaction(); success = false; User user = session.load(User.class, id); System.out.println("多态性读取成功:" + user); session.getTransaction().commit(); success = true; } finally { if(session != null){ if(!success) session.getTransaction().rollback(); session.close(); session = null; } } } } finally { if(factory != null){ factory.close(); factory = null; } } } }