Null pointer when using QueryDSL subtypes - spring-data-jpa

I am trying to create a subtype query along the following lines, but tyre is coming back as null even if I set #QueryInit("tyre") on the wheel property of car.
QWheel wheel = QCar.car.wheel;
QTyre tyre = wheel.as(QRoadWheel.class).tyre;
BooleanExpression tyreFittedOverYearAgo
= tyre.fitted.lt(today.minusYears(1));
Iterable<Car> carsWithOldTyres = repo.findAll(tyreFittedOverYearAgo);
How do I get QueryDSL to initialise tyre when it is accessed using as()?

By default Querydsl initializes only direct reference properties. In cases where longer initialization paths are required, these have to be annotated in the domain types via com.mysema.query.annotations.QueryInit usage. QueryInit is used on properties where deep initializations are needed.
#Entity
class Event {
#QueryInit("customer")
Account account;
}
#Entity
class Account{
Customer customer;
}
#Entity
class Customer{
String name;
String address;
}
This will intialize customer.name ,customer.address

I've not been able to establish why, but I've now got things working but by using:
#QueryInit("*")
Tyre tyre;

Related

#ElementCollection to non-collection field

I have this #ElementCollection mapping so i could bring a legacy table with no unique id to work:
#Entity #Table(...)
#Inheritance(...) #DiscriminatorColumn(...)
class Notification {
#Id
#Column(name="NOTIFICATION_ID")
private BigInteger id;
}
#Entity
#DiscriminatorValue(...)
class SomeNotification extends Notification {
#ElementCollection
#CollectionTable(name="LEGACY_TABLE", joinColumns=#JoinColumn(name="NOTIFICATION_ID"))
private Set<NotificationInfo> someInformations;
}
#Embeddable
class NotificationInfo { // few columns }
I really can't touch the structure of LEGACY_TABLE, and now i am facing this:
#Entity
#DiscriminatorValue(...)
class SpecialNotification extends Notification {
// ? This is not a Collection, and it can't be a ManyToOne or OneToOne
// since there is no ID declared on NotificationInfo.
private NotificationInfo verySpecialInformation;
}
I know this is not supported by default, but i am fine to implement a Customizer to make it work with EclipseLink. The point is that for SpecialNotification instances, there will be only up to one NotificationInfo associated, instead of many, that is the case of SomeNotification.
Any thoughts about where i could start in the Customizer?
Thank you!
I'm not sure this will work, but it's worth a shot. Try a combination of #SecondaryTable and #AttributeOverride
#Entity
#SecondaryTable(name="LEGACY_TABLE",
pkJoinColumns=#PrimaryKeyJoinColumn(name="NOTIFICATION_ID"))
#DiscriminatorValue(...)
class SpecialNotification extends Notification {
...
#Embedded
#AttributeOverrides({
#AttributeOverride(name="someField", column=#Column(table = "LEGACY_TABLE", name="SOME_FIELD")),
#AttributeOverride(name="someOtherField", column=#Column(table = "LEGACY_TABLE", name="SOME_OTHER_FIELD"))
})
private NotificationInfo verySpecialInformation;
...
}
UPDATE
Since #SecondaryTable by default makes an inner join, which may not be desired, it can be worked around with vendor specific APIs.
If you use Hibernate (which you don't, judging by the question tags, but nevertheless), it can be done with #org.hibernate.annotations.Table, by setting optional = true.
With EclipseLink, you should make use of #DescriptorCustomizer and DescriptorQueryManager#setMultipleTableJoinExpression, you can find a (not spot-on, but close enough) code example here.

How to cache referenced entities in Morphia?

I'm using Morphia for MongoDB with Stripes Framework.
Let us assume I have two entities, Car (which describes a specific car, say some particular 1984 Honda Accord) and CarType (which specifies all Honda Accords of that kind):
The most natural way to model this seems:
#Entity
class Car {
#Id private String id; // VIN
private Date purchaseDate;
private Color color;
#Reference private CarType type;
// ..
}
#Entity
class CarType {
#Id private String id;
private String manufacturerId;
private float engineDisplacement;
// ..
}
This works, but is inefficient, as CarType is looked up from DB every time a Car is loaded. I would like to cache car types in memory, as they change rarely. Persistence frameworks like GORM and Hibernate would allow that out of the box, but I'm not sure how to do it under Morphia (there is a feature request raised for that).
I'd like to keep the reference to CarType, as just storing a String carTypeId would complicate the views and everything else too much.
So I thought I could do something like this:
#Entity
class Car {
#Id private String id; // VIN
private Date purchaseDate;
private Color color;
private String typeId;
#Transient private CarType type;
#Transient private CarService service = new CarServiceImpl();
public void setTypeId() {
this.typeId = typeId;
updateTypeReference();
}
#PostLoad void postLoad() {
updateTypeReference();
}
private void updateTypeReference() {
type = service.findTypeById(typeId);
}
// ..
}
class CarServiceImpl implements CarService {
#CacheResult CarType findCarTypeId(String typeId) {
datastore.get(CarType.class, typeId);
}
// ..
}
Which works and does what I want, but:
Does seem like a hack
I'd to inject the service instead using Guice, but cannot figure out how, although I have overall dependency injection working in Stripes ActionBeans.
So I'd like to either:
Learn how to inject (preferably, Guice) services into Morphia entities
or
Learn how to otherwise properly do caching for referenced entities in Morphia
or
If all else fails, switch to some other MongoDB POJO mapping approach which supports caching. But I really like Morphia so I'd rather not.
Another common approach would be to embed the CarType in each Car. That way you would only have to fetch a single entity.
Trade-offs:
You'll need an update logic for all duplicated CarTypes. Since you said that they hardly change, this should be fine performance-wise.
Duplicated data requires additional disk-space and the working set in RAM gets bigger as well.
You'll need to evaluate how this works out for your data, but data duplication to make reads faster is quite a common approach...
Since I didn't think of a better solution I am doing a #PostLoad event handler which gets the datastore class from a static variable, and can then look up the Referenced entity.
That seems like a hack and requires the datastore service to be thread-safe, but it works for me.

