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);
....
Related
I have entity called Issue and entity called UserIssue. UserIssue extends Issue.
#Inheritance(strategy = InheritanceType.JOINED)
#Entity(name = "ISSUE")
public class Issue extends VersionedSequenceIdEntity {
... all fields
}
#Entity(name = "USER_ISSUE")
public class UserIssue extends Issue {
...
#Enumerated(EnumType.STRING)
#Column(name = "CATEGORY", nullable = false)
private IssueCategory category;
...
}
I need to do e.g. something like this:
Predicate predicate= root.get("category").in(IssueCategory.CATEGORY_1, IssueCategory.CATEGORY_2);
The problem is that root is instance of Root<Issue> but "category" field is defined on subclass UserIssue so the line of code obviously does not work.
Is there a way how to build a predicate that creates where condition for subclass field? I have only instance of Root<Issue>, CriteriaQuery and CriteriaBuilder.
Thank you,
Lukas
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Issue> issueQuery = cb.createQuery(Issue.class);
Root<Issue> issueRoot = issueQuery.from(Issue.class);
Subquery<UserIssue> subQuery = issueQuery.subquery(UserIssue.class);
Root<UserIssue> userIssueRoot = subQuery.from(UserIssue.class);
Predicate predicate= userIssueRoot.get("category")
.in(IssueCategory.CATEGORY_1, IssueCategory.CATEGORY_2);
subQuery.select(userIssueRoot).where(predicate);
issueQuery.select(issueRoot).where(issueRoot.get("id").in(subQuery));
em.createQuery(issueQuery).getResultList();
I have an inheritance relationship of entities with joined type.
#Entity
#Table(name = "MSM_SUBSCRIPTION")
#DiscriminatorColumn(name = "SUBSCRIPTIONTYPE", discriminatorType = DiscriminatorType.STRING, length = 100)
class subscription {
}
#DiscriminatorValue("com.xxx.XXXSubscription")
#Table(name = "XXX")
public class XXXSubscription extends Subscription implements Serializable {
}
When I'm trying to use a named query such as
SELECT s.class AS subscriptiontype,
FROM
Subscription s
It is resulting in the following query
select
case
when s1_.subscriptionId is not null then com.xxx.XXXSubscription
when s.subscriptionId is not null then 'Subscription'
end AS subscriptiontype,
from
MSM_SUBSCRIPTION s
left outer join
XXXSubscription s1_
on s.subscriptionId=s6_.subscriptionId
Which throws an error as below.
-ORA-00904: "COM"."xxx"."MMSSUBSCRIPTION": invalid identifier
As I noticed, there are no tags around the case statement in generated query, on manually firing this query with tags around the DiscriminatorValue 'com.xxx.XXXSubscription', the query is running fine.
Can someone please help ??
try this
you can define super class with #inheritance annotation
#Entity
#Table(name = "MSM_SUBSCRIPTION")
**#Inheritance(strategy = InheritanceType.SINGLE_TABLE)**
#DiscriminatorColumn(name = "SUBSCRIPTIONTYPE", discriminatorType = DiscriminatorType.STRING, length = 100)
class subscription {
}
#DiscriminatorValue("com.xxx.XXXSubscription")
#Table(name = "XXX")
public class XXXSubscription extends Subscription implements Serializable {
}
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!
I'm using EJB 3 with Hibernate 4 on Weblogic 10.3.4.
I have a problem when I try to select an entity based on a property that doesn't exists in its parent entity:
I have in my system something like this: A basic abstract VehicleDefinition class and two concrete subclasses - CarDefinition and TruckDefinition.
Also, I have a basic abstract VehicleInstance and two concrete subclasses - CarInstance and TruckInstance.
In VehicleInstance I have a VehicleDefinition field.
In the TruckDefinition class I map a field (Height) that doesn't exists In VehicleDefinition or in CarDefinition. The problem is that with the mentioned mapping, I can't use an HQL to select a TruckInstance by a the field of TruckDefintion (i.e. A TruckInstance by height), because TruckInstance only "knows" VehicleDefinition, not TruckDefinition.
The mappings goes something like this:
#Entity
#Table(name = "VEHICLES" uniqueConstraints = {#UniqueConstraint(columnNames = {"TYPE_NAME"}) })
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "VEHICLE_TYPE", discriminatorType = DiscriminatorType.INTEGER)
public abstract class VehicleDefinition {...}
#Entity
#DiscriminatorValue("222")
public class CarDefenition extends VehicleDefinition {...}
#Entity
#DiscriminatorValue("555")
public class TruckDefenition extends VehicleDefinition{
...
private Integer mHeight;
#Column(name = "TRUCK_HIGHT")
public Integer getHeight()
{
return mHeight;
}
}
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorFormula("CASE WHEN 222 = (SELECT a.VEHICLE_TYPE FROM VEHICLES a WHERE a.id = VEHICLE_ID) THEN 222 ELSE 555 END")
#Table(name = "VEHICLE_INSTANCES" uniqueConstraints = {#UniqueConstraint(columnNames = {"VEHICLE_ID", "LPLATE"}) })
public abstract class VehicleInstance {
...
private VehicleDefinition mDefinition
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "VEHICLE_ID" referencedColumnName = "ID", nullable = false)
public VehicleDefenition getDefinition()
{
return mDefinition;
}
}
#Entity
#DiscriminatorValue("222")
public class CarInstance extends VehicleInstance {...}
#Entity
#DiscriminatorValue("555")
public class TruckInstance extends VehicleInstance {...}
I tried to refactore he classes, making getDefinition in VehicleInstance abstract, and having TruckInstance and CarInstance implement, both returning their repectivetypes, but then I couldn't figure out how two map the new methods. Should I keep the original annotations at VehicleInstance? Should both of them point to VEHICLE_ID? Every combination I tried seemed to fail.
So in conclusion - How can I map VehicleInstance, CarInstance and TruckInstance so that I can select in HQL a TruckInstance based on its Height property?
Thanks in advance!
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")