initial work for table per class inheritance

This commit is contained in:
Andrea Boriero 2019-10-24 11:28:44 +01:00
parent 2ea03f7d56
commit 47a7a0765c
5 changed files with 486 additions and 69 deletions

View File

@ -5482,8 +5482,6 @@ public abstract class AbstractEntityPersister
return accessOptimizer.getPropertyValues( entity );
}
final Collection<AttributeMapping> attributeMappings = getAttributeMappings();
final Object[] result = new Object[this.attributeMappings.size()];
for ( int i = 0; i < this.attributeMappings.size(); i++ ) {
result[i] = this.attributeMappings.get( i ).getPropertyAccess().getGetter().getForInsert(
@ -6102,7 +6100,7 @@ public abstract class AbstractEntityPersister
private List<AttributeMapping> attributeMappings;
private List<Fetchable> staticFetchableList;
private ReflectionOptimizer.AccessOptimizer accessOptimizer;
protected ReflectionOptimizer.AccessOptimizer accessOptimizer;
@Override
public void visitAttributeMappings(Consumer<AttributeMapping> action) {
@ -6142,17 +6140,7 @@ public abstract class AbstractEntityPersister
);
}
if ( getDiscriminatorType() == null && shouldProcessSuperMapping() ) {
discriminatorMapping = null;
}
else {
discriminatorMapping = new EntityDiscriminatorMappingImpl(
this,
getRootTableName(),
getDiscriminatorColumnName(),
(BasicType) getDiscriminatorType()
);
}
buildDiscriminatorMapping();
// todo (6.0) : support for natural-id not yet implemented
naturalIdMapping = null;
@ -6165,14 +6153,13 @@ public abstract class AbstractEntityPersister
.getEntityBinding( getEntityName() );
final EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel();
int stateArrayPosition = superMappingType == null ? 0 : superMappingType.getNumberOfAttributeMappings();
int stateArrayPosition = getStateArrayInitialPosition( creationProcess );
for ( int i = 0; i < currentEntityMetamodel.getPropertySpan(); i++ ) {
final NonIdentifierAttribute runtimeAttrDefinition = currentEntityMetamodel.getProperties()[i];
final Property bootProperty = bootEntityDescriptor.getProperty( runtimeAttrDefinition.getName() );
if ( superMappingType != null && superMappingType.findAttributeMapping( bootProperty.getName() ) != null && shouldProcessSuperMapping() ) {
if ( superMappingType != null && superMappingType.findAttributeMapping( bootProperty.getName() ) != null ) {
// its defined on the super-type, skip it here
}
else {
@ -6201,6 +6188,33 @@ public abstract class AbstractEntityPersister
}
}
protected int getStateArrayInitialPosition(MappingModelCreationProcess creationProcess) {
// todo (6.0) not sure this is correct in case of SingleTable Inheritance and for Table per class when the selection is the root
int stateArrayPosition;
if ( superMappingType != null ) {
( (InFlightEntityMappingType) superMappingType ).prepareMappingModel( creationProcess );
stateArrayPosition = superMappingType.getNumberOfAttributeMappings();
}
else {
stateArrayPosition = 0;
}
return stateArrayPosition;
}
protected void buildDiscriminatorMapping() {
if ( getDiscriminatorType() == null) {
discriminatorMapping = null;
}
else {
discriminatorMapping = new EntityDiscriminatorMappingImpl(
this,
getRootTableName(),
getDiscriminatorColumnName(),
(BasicType) getDiscriminatorType()
);
}
}
protected boolean shouldProcessSuperMapping(){
return true;
}
@ -6524,6 +6538,7 @@ public abstract class AbstractEntityPersister
EntityMappingType treatTargetType) {
if ( treatTargetType == null ) {
getStaticFetchableList().forEach( fetchableConsumer );
// staticFetchableList.forEach( fetchableConsumer );
// EARLY EXIT!!!
return;
}

View File

@ -21,9 +21,7 @@ import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.bytecode.spi.ReflectionOptimizer;
import org.hibernate.cache.spi.access.EntityDataAccess;
import org.hibernate.cache.spi.access.NaturalIdDataAccess;
import org.hibernate.cfg.Settings;
@ -40,30 +38,25 @@ import org.hibernate.internal.util.collections.JoinedIterator;
import org.hibernate.internal.util.collections.SingletonIterator;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.Table;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.internal.EntityDiscriminatorMappingImpl;
import org.hibernate.metamodel.mapping.internal.InFlightEntityMappingType;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMapping;
import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.sql.SqlExpressionResolver;
import org.hibernate.sql.SelectFragment;
import org.hibernate.sql.SimpleSelect;
import org.hibernate.sql.ast.JoinType;
import org.hibernate.sql.ast.spi.SqlAliasBase;
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.UnionTableGroup;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.tuple.NonIdentifierAttribute;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.type.BasicType;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/**
* Implementation of the "table-per-concrete-class" or "roll-down" mapping
@ -232,9 +225,30 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
initSubclassPropertyAliasesMap( persistentClass );
postConstruct( creationContext.getMetadata() );
}
@Override
public TableGroup createRootTableGroup(
NavigablePath navigablePath,
String explicitSourceAlias,
JoinType tableReferenceJoinType,
LockMode lockMode,
SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver,
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
SqlAstCreationContext creationContext) {
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
final TableReference tableReference = new TableReference(
getTableName(),
sqlAliasBase.generateNewAlias(),
false,
getFactory()
);
return new UnionTableGroup( navigablePath, tableReference, this );
}
@Override
public Serializable[] getQuerySpaces() {
return subclassSpaces;
}
@ -244,18 +258,27 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
return tableName;
}
@Override
public String getTableName() {
return subquery;
if ( hasSubclasses() ) {
return subquery;
}
else {
return tableName;
}
}
@Override
public Type getDiscriminatorType() {
return StandardBasicTypes.INTEGER;
}
@Override
public Object getDiscriminatorValue() {
return discriminatorValue;
}
@Override
public String getDiscriminatorSQLValue() {
return discriminatorSQLValue;
}
@ -264,10 +287,12 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
return subclassClosure;
}
@Override
public String getSubclassForDiscriminatorValue(Object value) {
return (String) subclassByDiscriminatorValue.get( value );
}
@Override
public Serializable[] getPropertySpaces() {
return spaces;
}
@ -276,37 +301,40 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
return false;
}
/**
* Generate the SQL that selects a row by id
*/
protected String generateSelectString(LockMode lockMode) {
SimpleSelect select = new SimpleSelect( getFactory().getDialect() )
.setLockMode( lockMode )
.setTableName( getTableName() )
.addColumns( getIdentifierColumnNames() )
.addColumns(
getSubclassColumnClosure(),
getSubclassColumnAliasClosure(),
getSubclassColumnLazyiness()
)
.addColumns(
getSubclassFormulaClosure(),
getSubclassFormulaAliasClosure(),
getSubclassFormulaLazyiness()
@Override
public void setPropertyValues(Object object, Object[] values) {
if ( accessOptimizer != null ) {
accessOptimizer.setPropertyValues( object, values );
}
else {
if ( hasSubclasses() ) {
visitAttributeMappings(
attribute -> {
final int stateArrayPosition = ( (StateArrayContributorMapping) attribute ).getStateArrayPosition();
final Object value = values[stateArrayPosition];
if ( value != UNFETCHED_PROPERTY ) {
final Setter setter = attribute.getPropertyAccess().getSetter();
setter.set( object, value, getFactory() );
}
}
);
//TODO: include the rowids!!!!
if ( hasSubclasses() ) {
if ( isDiscriminatorFormula() ) {
select.addColumn( getDiscriminatorFormula(), getDiscriminatorAlias() );
}
else {
select.addColumn( getDiscriminatorColumnName(), getDiscriminatorAlias() );
visitFetchables(
fetchable -> {
final AttributeMapping attribute = (AttributeMapping) fetchable;
final int stateArrayPosition = ( (StateArrayContributorMapping) attribute ).getStateArrayPosition();
final Object value = values[stateArrayPosition];
if ( value != UNFETCHED_PROPERTY ) {
final Setter setter = attribute.getPropertyAccess().getSetter();
setter.set( object, value, getFactory() );
}
},
null
);
}
}
if ( getFactory().getSettings().isCommentsEnabled() ) {
select.setComment( "load " + getEntityName() );
}
return select.addCondition( getIdentifierColumnNames(), "=?" ).toStatementString();
}
@Override
@ -318,24 +346,29 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
return null;
}
@Override
protected String getTableName(int j) {
return tableName;
}
@Override
protected String[] getKeyColumns(int j) {
return getIdentifierColumnNames();
}
@Override
protected boolean isTableCascadeDeleteEnabled(int j) {
return false;
}
@Override
protected boolean isPropertyOfTable(int property, int j) {
return true;
}
// Execute the SQL:
@Override
public String fromTableFragment(String name) {
return getTableName() + ' ' + name;
}
@ -352,39 +385,55 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
return filterFragment( alias );
}
@Override
public String getSubclassPropertyTableName(int i) {
return getTableName();//ie. the subquery! yuck!
}
@Override
protected void addDiscriminatorToSelect(SelectFragment select, String name, String suffix) {
select.addColumn( name, getDiscriminatorColumnName(), getDiscriminatorAlias() );
}
@Override
protected int[] getPropertyTableNumbersInSelect() {
return new int[getPropertySpan()];
}
@Override
protected int getSubclassPropertyTableNumber(int i) {
return 0;
}
@Override
public int getSubclassPropertyTableNumber(String propertyName) {
return 0;
}
@Override
public boolean isMultiTable() {
// This could also just be true all the time...
return isAbstract() || hasSubclasses();
}
@Override
protected void buildDiscriminatorMapping() {
if ( hasSubclasses() ) {
super.buildDiscriminatorMapping();
}
}
@Override
public int getTableSpan() {
return 1;
}
@Override
protected int[] getSubclassColumnTableNumberClosure() {
return new int[getSubclassColumnClosure().length];
}
@Override
protected int[] getSubclassFormulaTableNumberClosure() {
return new int[getSubclassFormulaClosure().length];
}
@ -393,6 +442,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
return new boolean[] {true};
}
@Override
protected int[] getPropertyTableNumbers() {
return new int[getPropertySpan()];
}
@ -472,6 +522,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
return buf.append( " )" ).toString();
}
@Override
protected String[] getSubclassTableKeyColumns(int j) {
if ( j != 0 ) {
throw new AssertionFailure( "only one table" );
@ -479,6 +530,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
return getIdentifierColumnNames();
}
@Override
public String getSubclassTableName(int j) {
if ( j != 0 ) {
throw new AssertionFailure( "only one table" );
@ -486,10 +538,12 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
return tableName;
}
@Override
public int getSubclassTableSpan() {
return 1;
}
@Override
protected boolean isClassOrSuperclassTable(int j) {
if ( j != 0 ) {
throw new AssertionFailure( "only one table" );
@ -503,10 +557,12 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
return getTableName();
}
@Override
public String[] getConstraintOrderedTableNameClosure() {
return constraintOrderedTableNames;
}
@Override
public String[][] getContraintOrderedTableKeyColumnClosure() {
return constraintOrderedKeyColumnNames;
}
@ -515,5 +571,4 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
public FilterAliasGenerator getFilterAliasGenerator(String rootAlias) {
return new StaticFilterAliasGenerator( rootAlias );
}
}

View File

@ -0,0 +1,110 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.sql.ast.tree.from;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.LockMode;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.persister.entity.UnionSubclassEntityPersister;
import org.hibernate.query.NavigablePath;
/**
* @author Andrea Boriero
*/
public class UnionTableGroup implements VirtualTableGroup {
private final NavigablePath navigablePath;
private final UnionSubclassEntityPersister modelPart;
private final TableReference tableReference;
public UnionTableGroup(
NavigablePath navigablePath,
TableReference tableReference,
UnionSubclassEntityPersister modelPart) {
this.navigablePath = navigablePath;
this.tableReference = tableReference;
this.modelPart = modelPart;
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
}
@Override
public String getGroupAlias() {
return null;
}
@Override
public ModelPartContainer getModelPart() {
return modelPart;
}
@Override
public LockMode getLockMode() {
return LockMode.NONE;
}
@Override
public Set<TableGroupJoin> getTableGroupJoins() {
return Collections.emptySet();
}
@Override
public boolean hasTableGroupJoins() {
return false;
}
@Override
public void setTableGroupJoins(Set<TableGroupJoin> joins) {
}
@Override
public void addTableGroupJoin(TableGroupJoin join) {
}
@Override
public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) {
}
@Override
public void applyAffectedTableNames(Consumer<String> nameCollector) {
}
@Override
public TableReference getPrimaryTableReference() {
return tableReference;
}
@Override
public List<TableReferenceJoin> getTableReferenceJoins() {
return Collections.emptyList();
}
@Override
public boolean isInnerJoinPossible() {
return false;
}
@Override
public TableReference resolveTableReference(
String tableExpression, Supplier<TableReference> creator) {
return tableReference;
}
@Override
public TableReference resolveTableReference(String tableExpression) {
return tableReference;
}
}

View File

@ -16,7 +16,6 @@ import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.UnionSubclassEntityPersister;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
@ -36,17 +35,17 @@ import static org.hamcrest.MatcherAssert.assertThat;
*/
@DomainModel(
annotatedClasses = {
TablePerClassInheritanceTest.Customer.class,
TablePerClassInheritanceTest.DomesticCustomer.class,
TablePerClassInheritanceTest.ForeignCustomer.class
TablePerClassInheritanceWithAbstractRootTest.Customer.class,
TablePerClassInheritanceWithAbstractRootTest.DomesticCustomer.class,
TablePerClassInheritanceWithAbstractRootTest.ForeignCustomer.class
}
)
@ServiceRegistry
@SessionFactory
@Tags({
@Tag("RunnableIdeTest"),
@Tag("RunnableIdeTest"),
})
public class TablePerClassInheritanceTest {
public class TablePerClassInheritanceWithAbstractRootTest {
@Test
public void basicTest(SessionFactoryScope scope) {
@ -80,7 +79,6 @@ public class TablePerClassInheritanceTest {
}
@Test
@FailureExpected
public void rootQueryExecutionTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
@ -114,7 +112,6 @@ public class TablePerClassInheritanceTest {
}
@Test
@FailureExpected
public void subclassQueryExecutionTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
@ -145,7 +142,7 @@ public class TablePerClassInheritanceTest {
);
}
// @BeforeEach
@BeforeEach
public void createTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
@ -155,7 +152,7 @@ public class TablePerClassInheritanceTest {
);
}
// @AfterEach
@AfterEach
public void cleanupTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {

View File

@ -0,0 +1,240 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.metamodel.mapping;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.UnionSubclassEntityPersister;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author Andrea Boriero
*/
@DomainModel(
annotatedClasses = {
TablePerClassInheritanceWithConcreteRootTest.Customer.class,
TablePerClassInheritanceWithConcreteRootTest.DomesticCustomer.class,
TablePerClassInheritanceWithConcreteRootTest.ForeignCustomer.class
}
)
@ServiceRegistry
@SessionFactory
public class TablePerClassInheritanceWithConcreteRootTest {
@Test
public void basicTest(SessionFactoryScope scope) {
final EntityPersister customerDescriptor = scope.getSessionFactory()
.getMetamodel()
.findEntityDescriptor( Customer.class );
final EntityPersister domesticCustomerDescriptor = scope.getSessionFactory()
.getMetamodel()
.findEntityDescriptor( DomesticCustomer.class );
final EntityPersister foreignCustomerDescriptor = scope.getSessionFactory()
.getMetamodel()
.findEntityDescriptor( ForeignCustomer.class );
assert customerDescriptor instanceof UnionSubclassEntityPersister;
assert customerDescriptor.isTypeOrSuperType( customerDescriptor );
assert !customerDescriptor.isTypeOrSuperType( domesticCustomerDescriptor );
assert !customerDescriptor.isTypeOrSuperType( foreignCustomerDescriptor );
assert domesticCustomerDescriptor instanceof UnionSubclassEntityPersister;
assert domesticCustomerDescriptor.isTypeOrSuperType( customerDescriptor );
assert domesticCustomerDescriptor.isTypeOrSuperType( domesticCustomerDescriptor );
assert !domesticCustomerDescriptor.isTypeOrSuperType( foreignCustomerDescriptor );
assert foreignCustomerDescriptor instanceof UnionSubclassEntityPersister;
assert foreignCustomerDescriptor.isTypeOrSuperType( customerDescriptor );
assert !foreignCustomerDescriptor.isTypeOrSuperType( domesticCustomerDescriptor );
assert foreignCustomerDescriptor.isTypeOrSuperType( foreignCustomerDescriptor );
}
@Test
public void rootQueryExecutionTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
{
// [name, taxId, vat]
final List<Customer> results = session.createQuery(
"select c from Customer c",
Customer.class
).list();
assertThat( results.size(), is( 3 ) );
for ( Customer result : results ) {
if ( result.getId() == 1 ) {
assertThat( result, instanceOf( DomesticCustomer.class ) );
final DomesticCustomer customer = (DomesticCustomer) result;
assertThat( customer.getName(), is( "domestic" ) );
assertThat( ( customer ).getTaxId(), is( "123" ) );
}
else if ( result.getId() == 2 ) {
assertThat( result.getId(), is( 2 ) );
final ForeignCustomer customer = (ForeignCustomer) result;
assertThat( customer.getName(), is( "foreign" ) );
assertThat( ( customer ).getVat(), is( "987" ) );
}
else {
assertThat( result.getId(), is( 3 ) );
final Customer customer = result;
assertThat( customer.getName(), is( "customer" ) );
}
}
}
}
);
}
@Test
public void subclassQueryExecutionTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
{
final DomesticCustomer result = session.createQuery(
"select c from DomesticCustomer c",
DomesticCustomer.class
).uniqueResult();
assertThat( result, notNullValue() );
assertThat( result.getId(), is( 1 ) );
assertThat( result.getName(), is( "domestic" ) );
assertThat( result.getTaxId(), is( "123" ) );
}
{
final ForeignCustomer result = session.createQuery(
"select c from ForeignCustomer c",
ForeignCustomer.class
).uniqueResult();
assertThat( result, notNullValue() );
assertThat( result.getId(), is( 2 ) );
assertThat( result.getName(), is( "foreign" ) );
assertThat( result.getVat(), is( "987" ) );
}
}
);
}
@BeforeEach
public void createTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.persist( new Customer( 3, "customer" ) );
session.persist( new DomesticCustomer( 1, "domestic", "123" ) );
session.persist( new ForeignCustomer( 2, "foreign", "987" ) );
}
);
}
@AfterEach
public void cleanupTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "from Customer", Customer.class ).list().forEach(
cust -> session.delete( cust )
);
}
);
}
@Entity(name = "Customer")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public static class Customer {
private Integer id;
private String name;
public Customer() {
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
@Id
public 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;
}
}
@Entity(name = "DomesticCustomer")
public static class DomesticCustomer extends Customer {
private String taxId;
public DomesticCustomer() {
}
public DomesticCustomer(Integer id, String name, String taxId) {
super( id, name );
this.taxId = taxId;
}
public String getTaxId() {
return taxId;
}
public void setTaxId(String taxId) {
this.taxId = taxId;
}
}
@Entity(name = "ForeignCustomer")
public static class ForeignCustomer extends Customer {
private String vat;
public ForeignCustomer() {
}
public ForeignCustomer(Integer id, String name, String vat) {
super( id, name );
this.vat = vat;
}
public String getVat() {
return vat;
}
public void setVat(String vat) {
this.vat = vat;
}
}
}