Hibernate miss existing field - java

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.

Related

Problem with #UniqueConstraint with extends

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.

map "table per class hierarchy" superclass having subclasses mapping sub classes of another "table per class hierarchy"

Sorry for long title..
I have the following error when querying a TableColumn entity:
Could not set field value
[org.comp.domain.data.ConstantParameterType#18c81fe5] value by
reflection : [class org.comp.data.AnalogParameter.analogParameterType]
setter of org.comp.data.AnalogParameter.analogParameterType; nested
exception is org.hibernate.PropertyAccessException: Could not set
field value [org.comp.data.ConstantParameterType#18c81fe5] value by
reflection : [class
org.comp.domain.data.AnalogParameter.analogParameterType] setter of
org.comp.domain.data.AnalogParameter.analogParameterType
My model contains two distincts 'single table per class' hierarchies having Parameter and ParameterType as superclasses. Each subclasse of Parameter hierarchy is mapped with a subclass of ParameterType hierarchy through #ManyToOne associations.
Here is an extract of my model with involved entities (unrelated fields ommitted):
// `Parameter` Single Table Per Class hierarchy
#Entity
#Table(name="parameters")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "category", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorOptions(force=true)
public abstract class Parameter {
}
#Entity
#DiscriminatorValue(value="analog")
public class AnalogParameter extends Parameter {
#ManyToOne
#JoinColumn(name="parameter_type_id")
private AnalogParameterType analogParameterType;
public AnalogParameterType getAnalogParameterType() {
return analogParameterType;
}
public void setAnalogParameterType(AnalogParameterType analogParameterType) {
this.analogParameterType = analogParameterType;
}
}
#Entity
#DiscriminatorValue(value="constant")
public class ConstantParameter extends Parameter {
#ManyToOne
#JoinColumn(name="parameter_type_id")
private ConstantParameterType constantParameterType;
public ConstantParameterType getConstantParameterType() {
return constantParameterType;
}
public void setConstantParameterType(ConstantParameterType constantParameterType) {
this.constantParameterType = constantParameterType;
}
}
// `ParameterType` Single Table Per Class hierarchy
#Entity
#Table(name="parameters_types")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "category", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorOptions(force=true)
public abstract class ParameterType { }
#Entity
#DiscriminatorValue(value="analog")
public class AnalogParameterType extends ParameterType { }
#Entity
#DiscriminatorValue(value="constant")
public class ConstantParameterType extends ParameterType {
}
Here is the TableColumn which is mapped with Parameter superclass through a #ManyToOne association:
#Entity
#Table(name="tables_columns")
public class TableColumn {
#ManyToOne
#JoinColumn(name="parameter_id")
private Parameter parameter;
public Parameter getParameter() {
return parameter;
}
}
And here is the generated SQL when querying A TableColumn Entity:
select tablecolum0_.id as id1_12_0_, tablecolum0_.created_at as
created_2_12_0_, tablecolum0_.is_optional as is_optio3_12_0_,
tablecolum0_.parameter_id as paramete6_12_0_, tablecolum0_.position as
position4_12_0_, tablecolum0_.updated_at as updated_5_12_0_,
parameter1_.id as id2_8_1_, parameter1_.category as category1_8_1_,
parameter1_.created_at as created_3_8_1_, parameter1_.device_id as
device_14_8_1_, parameter1_.effective_date as effectiv4_8_1_,
parameter1_.expiry_date as expiry_d5_8_1_, parameter1_.fqn as
fqn6_8_1_, parameter1_.height_from_the_ground as height_f7_8_1_,
parameter1_.label as label8_8_1_, parameter1_.name as name9_8_1_,
parameter1_.resolution_label as resolut10_8_1_, parameter1_.updated_at
as updated11_8_1_, parameter1_.parameter_type_id as paramet15_8_1_,
parameter1_.data_validity_period as data_va12_8_1_,
parameter1_.resolution as resolut13_8_1_, device2_.id as id1_1_2_,
device2_.created_at as created_2_1_2_, device2_.device_type_id as
device_t8_1_2_, device2_.fqn as fqn3_1_2_, device2_.label as
label4_1_2_, device2_.name as name5_1_2_, device2_.notes as
notes6_1_2_, device2_.parent_device_id as parent_d9_1_2_,
device2_.plant_id as plant_i10_1_2_, device2_.updated_at as
updated_7_1_2_, constantpa3_.id as id2_9_3_, constantpa3_.created_at
as created_3_9_3_, constantpa3_.description as descript4_9_3_,
constantpa3_.is_signed as is_signe5_9_3_, constantpa3_.label as
label6_9_3_, constantpa3_.name as name7_9_3_, constantpa3_.updated_at
as updated_8_9_3_ from tables_columns tablecolum0_ left outer join
parameters parameter1_ on tablecolum0_.parameter_id=parameter1_.id
left outer join devices device2_ on parameter1_.device_id=device2_.id
left outer join parameters_types constantpa3_ on
parameter1_.parameter_type_id=constantpa3_.id where tablecolum0_.id=1
I'm using Hibernate 5.0.11 with MySQL in a Spring Boot 1.4.1 / Data Rest project
EDIT
I tried in a vanilla Maven/Hibernate project using the same database. I have the same error. If i query Parameter objects directly, it's OK, but i get the error if i query TableColumn:
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory( "org.hibernate.tutorial.jpa" );
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
// The following works
List<Parameter> ps = entityManager.createQuery("from Parameter", Parameter.class).getResultList();
for (Parameter p: ps) {
System.out.println(p);
}
// But if i replace with following, i get the same error as reported
// in the beginning of this question
List<TableColumn> tcs = entityManager.createQuery("from TableColumn", TableColumn.class).getResultList();
for (TableColumn tc: tcs) {
System.out.println(tc);
}
entityManager.getTransaction().commit();
entityManager.close();
1) Your category is of size=7, but you are setting a value of 8 characters i.e, constant
#DiscriminatorColumn(name="category", length=7)//change to more than 7
Because, #DiscriminatorValue(value="constant")//here the length is 8
You can use the below code for Discriminator column:
#DiscriminatorColumn(name = "category", discriminatorType = DiscriminatorType.STRING)//no need of length attribute
2) verify that for the property private AnalogParameterType analogParameterType; you have below setter and getter methods.
//setter
public void setAnalogParameterType(AnalogParameterType analogParameterType) {
this.analogParameterType = analogParameterType;
}
//getter
public AnalogParameterType getAnalogParameterType() {
return analogParameterType;
}
3) Instead of
#ManyToOne
#JoinColumn(name="parameter_type_id")
use #ManyToOne(cascade=CascadeType.ALL) on getters.
For example:
#ManyToOne(cascade = CascadeType.ALL)
public AnalogParameterType getAnalogParameterType() {
return analogParameterType;
}
So i solved my issue by mapping the two superclasses Parameter and ParameterType with insertable/updatable=false:
#Entity
#Table(name="parameters")
#DiscriminatorColumn(name = "category", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorOptions(force=true)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Parameter {
#ManyToOne
#JoinColumn(name="parameter_type_id", insertable = false, updatable = false)
private ParameterType parameterType;
}
It appears that enforcing the association at the superclass level does the trick. I don't know if it is a pure side effect or a design need. I have not found any resources on the subject.

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