Ecliplselink - #CascadeOnDelete doesn't work with #Customizer

I have two entities. "Price" class has "CalculableValue" stored as SortedMap field.
In order to support sorted map I wrote customizer. After that, it seems #CascadeOnDelete is not working. If I remove CalculableValue instance from map and then save "Price" EclipseLink only updates priceId column to NULL in calculableValues table...
I really want to keep the SortedMap. It helps to avoid lots of routine work for values access on Java level.
Also, there is no back-reference (ManyToOne) defined in the CalculableValue class, it will never be required for application logic, so, wanted to keep it just one way.
Any ideas what is the best way to resolve this issue? I actually have lots of other dependencies like this and pretty much everything is OneToMany relation with values stored in sorted map.
Price.java:
#Entity
#Table(uniqueConstraints={
#UniqueConstraint(columnNames={"symbol", "datestring", "timestring"})
})
#Customizer(CustomDescriptorCustomizer.class)
public class Price extends CommonWithDate
{
...
#CascadeOnDelete
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#MapKeyColumn(name="key")
#JoinColumn(name = "priceId")
private Map<String, CalculatedValue> calculatedValues =
new TreeMap<String, CalculatedValue>();
...
}
public class CustomDescriptorCustomizer implements DescriptorCustomizer
{
#Override
public void customize(ClassDescriptor descriptor) throws Exception
{
DatabaseMapping jpaMapping = descriptor.getMappingByAttribute("calculatedValues");
((ContainerMapping) mapping).useMapClass(TreeMap.class, methodName);
}
}
Your customizer should have no affect on this. It could be because you are using a #JoinColumn instead of using a mappedBy which should normally be used in a #OneToMany.
You can check the mapping in your customizer using, isCascadeOnDeleteSetOnDatabase()
or set it using
mapping.setIsCascadeOnDeleteSetOnDatabase(true)

Entity Framework: Many-to-Many

