JPA2 Criteria Builder - Queries on abstract classes and multiple subclasses - jpa-2.0

first of all, sorry if this question has already been asked, but I couldn't find any similar questions nor answers for my problem.
My problem is, that I have several subclasses in several hierarchy levels inheriting from one subclass.
#Entity
public class A{ }
#Entity
public class B extends A { ... }
#Entity
public class C extends A{ ... }
#Entity
public class D extends C {
private String someAttribute;
}
#Entity
public class E extends C {
private String anotherAttribute;
}
I need to process a query on C and get all entities from C,D,E according to my criteria, which accesses attributes from D and E.
I noticed, that it is not possible to access for example someAttribute fro D executing a query on C. like this:
Root root = query.from(C.class);
Path p = root.get("someAttribute");
Path p2 = root.get("anotherAttribute");
Please note, that I cannot work with metamodels at this point.
In JPAQL I would code something like that:
`select e1 from C eq where someAttribute = .... or anotherAttribute = ....`
And it would resolve my hierarchy properly.
To solve the problem, I created my own annotation equivalent to #XmlSeeAlso and named it #PersistenceSeeAlso which tells me, which subclasses I have to look up to find my attribute. So when I process my hierarchy accoridng to #PersistenceSeeAlso and get my paths I need to create a new Root element for each subclass, that I look up for my attribute.
The main Problem here is, that query.form(clazz) creates a join on the query, which totally messes up my query, but I need a Root element on my type to resolve the path.
So my question is; is there a way, to handle multiple subclass selections with JPA2 CriteriaBuilder without creating new Root instances, maybe with EntityType?
Or am I doing something totally wrong?
Thank you very much in advance!
Best regards,
Q

