JPA: fetch field from child class in JOINED inheritance - java

my superclass is:
#Entity
#Table(name = "TEST_VEHICLE")
#ChangesListener
#AttributeOverride(name = "id", column = #Column(name = "VEHICLE_ID"))
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "VEHICLE_TYPE_ID", discriminatorType = DiscriminatorType.INTEGER)
public abstract class Vehicle extends ParentEntity {
#Column(name = "MAX_SPEED", nullable = false)
private Integer maxSpeed;
public Integer getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(Integer maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
and subclass is:
#Entity
#Table(name = "TEST_BUS")
#DiscriminatorValue("2")
public class Bus extends Vehicle {
#Column(name = "PASSENGER_NUMBER", nullable = false)
private Short passengerNumber;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FOO_OF_VEHICLE")
private Foo foo;
public Short getPassengerNumber() {
return passengerNumber;
}
public void setPassengerNumber(Short passengerNumber) {
this.passengerNumber = passengerNumber;
}
public Foo getFoo() {
return foo;
}
public void setFoo(Foo foo) {
this.foo = foo;
}
}
using fetch of foo on Root<Vehicle> in criteria:
root.fetch("foo", JoinType.LEFT);
causes this error :
java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [foo] on this ManagedType ...
how can I fetch fields from subclasses?
update:
using treat does not resolve my problem:
Root<Bus> busRoot = builder.treat(root, Bus.class);
busRoot.fetch("foo", JoinType.INNER);
I don't receive any error but foo does not fetch yet.
generated SQL is:
SELECT vehicle0_.VEHICLE_ID AS VEHICLE_2_72_,
vehicle0_.ATTACHMENT_COUNT AS ATTACHME3_72_,
vehicle0_.COMMENTS AS COMMENTS4_72_,
vehicle0_.CREATE_TIMESTAMP AS CREATE_T5_72_,
vehicle0_.CREATOR_USER_ID AS CREATOR_8_72_,
vehicle0_.MODIFIER_USER_ID AS MODIFIER9_72_,
vehicle0_.UPDATE_TIMESTAMP AS UPDATE_T6_72_,
vehicle0_.MAX_SPEED AS MAX_SPEE7_72_,
vehicle0_1_.FOO_OF_VEHICLE AS FOO_OF_V3_70_,
vehicle0_1_.PASSENGER_NUMBER AS PASSENGE1_70_,
vehicle0_2_.ENGINE_TYPE AS ENGINE_T1_71_,
vehicle0_.VEHICLE_TYPE_ID AS VEHICLE_1_72_
FROM TEST_VEHICLE vehicle0_
LEFT OUTER JOIN TEST_BUS vehicle0_1_
ON vehicle0_.VEHICLE_ID=vehicle0_1_.VEHICLE_ID
LEFT OUTER JOIN TEST_CAR vehicle0_2_
ON vehicle0_.VEHICLE_ID =vehicle0_2_.VEHICLE_ID
WHERE vehicle0_.VEHICLE_ID=105

This problem can be solved using meta model.
public abstract class Bus_ extends com.rh.cores.architecture.tests.models.Vehicle_ {
public static volatile SingularAttribute<Bus, Foo> foo;
public static volatile SingularAttribute<Bus, Short> passengerNumber;
}
means this:
root.fetch(Bus_.foo, JoinType.LEFT);
but since fetch signature in JPA is like this:
<Y> Fetch<X, Y> fetch(SingularAttribute<? super X, Y> attribute, JoinType jt);
above code causes compile error!
with changing code like this:
SingularAttribute attribute = Bus_.foo;
root.fetch(attribute, JoinType.LEFT);
we can bypass generics check SingularAttribute<? super X, Y> in JPA standard while Hibernate handle it!

Related

JPQL gives back java.langObject, instad of the expected type

I have an Employee class. A Worker class extends Employee, and Leader class extends Worker.
Every Leader has a list public List workersResponsibleFor = new ArrayList<>();. Which is a Join table, to illustrate if a Leader is responsible for one or many Workers.
#Entity
#Inheritance
#DiscriminatorColumn(name="EMP_TYPE")
#Table(name="EMPLOYEES")
public abstract class Employee implements Printable, Serializable {
#Id
#GeneratedValue
#Column(name = "employee_id", unique = true)
private int id;
#Column(name = "position")
#Enumerated(EnumType.STRING)
private Position position;
#Column(name = "name")
private String name;
#Column(name = "salary")
private Double salary;
#Transient
public List<Project> projectsWorkingOn = new ArrayList<>();
public Employee() {
}
}
Worker:
#Entity
public class Worker extends Employee {
#Id
#GeneratedValue
#Column(name = "worker_id", unique = true)
private int id;
public Worker() {
}
}
Leader:
#Entity
public abstract class Leader extends Worker {
public Leader() {
}
public Leader(Position position, String name, Double salary) {
super(position, name, salary);
}
#OneToMany
#JoinTable(name="WORKERS_RESPONSIBLE_FOR",
joinColumns = {#JoinColumn(name="RES_ID")},
inverseJoinColumns = {#JoinColumn(name="FOR_ID")})
public List<Worker> workersResponsibleFor = new ArrayList<>();
}
The problem is, every time I try to get a Worker back with the following method:
private static final EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("manager1");
private static final EntityManager entityManager = entityManagerFactory.createEntityManager();
public Worker exampleQuery() {
Worker w;
try {
Query query = entityManager.createQuery("select e1, e2 from Employee e1 join e1.workersResponsibleFor e2 WHERE e1.name LIKE 'James%'");
w = (Worker) query.getSingleResult();
} finally {
entityManager.close();
entityManagerFactory.close();
}
System.out.println(w.getName());
return w;
}
Error:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to com.company.employees.Worker
I get a java.lang.Object back, insted of a Worker. But all my other methods are able to return Workers, so could be the problem with the query I'm using? Thanks
JPQL of
select e1, e2 from Employee e1 join e1.workersResponsibleFor e2 WHERE ...
will always return rows of type Object[] (and the message tells you exactly that), since that is what the JPA spec defines it to do. And if you look at element 0 it will be of the type of e1, and element 1 will be of the type of e2. Suggest you look at the JPA spec or the documentation of whichever implementation you use.
If you had instead selected just the candidate alias then you get a row of type of e1. Again, defined in the JPA spec!

Hibernate multiple fetch with abstract class

Hello I have following classes:
public class OrderTemplate {
<other fields>
#OneToMany(mappedBy = "orderTemplate", cascade = CascadeType.ALL)
#Column(name="order_template_input")
private Set<OrderTemplateInput> orderTemplateInputs;
}
Now i have Abstract Class orderTemplateInput like this:
public abstract class OrderTemplateInput {
<other fields>
#Column(name="text", nullable = false)
protected String text;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "order_template_id")
#JsonIgnore
protected OrderTemplate orderTemplate;
}
And two classes extends this one:
public class OrderTemplateTextInput extends OrderTemplateInput {
#JsonProperty(value="type")
public OrderTemplateInputType getType() {
return OrderTemplateInputType.TEXT;
}
}
And this one:
public class OrderTemplateSelectInput extends OrderTemplateInput {
#OneToMany(mappedBy = "orderTemplateSelectInput", cascade = CascadeType.ALL)
private Set<InputValue> inputValues;
#JsonProperty(value="type")
public OrderTemplateInputType getType() {
return OrderTemplateInputType.SELECT;
}
}
First of all i want to avoid fetchType.Eager. So i want to get all orderTemplate with associations collections. I have following hql query:
#Query("SELECT distinct o FROM OrderTemplate o JOIN FETCH o.orderTemplateInputs JOIN FETCH o.orderTemplateInputs oIn JOIN FETCH oIn.inputValues")
It's not working only in one case when the OrderTemplate has only collection of OrderTemplateTextInput. In the list of OrderTemplate there is only OrderTemplate with OrderTemplateSelectInput and with both select and text. How should look like the query to get all associations collections?

JPA Query on sublclass with SingleTable inheritance

Say I have the following entities:
#Entity
public class Container
{
#OneToMany
Set<AbstractElement> elements;
//getter setter and other attributes...
}
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "CLASS_CODE", discriminatorType = DiscriminatorType.STRING, length = 10)
public abstract class AbstractElement<T> extends AbstractEntity
{
#Transient
T value;
//getter setter and other attributes...
}
#DiscriminatorValue(value = "BOOL")
#Entity
public class BooleanElement extends AbstractElement<Boolean>
{
/**
* {#inheritDoc}
*/
#Column(name = "VALUE_BOOL")
#Override
public Boolean getValue()
{
return super.getValue();
}
}
The question is this:
How can I execute a jpa criteria query on the value of the BooleanElement, starting from the class CONTAINER?
What I actually have so far is this:
CriteriaQuery<Container> criteriaQuery = criteriaBuilder.createQuery(Container.class);
Root<Container> from = criteriaQuery.from(Container.class);
criteriaQuery.select(from);
from = from.join("elements");
Predicate pred = criteriaBuilder.equal(criteriaBuilder.treat(from ,BooleanElement.class).get("value"), FOO);
//etc......
The exception at this point is that "there is not VALUE attributes on the AbstractEntity".
Thanks in advance.
The Treat operator doesn't work in this specific case.
The as operator on the joins it's still not so clear how to implement: i get class cast exception at line 3 when doing this:
CriteriaQuery<AbstractElement> criteriaQuery = criteriaBuilder.createQuery(AbstractElement.class);
Root<AbstractElement> rootAbstract = criteriaQuery.from(AbstractElement.class);
Path predPath = (Path)rootAbstract.as(BooleanElement.class);
predPath.get("value");
The only way to execute this type of query is by executing a subquery
or creating an additional "from" clause:
Root<BooleanElement> from2 = criteriaQuery.from(BooleanElement.class);
Predicate joinPredicate = criteriaBuilder.equal(from, from2);
....

JPA -- Cannot instantiate abstract class exception

FundOperationItem.java
#Entity
#Table(name = "OPERATION_ITEMS")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "D_TYPE", discriminatorType = DiscriminatorType.INTEGER)
public abstract class FundOperationItem implements Serializable {
#ManyToOne(fetch = FetchType.LAZY, optional=false)
#JoinColumn(name = "PARENT_OPERATION_ID", nullable=false)
private FundOperation operation;
public FundOperation getOperation() {
return this.operation;
}
public void setOperation(final FundOperation operation) {
this.operation = operation;
}
}
ExchangeOperationItem.java
#Entity
#Table(name = "EXCHANGE_OPERATION_ITEMS")
#DiscriminatorValue(value="2")
public class ExchangeOperationItem extends FundOperationItem {
}
SimpleOperationItem.java
#Entity
#Table(name = "SIMPLE_OPERATION_ITEMS")
#DiscriminatorValue(value="1")
public class SimpleOperationItem extends FundOperationItem {
}
FundOperation.java
#Entity
#Table(name = "OPERATIONS")
public class FundOperation implements java.io.Serializable{
#OneToMany(cascade = CascadeType.ALL, mappedBy = "operation", fetch = FetchType.LAZY)
private List<FundOperationItem> operationItems = new ArrayList<FundOperationItem>();
public List<FundOperationItem> getOperationItems() {
return this.operationItems;
}
public void setOperationItems(final List<FundOperationItem> operationItems) {
this.operationItems = operationItems;
}
}
Using this in such manner:
#Test
public void test(){
FundOperation oper = operationRepository.findById(1L);
System.out.println(oper.getOperationItems().size());
}
Got such exception:
org.apache.openjpa.persistence.ArgumentException: Cannot instantiate abstract class of type "rba.pm.persistency.operation.FundOperationItem" with object id "rba.pm.persistency.operation.FundOperationItem-1"; this may indicate that the inheritance discriminator for the class is not configured correctly.
DB content:
Insert into OPERATIONS (OPERATION_ID) values (1);
Insert into OPERATIONS (OPERATION_ID) values (2);
Insert into OPERATIONS (OPERATION_ID) values (3);
Insert into OPERATION_ITEMS (OPERATION_ITEM_ID,PARENT_OPERATION_ID,D_TYPE) values (1,1,1);
Insert into OPERATION_ITEMS (OPERATION_ITEM_ID,PARENT_OPERATION_ID,D_TYPE) values (2,2,1);
Insert into OPERATION_ITEMS (OPERATION_ITEM_ID,PARENT_OPERATION_ID,D_TYPE) values (3,3,1);
Insert into SIMPLE_OPERATION_ITEMS (OPERATION_ITEM_ID) values (1);
Insert into SIMPLE_OPERATION_ITEMS (OPERATION_ITEM_ID) values (2);
Insert into SIMPLE_OPERATION_ITEMS (OPERATION_ITEM_ID) values (3);
Have I made something wrong?
Update:
**There is an solution, if add to the test above a new line
SimpleOperationItem sio = new SimpleOperationItem();
it works
#Test
public void test(){
SimpleOperationItem sio = new SimpleOperationItem();
FundOperation oper = operationRepository.findById(1L);
System.out.println(oper.getOperationItems().size());
}
Note: Object 'sio' does not have any relation to 'oper'.
Any idea, what is going on?
Is this a problem with classloader?
Is this a known problem?
Not tested this, but try the following in FundOperationItem:
#DiscriminatorValue(value="0")

Hibernate, single table inheritance and using field from superclass as discriminator column

I have following kinds of classes for hibernate entity hierarchy. I am trying to have two concrete sub classes Sub1Class and Sub2Class. They are separated by a discriminator column (field) that is defined in MappedSuperClass. There is a abstract entity class EntitySuperClass which is referenced by other entities. The other entities should not care if they are actually referencing Sub1Class or Sub2Class.
It this actually possible? Currently I get this error (because column definition is inherited twice in Sub1Class and in EntitySuperClass) :
Repeated column in mapping for entity: my.package.Sub1Class column: field (should be mapped with insert="false" update="false")
If I add #MappedSuperClass to EntitySuperClass, then I get assertion error from hiberante: it does not like if a class is both Entity and a mapped super class. If I remove #Entity from EntitySuperClass, the class is no longer entity and can't be referenced from other entities:
MappedSuperClass is a part of external package, so if possible it should not be changed.
My classes:
#MappedSuperclass
public class MappedSuperClass {
private static final String ID_SEQ = "dummy_id_seq";
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = ID_SEQ)
#GenericGenerator(name=ID_SEQ, strategy="sequence")
#Column(name = "id", unique = true, nullable = false, insertable = true, updatable = false)
private Integer id;
#Column(name="field", nullable=false, length=8)
private String field;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
}
#Entity
#Table(name = "ACTOR")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="field", discriminatorType=DiscriminatorType.STRING)
abstract public class EntitySuperClass extends MappedSuperClass {
#Column(name="description", nullable=false, length=8)
private String description;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
#Entity
#DiscriminatorValue("sub1")
public class Sub1Class extends EntitySuperClass {
}
#Entity
#DiscriminatorValue("sub2")
public class Sub2Class extends EntitySuperClass {
}
#Entity
public class ReferencingEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer id;
#Column
private Integer value;
#ManyToOne
private EntitySuperClass entitySuperClass;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public EntitySuperClass getEntitySuperClass() {
return entitySuperClass;
}
public void setEntitySuperClass(EntitySuperClass entitySuperClass) {
this.entitySuperClass = entitySuperClass;
}
}
In my project it is done this way:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "field", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorValue("dummy")
public class EntitySuperClass {
// here definitions go
// but don't define discriminator column here
}
#Entity
#DiscriminatorValue(value="sub1")
public class Sub1Class extends EntitySuperClass {
// here definitions go
}
And it works. I think your problem is that you needlessly define discriminator field in your superclass definition. Remove it and it will work.
In order to use a discriminator column as a normal property you should make this property read-only with insertable = false, updatable = false. Since you can't change MappedSuperClass, you need to use #AttributeOverride:
#Entity
#Table(name = "ACTOR")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="field", discriminatorType=DiscriminatorType.STRING)
#AttributeOverride(name = "field",
column = #Column(name="field", nullable=false, length=8,
insertable = false, updatable = false))
abstract public class EntitySuperClass extends MappedSuperClass {
...
}
You can map a database column only once as read-write field (a field that has insertable=true and/or updatable=true) and any number times as read-only field (insertable=false and updatable=false). Using a column as #DiscriminatorColumn counts as read-write mapping, so you can't have additional read-write mappings.
Hibernate will set value specified in #DiscriminatorColumn behind the scenes based on the concrete class instance. If you could change that field, it would allow modifying the #DiscriminatorColumn field so that your subclass and value in the field may not match.
One fundamental: You effectively should not need to retrieve your discriminator column from DB. You should already have that information within the code, of which you use in your #DiscriminatorValue tags. If you need read that from DB, reconsider carefully the way you are assigning discriminators.
If you need it in final entity object, one good practice can be to implement an Enum from discriminator value and return store it in a #Transient field:
#Entity
#Table(name="tablename")
#DiscriminatorValue(Discriminators.SubOne.getDisc())
public class SubClassOneEntity extends SuperClassEntity {
...
#Transient
private Discriminators discriminator;
// Setter and Getter
...
}
public enum Discriminators {
SubOne ("Sub1"),
SubOne ("Sub2");
private String disc;
private Discriminators(String disc) { this.disc = disc; }
public String getDisc() { return this.disc; }
}

Categories

Resources