Hibernate expecting wrong parameter type in HQL

We have an hierarchy of entities, e.g. abstract Basket and two concrete, which in turn can contain a list of some other items, for example, with exact same hierarchy(SaleBasketItem, ReturnBasketItem).
#Entity
#XStreamAlias("basket")
#DiscriminatorColumn(name = "basket_type")
#DiscriminatorOptions(force = true)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Basket<T extends BasketItem>
#Entity
#DiscriminatorValue(value = "SALE")
public class SaleBasket extends Basket<SaleBasketItem>
#Entity
#DiscriminatorValue(value = "RETURN")
public class ReturnBasket extends Basket<ReturnBasketItem>
#Entity
#Table(name = "basket_item")
#XStreamAlias("basket_str")
#DiscriminatorColumn(name = "basket_item_type")
#DiscriminatorOptions(force = true)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class BasketItem<U extends BasketItem, V extends Basket>
#Entity
#DiscriminatorValue(value = "RETURN")
public class ReturnBasketItem extends BasketItem<ReturnBasketItem, ReturnBasket>
#Entity
#DiscriminatorValue(value = "SALE")
public class SaleBasketItem extends BasketItem<SaleBasketItem, SaleBasket>
So, the problem arises when I try to execute hql query like
"SELECT bi FROM " + basketType + "BasketItem bi JOIN bi.basket b JOIN b.saleSession JOIN bi.ware w WHERE b.state = :state"
where basketType is dynamically either "Sale" or "Return". Then I put that query in TypedQuery with type of BasketItem I need and try to setParameter("state", state), where state is some enum representing state of concrete basket (different in each concrete class, not present in abstract class at all), which throws
IllegalArgumentException(java.lang.IllegalArgumentException: Parameter value ... was not matching type)
where type is ReturnBasketState when actual is SaleBasket state, or vice versa.
So the question is - how to force Hibernate to expect correct type for that parameter?
I suppose it's expecting a String as parameter, rather than an Enum. try passing a String instead.

Categories