sorry for the long wait!
That's a great question that I see all the time. The problem is that you are assuming information about the sub-types of C, which isn't exactly right.
If you use a "table per class" or "joined table" inheritance strategy, then the data for D and E are stored in separate tables and select e1 from C eq where someAttribute = .... or anotherAttribute = .... would not work, since those columns don't exist in C.
You would need to LEFT JOIN the columns from D and E onto C before you can filter. (I answered a similar question before that should help.

Related

Doctrine2 with dynamic relationships base on a value

What I'm trying to do is basically Entity A which have field type to relate with Entity B or C base on the value of field type
for example: a Product if is type drinks to be related with DrinkOptions entity, but if it's with type food to be related with FoodOptions entity
Is there any elegant solution which can work fine in such case?
This is what I thought it will work, but it doesn't..
Changing dynamically the entity A relation will not help, because it's not yet loaded and I can't see the value of field type
combine the different option entities into one, but it will be very ugly picture with 200+ properties..
define all option types in the config and then dynamically create the mapping with the loadClassMetadata, but is not so pretty also it requires a "dummy" property, getters and setters for all option types in the Entity A
A possible solutions could be to use a single table inheritance. Might not be the nicest solution but it could fit your specific need.
<?php
namespace MyProject\Model;
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discriminator", type="string")
* #DiscriminatorMap({"drink" = "DrinkOption", "food" = "FoodOption"})
*/
class Option
{
// ...
}
/**
* #Entity
*/
class FoodOption extends Option
{
// ...
}
/**
* #Entity
*/
class DrinkOption extends Option
{
// ...
}
You then could even switch the type by altering the "discriminator" column.
I think the simpliest way would be to have multiple product entities with an inheritence
Like Product (parent) -> foodProduct (child) or DrinkProduct().
This way you could have Product link to common others entities (like price or brand) and specific link for food, drinks, smokes etc...
Product
id:
type: food/drink/smoke etc..
foodproduct: one to Many nullable: FoodProduct (it's actually one to zero/one but i find it more performant)
drinkproduct: one to Many nullable: DrinkProduct
smokeproduct:one to Many nullable: SmokeProduct
FoodProduct:
id:
product_id (many to one to Product)
options one to many to foodOptions
If it still don't fit you will have to find some design pattern

django one-to-many

Long story short: I have two models, and a database imported from XML. The model layout is as follows:
class A:
ForgeinKey(B)
class B:
list = {A1, A2 ... An}
Is there a replacement for {A1, A2 ... An} that would make B.list return a list of A's.
Edit: The idea is to have a field in B that lists all the A's that are pointing to it. I can't seem to figure out how to call A.objects.* from inside B's definition. I don't even know if that's possible.
Edit2: Solved, thanks everyone for help :)
If I understood correctly, you want B().list to return the result of SELECT * FROM A WHERE B_id = <B.id>?
You have to use related_name, then:
class A(Model):
b = ForeignKey(B, related_name='list')
Or, you can use the default name, "B().A_set"

JPA JPQL: select items when attribute of item (list/set) contains another item

public class Document extends Model {
...
#ManyToMany
public Set<User> accessors;
...
}
I want to select all Documents which accessors contain a certain user.
I have just minimal experiences with SQL and no experiences with JPQL.
So how to do that?
thanks in advance
SELECT d FROM Document AS d WHERE :user MEMBER OF d.accessors
Should be what you need, and it is simpler than joining tables.
Just dont forget to use the user as a parameter instead of using its id:
query.setParameter("user", user);
select distinct d from Document d inner join d.accessors a where a.id = :id
You should learn how SQL joins work, and then learn how to use joins in JPQL. That's essential. You'll find plenty of tutorials online. Google is your friend.

JPA deleting inverse relationship

I have been trying to delete the inverse relationship on a JPA entity, however this have not been working well. What I'm trying right now is to set the ManyToOne property to null and then saving it using the entityManager's merge method. The ManyToOne relationship is marked with the cascade all property, however in the dataBase the foreign key is not removed. How should I do this?. Thanks a lot.
It would be easier to find out what you mean, with code in question. But I will try anyway:
#Entity
public class AEntity {
#GeneratedValue (strategy = GenerationType.SEQUENCE)
#Id int id;
//Having some cascade here doesn't matter for our case
//because we now do not cascade anything, we just set this field to
//null. Cascade=REMOVE is about never meaningful (and never fully
//fully portable) in ManyToOne side:
//just think what happens to other AEntity instances that refer to
//same BEntity.
#ManyToOne
BEntity bEntity;
public void setbEntity(BEntity bEntity) {
this.bEntity = bEntity;
}
}
public class BEntity {
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Id int id;
}
In the beginning we have following data:
AEntity(id=1,bEntity_id=2)
BEntity(id=2)
Then removing connection between a and b:
AEntity oldDirty = em.find(AEntity.class, 1);
//modify A somewhere else in code
oldDirty.setbEntity(null);
//and bring changes in:
em.merge(oldDirty);
Afterwards we have:
AEntity(id=1,bEntity_id=null)
BEntity(id=2)
If BEntity also have set that contains AEntity entities (so to say bidirectional relationship), then you have to remove A from there as well, because you have to keep care about relationship by yourself. OneToMany side is one where it can make sense to cascade removal from.
Check the cascade type of the relationship on both ends. For instance, if you want to delete all the associated entities when you delete the main entity, the annotation should look like this: #ManyToOne(cascade={CascadeType.REMOVE}), and on the inverse #OneToMany(cascade={CascadeType.REMOVE})

Filtering inherited objects

I have a base class A . Two derived classes B , C.
Now I have a global class witch contains a many-to-many relation to object A.
Class D:
aObjects : ManyToMany("A")
how could know the real object the filter query return in object D.
I mean : d.objects.get(id=5)
now d has n objects of class A, but they are a mix of A,B or C.
How can get only those of type B in the query.
Thanks in advance.
There's no way to do this automatically. The documentation is quite clear that there's no way to tell from an instance of a base class whether or not it should 'actually' be an instance of a derived class.
The only thing to do is to define a field on the base class that shows what derived type it is, and set this automatically in the the save() method of the various derived classes. Then you can filter on the value of this field.
Will this works for you
filter(lambda x: isinstance(x, B), d.objects.get(id=5))