根類一張表,將所有子類的屬性都合并到根類中,此外還需要一個欄位用於區分子類的類型。
company:
package com.persia.singletable;import java.io.Serializable;import java.util.HashSet;import java.util.Set;import javax.persistence.CascadeType;import javax.persistence.Entity;import javax.persistence.FetchType;import javax.persistence.GeneratedValue;import javax.persistence.Id;import javax.persistence.OneToMany;import javax.persistence.OrderBy;import javax.persistence.Table;@Entity@Table(name="company")public class Company implements Serializable { private Integer id;private String name;private Set<Employee> employees=new HashSet<Employee>();@Id@GeneratedValuepublic Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@OneToMany(mappedBy="company",cascade=CascadeType.ALL,fetch=FetchType.EAGER)@OrderBy(value="id asc")public Set<Employee> getEmployees() {return employees;}public void setEmployees(Set<Employee> employees) {this.employees = employees;}public void addEmployee(Employee em){if(!this.employees.contains(em)){this.employees.add(em);em.setCompany(this);}}public void removeEmployee(Employee em){em.setCompany(null);this.employees.remove(em);}}
Employee:
package com.persia.singletable;import java.io.Serializable;import javax.persistence.CascadeType;import javax.persistence.DiscriminatorColumn;import javax.persistence.DiscriminatorType;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;import javax.persistence.Inheritance;import javax.persistence.InheritanceType;import javax.persistence.ManyToOne;import javax.persistence.Table;import javax.persistence.JoinColumn;@Entity@Table(name="employee")@Inheritance(strategy=InheritanceType.SINGLE_TABLE)@DiscriminatorColumn(name="Discriminator",discriminatorType=DiscriminatorType.STRING,length=30)public class Employee implements Serializable {private Integer id;private String name;private Company company;@Id@GeneratedValuepublic Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@ManyToOne(cascade=CascadeType.PERSIST,optional=false)@JoinColumn(name="company_id")public Company getCompany() {return company;}public void setCompany(Company company) {this.company = company;}}
繼承類:HourEmployee:
package com.persia.singletable;import java.io.Serializable;import javax.persistence.Column;import javax.persistence.DiscriminatorValue;import javax.persistence.Entity;@Entity@DiscriminatorValue("rate")public class HourEmployee extends Employee { private Float rate; @Column(nullable=true)public Float getRate() {return rate;}public void setRate(Float rate) {this.rate = rate;} }
SalaryEmployee:
package com.persia.singletable;import java.io.Serializable;import javax.persistence.Column;import javax.persistence.DiscriminatorValue;import javax.persistence.Entity;@Entity@DiscriminatorValue("salary")public class SalaryEmployee extends Employee {private Float salary;@Column(nullable=true)public Float getSalary() {return salary;}public void setSalary(Float salary) {this.salary = salary;}}
dao:
package com.persia.singletable;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.Set;import javax.ejb.Remote;import javax.ejb.Stateless;import javax.persistence.EntityManager;import javax.persistence.PersistenceContext;import javax.persistence.Query;@Stateless@Remote({EmployeeDAO.class})public class EmployeeDAOBean implements EmployeeDAO {@PersistenceContextprivate EntityManager em;@Overridepublic void deleteCompany(Integer companyId) {Company c=em.find(Company.class, companyId);em.remove(c);}@Overridepublic void deleteEmployee(Integer employeeId) {Employee e=em.find(Employee.class, employeeId);e.getCompany().getEmployees().remove(e);e.setCompany(null);em.remove(e);}@Overridepublic List getAllCompany() {Query q=em.createQuery("from Company");return q.getResultList();}@Overridepublic void insertCompany() {Company c=new Company();c.setName("waltmart");SalaryEmployee se=new SalaryEmployee();se.setCompany(c);se.setName("salary-a");se.setSalary(9999.0f);HourEmployee he=new HourEmployee();he.setCompany(c);he.setName("hour-b");he.setRate(888.0f);Set set=new HashSet();set.add(se);set.add(he);c.setEmployees(set);em.persist(c);}@Overridepublic void updateHourEmployee(Integer companyId, String name, float rate) {Company c=em.find(Company.class, companyId);Set set=c.getEmployees();Iterator it=set.iterator();while(it.hasNext()){Employee e=(Employee)it.next();if(e instanceof HourEmployee){if(e.getName().equals(name))((HourEmployee)e).setRate(rate);}}}}
用戶端:
package com.persia.singletabletest;import java.util.Iterator;import java.util.List;import java.util.Properties;import javax.naming.InitialContext;import javax.naming.NamingException;import com.persia.singletable.Company;import com.persia.singletable.Employee;import com.persia.singletable.EmployeeDAO;import com.persia.singletable.HourEmployee;import com.persia.singletable.SalaryEmployee;public class TestSingleTable {/** * @param args * @throws NamingException */public static void main(String[] args) throws NamingException {Properties props=new Properties(); props.setProperty("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory"); props.setProperty("java.naming.provider.url","localhost:1099"); props.setProperty("java.naming.factory.url.pkgs","org.jboss.naming");InitialContext context=new InitialContext(props);try{EmployeeDAO edao=(EmployeeDAO)context.lookup("EmployeeDAOBean/remote");edao.insertCompany();//edao.deleteCompany(new Integer(1));//edao.deleteEmployee(new Integer(1));//edao.updateHourEmployee(new Integer(1), "hour-b", 95.0f);List ls=edao.getAllCompany();if(ls!=null){for(int i=0;i<ls.size();i++){Company c=(Company)ls.get(i);if(c!=null){System.out.println("=====公司:"+c.getName()+"=====");Iterator it=c.getEmployees().iterator();while(it.hasNext()){Employee e=(Employee)it.next();if(e instanceof HourEmployee)System.out.println("臨時工:"+((HourEmployee)e).getName()+" 薪水:"+((HourEmployee)e).getRate());elseSystem.out.println("正式員工:"+((SalaryEmployee)e).getName()+" 薪水:"+((SalaryEmployee)e).getSalary());}}}}}catch(Exception e){e.printStackTrace();}}}
運行結果:
=====公司:waltmart=====正式員工:salary-a 薪水:9999.0臨時工:hour-b 薪水:888.0
資料庫:
mysql> desc company;+-------+--------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+-------+--------------+------+-----+---------+----------------+| id | int(11) | NO | PRI | NULL | auto_increment || name | varchar(255) | YES | | NULL | |+-------+--------------+------+-----+---------+----------------+2 rows in set (0.03 sec)mysql> desc employee;+---------------+--------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+---------------+--------------+------+-----+---------+----------------+| Discriminator | varchar(30) | NO | | NULL | || id | int(11) | NO | PRI | NULL | auto_increment || name | varchar(255) | YES | | NULL | || rate | float | YES | | NULL | || salary | float | YES | | NULL | || company_id | int(11) | NO | MUL | NULL | |+---------------+--------------+------+-----+---------+----------------+6 rows in set (0.02 sec)mysql> select * from company;+----+----------+| id | name |+----+----------+| 1 | waltmart |+----+----------+1 row in set (0.00 sec)mysql> select * from employee;+---------------+----+----------+------+--------+------------+| Discriminator | id | name | rate | salary | company_id |+---------------+----+----------+------+--------+------------+| rate | 1 | hour-b | 888 | NULL | 1 || salary | 2 | salary-a | NULL | 9999 | 1 |+---------------+----+----------+------+--------+------------+2 rows in set (0.00 sec)
進行刪除和修改:
修改
=====公司:waltmart=====臨時工:hour-b 薪水:95.0正式員工:salary-a 薪水:9999.0
刪除company後:
mysql> select * from employee;Empty set (0.00 sec)mysql> select * from company;Empty set (0.00 sec)
若刪除SalaryEmployee:
=====公司:waltmart=====臨時工:hour-b 薪水:888.0
mysql> select * from employee;+---------------+----+--------+------+--------+------------+| Discriminator | id | name | rate | salary | company_id |+---------------+----+--------+------+--------+------------+| rate | 2 | hour-b | 888 | NULL | 1 |+---------------+----+--------+------+--------+------------+1 row in set (0.00 sec)mysql> select * from company;+----+----------+| id | name |+----+----------+| 1 | waltmart |+----+----------+1 row in set (0.00 sec)
------------------------------------------------------------------------------------------------------------------------------------------
SINGLE_TABLE的映射策略在所有繼承策略中是最簡單的,同時也是執行效率最高的,它僅僅需要對一個表進行管理和操作。在載入entity和多態串連時不需要進行任何關聯,聯合或子查詢,因為所有的資料都儲存在一個表中。
缺點是:
1.不能為所有子類的屬性對應的欄位定義not null 約束
2.在子類屬性不是非常多的時候優先選擇。(查詢效能高)