I have the following scenario: A Doctor can have multiple Companions. A Companion can also have multiple Doctors. Here are my classses (minus the context):
Public Class Doctor
Public Property DoctorId As Integer
Public Property Regeration As Integer
Public Property Name As String
Public Property Actor As String
Public Property Companions As List(Of Companion)
End Class
Public Class Companion
Public Property CompanionId As Integer
Public Property Name As String
Public Property Actor As String
Public Property Doctors As List(Of Doctor)
End Class
Public Class DoctorViewModel
Public Property DoctorId As Integer
Public Property Actor As String
Public Property Companions As List(Of CompanionViewModel)
End Class
Public Class CompanionViewModel
Public Property Actor As String
End Class
I'm trying to fetch a singular Doctor with a list of companions who have travelled with him. This I can do quite simply, but I'm trying to shape the query to get only a few columns and not the entire entity. He is my bungled query - the X's are what I can't figure out.
Dim query = From d In context.Doctors
From c In context.Companions
Select New DoctorViewModel With {
.Actor = d.Actor,
.DoctorId = d.DoctorId,
.Companions = XXXXXXX}
EDIT: If I query:
(From d In Tardis.Doctors
Where d.Actor = "Tom Baker"
Select New DoctorViewModel With {.Actor = d.Actor, .Companions = d.Companions.Select(Function(c) New CompanionViewModel With {.Actor = c.Actor})}).SingleOrDefault
I get:
"Unable to cast the type 'System.Collections.Generic.IEnumerable1' to
type 'System.Collections.Generic.List1'. LINQ to Entities only
supports casting Entity Data Model primitive types."
Would it be considered nasty to ditch the ViewModel classes in the query and just get the stuff as an anonymous type, then pass this to a constuctor in a ViewModel (my models have a whack of functions that are needed) and fill the class like this?
It probably would be not only okay but much more readable (aka maintainable) as well. Especially since the query wouldn't return an anonymous type, it would return an IQueryable
Solved! I've spent days on this! Trick was to change the List inside the Doctor ViewModel to IEnumerable(Of CompanionViewModel)

JPA ManyToMany relation update failed due to constraint key in another relation

While developing an Eclipse GEF application using an eclipselink implementation of JPA i have found an error that has been annoying me for a while:
I have three different classes:
The first one represents a variable contained in a model:
#Entity
public class Variable extends AbstractVariable{
#Id
#Generated value
private int id;
/** Lots more of stuff */
#ManyToOne(cascade=CascadeType.ALL)
Model model;
//Setters, getters and the other functions.
}
And another subclass of the abstractvariant class, which is a variable which can hold a concatenation of variables.
#Entity
public class VariableList extends AbstractVariable{
#Id
#Generated value
private int id;
/** Lots more of stuff */
#ManyToMany(cascade=CascadeType.ALL)
List<AbstractVariable> variables;
//Setters, getters and the other functions.
}
The second class, a gef editpart that can hold a variable value.
#Entity
public class VariableEditPart{
#Id
#Generated value
private int id;
/** Lots more of stuff */
VariableList vars;
//Setters, getters and the other functions.
}
And a last class with the gef model:
#Entity
public class Model{
#Id
#Generated value
private int id;
/** Lots more of stuff */
#OneToMany(cascade=CascadeType.ALL)
List<Variable> availableVars;
#OneToMany(cascade=CascadeType.ALL)
List<VariableEditPart> editParts;
//Setters, getters and the other functions.
}
The issue here is that JPA creates a table for the relation variablelist-variable, and another relation with the editpart and the variablelist, so, as soon as I try to update the model at the database after some modifications, It tries automatically to delete the Variable, and ends up with a constraint violation error caused because the list of variables holded by the model still points to that variable (which by the way, I was not pretending to delete, and I've tested lots of differenst cascadeType's to avoid it without any luck...).
Thanks for your attention and be kind with my english, it's not my native language ;)
It seems you have a very interrelated model with everything referencing everything in cycles.
What are you doing exactly? When you remove the Variable, are you removing all references to it? You need to.