I am having a problem with Hibernate inheritance. I have been trying to find out what is causing the Exception. The Exception I get is
Caused by: java.lang.ClassCastException: org.hibernate.mapping.SingleTableSubclass cannot be cast to org.hibernate.mapping.RootClass.
The full Exception can be found at the end of this post.
public interface PersistentObjectKey {
    public String getPromotionId();
    public Integer getVersion();
}
// Promo
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "promotion")
public abstract class Promo implements PersistentObjectKey{
    @Id
    @Column(name="promotionId")
    @GeneratedValue(strategy=GenerationType.AUTO)
    protected String promotionId;
    @OneToMany(mappedBy="promo", cascade = CascadeType.ALL, fetch=FetchType.LAZY)
    @NotNull
    protected Set<Product> product = new HashSet<Product>();
    @Embedded
    @NotNull
    protected Attribute attribute;
    @Embedded
    @NotNull
    protected Duration duration; 
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name="imageTitle", column=@Column(nullable=true)),
        @AttributeOverride(name="imageFileName", column=@Column(nullable=true)),
        @AttributeOverride(name="imageUrl", column=@Column(nullable=true))
    })
    private Image promoImage; 
    @Embedded
    @Column(nullable=true)
    protected TermsAndConditions termsAndConditions;
    protected Promo(){
    }
    public static class PromoBuilder{
        private Set<Product> product;
        private Attribute attributes;
        private Duration duration;
        private TermsAndConditions termsAndConditions;
        private Image promoImage;
        public PromoBuilder(Set<Product> product, Attribute attributes, Duration duration){
            this.product = product;
            this.attributes = attributes;
            this.duration = duration;
        }
        public PromoBuilder termsAndConditions(TermsAndConditions termsAndConditions){
            this.termsAndConditions = termsAndConditions;
            return this;
        }
        public PromoBuilder promoImage(Image promoImage){
            this.promoImage = promoImage;
            return this;
        }
        public Promo build(){
            return null;
        }
    }
    protected Promo(PromoBuilder promoBuilder){
        this.product = promoBuilder.product;
        this.attribute = promoBuilder.attributes;
        this.duration = promoBuilder.duration;
        this.termsAndConditions = promoBuilder.termsAndConditions;
        this.promoImage = promoBuilder.promoImage;
    } 
    protected PromoBuilder newBuilder(){
        return new PromoBuilder(getProduct(), getAttribute(), getDuration());
    }
    protected PromoBuilder decorate(PromoBuilder builder){
        return builder.termsAndConditions(getTermsAndConditions());
    }
    public PromoBuilder toBuilder(){
        return decorate(newBuilder());
    }
    public String getPromotionId() {
        return promotionId;
    }
    public Integer getVersion() {
        return 10;
    }
    public Set<Product> getProduct() {
        return product;
    }
    public void setProduct(Set<Product> product) {
        this.product = product;
    }
    public Attribute getAttribute() {
        return attribute;
    }
    public void setAttribute(Attribute attribute) {
        this.attribute = attribute;
    }
    public Duration getDuration() {
        return duration;
    }
    public void setDuration(Duration duration) {
        this.duration = duration;
    }
    public Image getPromoImage() {
        return promoImage;
    }
    public void setPromoImage(Image promoImage) {
        this.promoImage = promoImage;
    }
    public TermsAndConditions getTermsAndConditions() {
        return termsAndConditions;
    }
    public void setTermsAndConditions(TermsAndConditions termsAndConditions) {
        this.termsAndConditions = termsAndConditions;
    }
    public boolean equals(Object o){
        if(this == o){
            return true;
        }
        if(o == null || !(o instanceof PersistentObjectKey)){
            return false;
        }
        PersistentObjectKey other = (PersistentObjectKey)o;
        // if the id is missing, return false
        if(this.promotionId == null){
            return false;
        }
        return this.promotionId.equals(other.getPromotionId());
    }
    public int hashCode(){
        if(promotionId != null){
            return promotionId.hashCode();
        }else{
            return super.hashCode();
        }
    }
    public String toString() {
        return this.getClass().getName()
            + "[id=" + promotionId + "]";
    }
}
// Coupon
@Entity
@Table(name = "coupon")
@PrimaryKeyJoinColumn(name="couponId", referencedColumnName="promotionId")
public class Coupon extends Promo{
    @Column(name="onlineCode", nullable=true)
    @Pattern(regexp="[a-zA-Z0-9]+", message="LATER")  
    private String onlineCode = "";
    @Column(name="offlineCode",nullable=true)
    private String offlineCode = "";
    protected Coupon(){}
    private Coupon(CouponBuilder couponBuilder){
        super(couponBuilder);
        this.onlineCode= couponBuilder.onlineCode;
        this.offlineCode = couponBuilder.offlineCode;
    }
    public class CouponBuilder extends Promo.PromoBuilder{
        private String onlineCode = "";
        private String offlineCode = "";
        public CouponBuilder(Set<Product> product, Attribute attributes, Duration duration){
            super(product, attributes, duration);
        }
        public CouponBuilder onlineCode(String onlineCode){
            this.onlineCode = onlineCode;
            return this;
        }
        public CouponBuilder offlineCode(String offlineCode){
            this.offlineCode = offlineCode;
            return this;
        }
        @Override
        public CouponBuilder termsAndConditions(TermsAndConditions termsAndConditions){
            return (CouponBuilder) super.termsAndConditions(termsAndConditions);
        }
        @Override
        public CouponBuilder promoImage(Image promoImage){
            return (CouponBuilder)super.promoImage(promoImage);
        }
        public Coupon build(){
            switch(attribute.getPromoUsage()){
            case ONLINE:
                if(this.onlineCode.equalsIgnoreCase("")){
                    throw new IllegalArgumentException("You must enter online coupon code");
                }
            case OFFLINE:
                if(this.offlineCode.equalsIgnoreCase("")){
                    throw new IllegalArgumentException("You must enter instore coupon code");
                }
            case ONLINE_AND_OFFLINE:
                if(this.onlineCode.equalsIgnoreCase("") || this.offlineCode.equalsIgnoreCase("")){
                    throw new IllegalArgumentException("You must enter online and instore code");
                }
            }
            return new Coupon(this);
        }
    }
    // GETTERS AND SETTERS
    public String getOnlineCode() {
        return onlineCode;
    }
    public void setOnlineCode(String onlineCode) throws IllegalArgumentException{
        if(onlineCode.equals("")){
            throw new IllegalArgumentException("code cannot be empty");
        }
        this.onlineCode = onlineCode;
    }
    public String getOfflineCode() {
        return offlineCode;
    }
    public void setOfflineCode(String offlineCode) throws IllegalArgumentException{
        if(offlineCode.equals("")){
            throw new IllegalArgumentException("code cannot be empty");
        }
        this.offlineCode = offlineCode;
    }
}
// Product
@Entity
@Table(name="product")
public class Product {
    @Id
    @Column(name="productId")
    private String productId = IdGenerator.createId(); 
    @Column(name="productName", nullable=false)
    @NotNull
    private String name;
    @OneToOne(cascade=CascadeType.ALL)
    @JoinColumn(name="categoryId", nullable=false) 
    @NotNull
    private Category category;
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="promotionId", nullable=true)
    @JsonBackReference
    private Promo promo;
    @Column(name="price", nullable=true)
    private double price;
    @Column(name="discountPercentage", nullable=false)
    private int discountPercentage;
    @ElementCollection
    @CollectionTable(
            name="image",
            joinColumns = @JoinColumn(name="productId"))
    private Set<Image> images = new HashSet<Image>(); 
    public Product(){
    }
    private Product(ProductBuilder productBuilder) {
        this.name = productBuilder.name;
        this.category = productBuilder.category;
        this.price = productBuilder.price;
        this.discountPercentage = productBuilder.discountPercentage;
        this.images = productBuilder.images;
    }
    public static class ProductBuilder{ 
        private String name;
        private Category category;
        private double price = 0f;
        private int discountPercentage = 0;
        @OneToMany 
        private Set<Image> images = new HashSet<>(); 
        public ProductBuilder(String name, Category category){
            this.name = name;
            this.category = category;
        }
        public ProductBuilder price(double price){
            this.price = price;
            return this;
        }
        public ProductBuilder discountPercentage(int discountPercentage){
            this.discountPercentage = discountPercentage;
            return this;
        }
        public ProductBuilder images(Set<Image> images){
            this.images = images;
            return this;
        }
        public Product build(){
            return new Product(this);
        }
    }
    // GETTERS AND SETTERS
    public String getProductId() {
        return productId;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    public double getDiscountPercentage() {
        return discountPercentage;
    }
    public void setDiscountPercentage(int discountPercentage) throws IllegalArgumentException{
        if(discountPercentage <= 0){ 
            throw new IllegalArgumentException("Discount percentage cannot be zero or negative");
        }
        this.discountPercentage = discountPercentage;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Category getCategory() {
        return category;
    }
    public void setCategory(Category category) {
        this.category = category;
    }
    public Set<Image> getImages() {
        return images;
    }
    public void setImages(Set<Image> images) {
        this.images = images;
    }
    @Override
    public boolean equals(Object o){
        if(this == o){
            return true;
        }
        if(o == null || !(getClass() == o.getClass())){
            return false;
        }
        Product other = new Product();
        return Objects.equals(name, other.name) && Objects.equals(category,other.category) && 
                Objects.equals(price, other.price) && Objects.equals(discountPercentage, other.discountPercentage) &&
                Objects.equals(images, other.images);
    }
    public int hashCode(){
        if(productId != null){
            return productId.hashCode();
        }else{
            return super.hashCode();
        }
    }
}
// Attributes
@Embeddable
public class Attribute {
    @Column(name="shortDescription", nullable=false)
    @NotNull
    protected String shortDescription;
    @Column(name="detailDescription", nullable=true)
    protected String detailDescription;
    @Column(name="promoUsage", nullable=false) 
    @NotNull
    @Enumerated(EnumType.STRING)
    private PromoUsage promoUsage;  
    @Column(name="destinationUrl", nullable=true)
    protected String destinationUrl;
    public Attribute(){}
    private Attribute(AttributesBuilder attributesBuilder){
        this.shortDescription = attributesBuilder.shortDescription;
        this.destinationUrl = attributesBuilder.destinationUrl;
        this.promoUsage = attributesBuilder.promoUsageType;
        this.detailDescription = attributesBuilder.detailDescription;
    }
    public static class AttributesBuilder{
        private String shortDescription;
        private String detailDescription;
        private PromoUsage promoUsageType;
        private String destinationUrl;
        //Builder constructor
        public AttributesBuilder(String shortDescription, PromoUsage promoUsageType){
            this.shortDescription = shortDescription;
            this.promoUsageType = promoUsageType;
        }
        public AttributesBuilder detailDescription(String detailDescription){
            this.detailDescription = detailDescription;
            return this;
        }
        public AttributesBuilder destinationUrl(String destinationurl){
            this.destinationUrl = destinationurl;
            return this;
        }
        public Attribute build()throws IllegalArgumentException{
            if(this.promoUsageType == PromoUsage.ONLINE || this.promoUsageType == PromoUsage.ONLINE_AND_OFFLINE){
                if(destinationUrl.equals("")){ 
                    throw new IllegalArgumentException("destination Url must not be empty");
                }
            }
            if(this.shortDescription == null || this.shortDescription.equals("")){
                throw new IllegalArgumentException("Please provide a short description for the promo");
            }
            return new Attribute(this);
        }
    }
    public String getShortDescription() {
        return shortDescription;
    }
    public void setShortDescription(String shortDescription) {
        this.shortDescription = shortDescription;
    }
    public String getDetailDescription() {
        return detailDescription;
    }
    public void setDetailDescription(String detailDescription) {
        this.detailDescription = detailDescription;
    }
    public PromoUsage getPromoUsage() {
        return promoUsage;
    }
    public void setPromoUsage(PromoUsage promoUsage) {
        this.promoUsage = promoUsage;
    }
    public String getDestinationUrl() {
        return destinationUrl;
    }
    public void setDestinationUrl(String destinationUrl) {
        this.destinationUrl = destinationUrl;
    }
    public boolean equals(Object o){
        return false;
    } 
    public int hashCode(){
        return 1;
    }
}
// Duration
@Embeddable
public class Duration {
    @Column(name="startDate", nullable=false)
    @NotNull
    @JsonFormat(pattern = "dd-MM-YYYY")
    private Date startDate;
    @Column(name="expirationDate", nullable=false)
    @NotNull
    @JsonFormat(pattern = "dd-MM-YYYY")
    private Date expirationDate;
    public Duration(){
    }
    public Duration(Date startDate, Date expirationDate){
        this.startDate = startDate;
        this.expirationDate = expirationDate;
    }
    public Date getStartDate() {
        return startDate;
    }
    public void setStartDate(Date startDate) {
        this.startDate = startDate;
    }
    public Date getExpirationDate() {
        return expirationDate;
    }
    public void setExpirationDate(Date expirationDate) {
        this.expirationDate = expirationDate;
    }
    public boolean equals(Object o){
        return false;
    } 
    public int hashCode(){
        return 1;
    }
}
// Category
@Entity
@Table(name="category")
public class Category {
    @Id
    @Column(name="categoryId")
    private String categoryId = IdGenerator.createId();
    @Column(nullable=false, unique=true)
    @Size(min=2, max=60, message="LATER")
    private String categoryName;
    public Category() {
    }
    public String getCategoryId() {
        return categoryId;
    }
    public String getCategoryName() {
        return categoryName;
    }
    public void setCategoryName(String categoryName) {
        this.categoryName = categoryName;
    }
}
// Image
@Entity
public class Image extends MediaFile{
    public Image(){}
    public Image(String title, String fileName, String url) {
        super(title, fileName, url);
    }
}
public enum PromoUsage {
    ONLINE, OFFLINE, ONLINE_AND_OFFLINE;
}
// The exception: (because of character limit on Stack Overflow I can only post part of it)
    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [com/grabone/utility/GrabOneConfiguration.class]: Invocation of init method failed; nested exception is java.lang.ClassCastException: org.hibernate.mapping.SingleTableSubclass cannot be cast to org.hibernate.mapping.RootClass
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    ... 28 common frames omitted
Caused by: java.lang.ClassCastException: org.hibernate.mapping.SingleTableSubclass cannot be cast to org.hibernate.mapping.RootClass
    at org.hibernate.cfg.annotations.PropertyBinder.bind(PropertyBinder.java:208) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.cfg.annotations.PropertyBinder.makePropertyValueAndBind(PropertyBinder.java:199) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:2225) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.cfg.AnnotationBinder.fillComponent(AnnotationBinder.java:2635) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.cfg.AnnotationBinder.fillComponent(AnnotationBinder.java:2522) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.cfg.AnnotationBinder.bindComponent(AnnotationBinder.java:2470) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:2185) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:911) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:738) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:245) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:222) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:265) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:847) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:874) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60) ~[spring-orm-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:353) ~[spring-orm-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:370) ~[spring-orm-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:359) ~[spring-orm-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    ... 38 common frames omitted