Problem with #UniqueConstraint with extends - java

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.

Related

Eclipselink JPA + #Mutitenant + #Inheritance Insert Op Failing - Inconsistent Behavior

#MappedSuperClass
public abstract class BaseMappedSuperClass {
#EmbeddedId
private EmbeddedId id;
}
#Entity
#Multitenant(MultitenantType.TABLE_PER_TENANT)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "typeCol")
public abstract class Vehicle extends BaseMappedSuperClass{
private String name;
}
#Entity(name = "Cycle")
#Multitenant(MultitenantType.TABLE_PER_TENANT)
#DiscriminatorValue(value = "Cycle")
public class Cycle extends Vehicle {
private String bellType;
}
#Entity(name = "Bus")
#Multitenant(MultitenantType.TABLE_PER_TENANT)
#DiscriminatorValue(value = "Bus")
public class Bus extends Vehicle {
private String gearType;
}
I have the above entity structure and if I try to do an insert op on the entity Cycle or Bus, it fails inconsistently, because of the missing primary key field (id).
When I tried to debug the JPA codebase, I figured that the tenant discriminator, which is tenant_id in my case is not appended to the table name prefix for the embeddedId field 'Id' and the discriminator column field 'typeCol'.
What is more interesting is that this behavior is not consistent. If I restart my application and try, it works. If I restart again and try,it does not work.
Any help would be appreciated. Version of eclipse link used is 2.5.1.
What is the logic behind the order in which the entities are processed to initialize the metadata?

Hibernate miss existing field

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.

JPA Entity inheritance with two DiscriminatorColumn

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 {
....
}

Hibernate nested discriminator on inherited classes

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.

Polymorphic query in spring data

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

Categories