I have 2 MYSQL tables
table1
id bigint auto increment Primary Key
type Enum ('vegetable','fruit')
color Enum ('green','red','yellow')
table2
id bigint (same as the id in Table 1)
sweet boolean
sour boolean
.. other fields specific to type fruit
Now I'm creating 3 objects, first the parent class
#Entity
#Configurable
#Table(name = "table1")
#DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
public class ParentClass {
..
}
Now the second class for vegetable
#Entity
#Configurable
#DiscriminatorValue("vegetable")
#DiscriminatorColumn(name = "color", discriminatorType = DiscriminatorType.STRING)
public class Vegetable extends Parent{
..
}
And thirdly, the fruit class
#Entity
#Configurable
#SecondaryTables({ #SecondaryTable(name = "table2",
pkJoinColumns = { #PrimaryKeyJoinColumn(name = "id", referencedColumnName = "id") }) })
#DiscriminatorValue("fruit")
#DiscriminatorColumn(name = "color", discriminatorType = DiscriminatorType.STRING)
public class Fruit extends Parent{
..
}
I'm needing the second discriminator to add further inherited classes (6 further classes) on Vegetable and Fruit like,
#Entity
#Configurable
#DiscriminatorValue("red")
public class RedVegetable extends Vegetable{
..
}
#Entity
#Configurable
#DiscriminatorValue("green")
public class GreenFruit extends Fruit{
..
}
and so on.
Hibernate isn't letting me do that. What's wrong with my design? Thanks in advance!
Learnt this cannot be done in Hibernate. So found an alternate way to do things by merging the discriminator friends in table1 like Enum ('fruit|red', 'fruit|green', 'vegetable|red'.. & so on).
Please correct me if I was wrong.
Related
Main class
#Entity
#Table(uniqueConstraints={#UniqueConstraint(columnNames={"codigoEAN", "tipo"})})
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "tipo")
#DiscriminatorValue("Produto")
public class Produto {
Class extending
#Entity
#DiscriminatorValue("Caixa")
public class ProdutosEmCaixa extends Produto {
What I wanted ?
That when it is a produto type "Produto" it does not repeat the keys (codigoEAN, tipo) but when it is of the type "Caixa" it can repeat the keys.
Something like
// That does not exist
#Table(uniqueConstraints={#UniqueConstraint(columnNames={"codigoEAN", "tipo=Produto"})})
Has anyone ever experienced this?
The problem is that you are using
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
This means that both entities will be mapped in a single table (using a discriminator column). You cannot define a unique constraint that only works for specific rows.
If the constraint only makes sense for a type of entity, you have to map them in separate tables.
With InheritanceType.TABLE_PER_CLASS:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Produto {
...
#Column(name="codigoEAN")
private String codigoEAN;
}
#Entity
#Table(uniqueConstraints={#UniqueConstraint(columnNames={"codigoEAN"})})
public class ProdutosEmCaixa extends Produto {
...
}
or InheritanceType.JOINED:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "tipo")
#DiscriminatorValue("Produto")
public class Produto {
...
}
#Entity
#Table(uniqueConstraints={#UniqueConstraint(columnNames={"codigoEAN"})})
#DiscriminatorValue("Caixa")
public class ProdutosEmCaixa extends Produto {
...
#Column(name="codigoEAN")
private String codigoEAN;
}
See the Hibernate ORM documentation for more details about mapping inheritance.
I have such entity structure
#MappedSuperclass
public abstract class Base {
UUID id;
}
#MappedSuperclass
public abstract class Parent<C extends Child> extends Base {
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<C> children;
}
#MappedSuperclass
public abstract class Child<P extends Parent> extends Base {
#JoinColumn(name = "parent_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private P parent;
}
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Table(name = "ap")
public class AP extends Parent<AC> {}
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Table(name = "ac")
public class AC extends Child<AP> {}
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Table(name = "bp")
public class BP extends Parent<BC> {}
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Table(name = "bc")
public class BC extends Child<BP> {}
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Table(name = "cp")
public class CP extends Parent<CC> {}
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Table(name = "cc")
public class CC extends Child<CP> {
String value;
}
I do criteria query
CriteriaQuery<Long> cq = getEntityManager().getCriteriaBuilder().createQuery(Long.class);
Root<CP> rt = cq.from(CP.class);
Path child = rt.join("children");
final CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
cq.select(criteriaBuilder.count(rt));
cq.where(criteriaBuilder.equal(child.get("value"), "exists"));
TypedQuery<Long> q = getEntityManager().createQuery(cq);
Long res = q.getSingleResult()
and get error on line with where clause:
java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [value] on this ManagedType [Base]
After debugging a while I've found out that Parent meta class in entity manager's meta model keeps property children as List of AC. AC obviously doesn't contains field "value".
Any idea how to fix this error?
I think there is no easy fix. Unless you are willing to move value to AC and make CC to extend AC<CP>. Or something alike.
This behavior is because of type erasure.
The actual generic type of children is not available at runtime because compiler casts list to generic type List<AC> based on the upper bound AC.
See also this great answer that explained this thing to me quite well.
After query it is possible to check and cast the list / items to actual type but not at all sure if it is possible within CriteriaQuery.
I need your help.
I have the following table of values that I want with JPA map.
I have considered the following class structure.
The classes "NotificationCustomShippingPlan" and "PreviewCustomShippingPlan" get everyone a DiscriminatorValue for DiscriminatorColumn "Type".
But the classes "ActivResultCustomShippingPlan" and "ActivResultCustomShippingPlan" should get other DiscriminatorColumn "SubType".
How can I so what map?
My feeling told me that it is not possible.
EDIT:
#Entity
#Inheritance
#DiscriminatorColumn(name = "TYPE")
#Table(name = "CC_CUSTOM_SHIPPING_PLAN")
public abstract class AbstractCustomShippingPlan {
....
....
}
#Entity
#DiscriminatorValue("NOTIFICATION")
public class NotificationCustomShippingPlan extends AbstractCustomShippingPlan
#Entity
#DiscriminatorValue("PREVIEW")
public class PreviewCustomShippingPlan extends AbstractCustomShippingPlan
#Entity
#DiscriminatorValue("RESULTS")
#DiscriminatorColumn(name = "SUBTYPE")
public abstract class ResultCustomShippingPlan extends AbstractCustomShippingPlan {
#Enumerated(EnumType.STRING)
#Column(name = "CC_CSP_SUBTYPE")
private ServiceType.Subtype subtype;
}
#Entity
#DiscriminatorValue("ACTIVE")
public class ActivResultCustomShippingPlan extends ResultCustomShippingPlan{
.....
}
#Entity
#DiscriminatorValue("REPORTED")
public class ReportedResultCustomShippingPlan extends ResultCustomShippingPlan {
....
}
I have one base abstract class.
#Entity
#Table(name = "P_FLD")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "FLD_DISCRIMINATOR", columnDefinition = "CHAR(3)")
abstract public class AbstractPassbookField
and some classes that extends it. For example:
#Entity
#DiscriminatorValue("F")
#Table(name = "P_FLD_F")
public class PassbookFileField extends AbstractPassbookField
and i create repository for base entity
public interface PassbookRepository extends CrudRepository<AbstractPassbookField, Long>
I'm running next test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:test-config.xml")
public class PassbookFieldRepositoryTest {
#Autowired
PassbookRepository passbookRepository;
#PersistenceContext
private EntityManager em;
#Test
public void testSave() {
PassbookFileField passbookFileField = new PassbookFileField();
passbookFileField.setFilename("text.test");
passbookFileField.setTemplate(true);
passbookFileField.setReadonly(true);
passbookFileField.setImageType(ImageType.I);
passbookFileField.setResoltuionType(ImageResolutionType.N);
passbookFileField = passbookRepository.save(passbookFileField);
passbookRepository.findAll();
}
}
passbookRepository.save(passbookFileField) - works well, but
passbookRepository.findAll() gives me an exception
org.springframework.orm.hibernate3.HibernateObjectRetrievalFailureException: Object [id=1] was not of the specified subclass [ru.teamlabs.moneybox.commons.model.passbook.field.AbstractPassbookField] : Discriminator: F ; nested exception is org.hibernate.WrongClassException: Object [id=1] was not of the specified subclass [ru.teamlabs.moneybox.commons.model.passbook.field.AbstractPassbookField] : Discriminator: F
Quering through entityManager gives me the same error. What I'm doing wrong?
You haven't given the DiscriminatorValue value for your Super Class thus when retrieving it can not distinguish Super and Sub Classes. Try the following, it must work.
#Entity
#Table(name = "P_FLD")
#DiscriminatorValue("SF")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "FLD_DISCRIMINATOR", columnDefinition = "CHAR(3)")
abstract public class AbstractPassbookField
I've found out why it was happening.
#DiscriminatorColumn(name = "FLD_DISCRIMINATOR", columnDefinition = "CHAR(3)")
This string was the problem.
In PassbookFileField i have
#DiscriminatorValue("F")
But repository expected to get entity with discriminator with 3 chars.
Such discriminator
#DiscriminatorValue("F")
or such discriminator column definition
#DiscriminatorColumn(name = "FLD_DISCRIMINATOR", columnDefinition = "CHARACTER VARYING(3)")
solves the problem
I have following situation:
Base class:
#Entity(name = "BaseEntity")
#Inheritance(strategy= InheritanceType.JOINED)
#DiscriminatorColumn(name="DISCR_COLUMN", discriminatorType = DiscriminatorType.STRING)
#Table(name = "base")
#DiscriminatorOptions(insert = true,force=true)
public abstract class Base implements Serializable {
Subclass:
#Entity(name = "SubclassEntity")
#DiscriminatorValue("A")
#Table(name="subclass")
#PrimaryKeyJoinColumn(name = "subclass_id", referencedColumnName = "base_id")
#DiscriminatorOptions(insert = true,force=true)
public class Subclass extends Base {
Repository:
public interface BaseRepository extends JpaRepository<Base, String>, JpaSpecificationExecutor<Base> {
The only problem I have is when I want to create a new Subclass
repository.saveAndFlush(Base base);
discriminator value is not saved and in DB appears NULL. I supposed it is set automatically by hibernate, isn't it ?
Attention: see comment below (!)
According to http://en.m.wikibooks.org/wiki/Java_Persistence/Inheritance section "Joined, Multiple Table Inheritance":
Hibernate does not support discriminator column for inheritance strategy: joined.