1. Table-per-class strategy
Table-per-class strategy is similar to joined-tables strategy because each entity in domain model gets its own table. One significant difference is no relationship between the tables, which doesn’t take advantage of power of relational database at all. Following the stock market example in join-table-strategy and single-table-strategy , we are there different tables namely product, bonds, shares but product_code, description, version properties are duplicated in each table. So if the data like product_code column needs to be unique, we should be careful once saving data with this strategy.
2. Stock market example
2.1 Project structure
Figure 1: Project structure for the financial product.
2.2 JPA configuration
The @DiscriminatorColumn and @DiscriminatorValue annotations are not used in this strategy
src/main/java/mapping/inheritance/domain/FinancialProduct.java
package mapping.inheritance.domain; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.Table; import javax.persistence.Version; @Entity @Table(name="product") @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public class FinancialProduct{ @Id @Column(name = "product_code") protected String productCode; @Column(name = "description") private String description; @Version protected long version; public FinancialProduct(){}; public FinancialProduct(String productCode, String description, long version) { super(); this.productCode = productCode; this.description = description; this.version = version; } public String getProductCode() { return productCode; } public void setProductCode(String productCode) { this.productCode = productCode; } public void setDescription(String description) { this.description = description; } public void setVersion(long version) { this.version = version; } @Override public String toString() { return this.productCode + " " + this.description + " " + this.version; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; FinancialProduct financialProduct = (FinancialProduct) o; if (version != financialProduct.version) return false; if (!productCode.equals(financialProduct.productCode)) return false; return true; } @Override public int hashCode() { int result = productCode.hashCode(); result = 31 * result + (int) (version ^ (version >>> 32)); return result; } }
src/main/java/mapping/inheritance/domain/Shares.java
package mapping.inheritance.domain; import javax.persistence.Entity; import javax.persistence.Table; @Entity @Table(name="shares") public class Shares extends FinancialProduct{ public Shares(String productCode, String description, long version) { super(productCode, description, version); } }
src/main/java/mapping/inheritance/domain/Bonds.java
package mapping.inheritance.domain; import javax.persistence.Entity; import javax.persistence.Table; @Entity @Table(name="bonds") public class Bonds extends FinancialProduct{ public Bonds(String productCode, String description, long version) { super(productCode, description, version); } }
2.3 Resource configuration
src/main/resources/db-changelog.xml
<?xml version="1.0" encoding="UTF-8"?> <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.3.xsd"> <changeSet id="1" author="Khiem.Truong"> <createTable tableName="product"> <column name="product_code" type="varchar(255)"> <constraints nullable="false" primaryKey="true" /> </column> <column name="description" type="varchar(255)" /> <column name="version" type="bigint" /> </createTable> <createTable tableName="bonds"> <column name="product_code" type="varchar(255)"> <constraints nullable="false" primaryKey="true"/> </column> <column name="description" type="varchar(255)" /> <column name="version" type="bigint" /> </createTable> <createTable tableName="shares"> <column name="product_code" type="varchar(255)"> <constraints nullable="false" primaryKey="true"/> </column> <column name="description" type="varchar(255)" /> <column name="version" type="bigint" /> </createTable> <loadData tableName="product" encoding="UTF-8" file="financial_product.csv" separator=";"> </loadData> </changeSet> </databaseChangeLog>
src/main/resources/financial_product.csv
product_code;description;version SH001;New York Stock Exchange;1 SH002;London Stock Exchange Group;1
2.4 Make the application executable
src/main/java/mapping/inheritance/service/Application.java
package mapping.inheritance.service; import java.util.List; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.orm.jpa.EntityScan; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import mapping.inheritance.domain.Bonds; import mapping.inheritance.domain.FinancialProduct; import mapping.inheritance.domain.Shares; @SpringBootApplication @ComponentScan(basePackages = { "mapping.inheritance.domain", "mapping.inheritance.service", "mapping.inheritance.mapper" }) @EnableJpaRepositories(value = { "mapping.inheritance.repository" }) @EntityScan(basePackageClasses = {FinancialProduct.class,Bonds.class,Shares.class}) public class Application { public static void main(String[] args) { ApplicationContext ctx = SpringApplication.run(Application.class, args); FinancialProductService shareService = (FinancialProductService) ctx.getBean("financialProductService"); FinancialProduct shares = new FinancialProduct("SH003", "NASDAQ Stock Exchange", 1); shareService.add(shares); shares = new FinancialProduct("SH004", "Japan Exchange Group - Tokyo", 1); shareService.add(shares); List<FinancialProduct> sharesList = shareService.findAll(); sharesList.forEach(s -> { System.out.println(s); }); } }
Output in console
SH001 New York Stock Exchange 1 SH002 London Stock Exchange Group 1 SH003 NASDAQ Stock Exchange 1 SH004 Japan Exchange Group - Tokyo 1
Because of there different tables in database without relational, the executable only saves data into product table as the Figure 2. Create new services to add new data for shares and bonds table if we want.
Figure 2: Tables in database.
Download Zip here (Build with Maven)
References:
- D. Pan, R. Rahman, R. Cuprak, M. Remijan, “Mapping inheritance”, In EJB3 in Action,pp.290-291, Manning, Second Edition, 2014