Blaze persistence - Attribute 't' not found on type 'X' - jpa

I am trying to make a recursive query using blaze persistence lib.
I have two tables (a and a_temp) and entities (A and ATemp respectively) that represent them. ATemp is almost like an entity view to the A class, but its table serves to temporary persist A object in data base (it's done by someone else and I don't intend on changing this). So, A and ATemp share some common columns. I also have an ACte.class, which is CTE of A.class.
I'm trying to join them by some of their properties, but get the following error:
Attribute 't' not found on type 'A'; nested exception is java.lang.IllegalArgumentException: Attribute 't' not found on type 'A'
the Criteria Builder looks like this
CriteriaBuilder<A> cb = cbf.create(em, A.class)
.from(A.class, "s")
.innerJoinOn(ATemp.class, "t")
.onExpression("s.prop1.id = t.prop1.id"
+ " AND s.prop2 = t.prop2")
.end()
// ...some more joins...
.whereExpression("...")
.where("s.id").notIn()
.from(ACte.class, "r")
.withRecursive(ACte.class)
.from(A.class, "m")
// bindings
.whereExpression("m.id = t.somepropId")
.unionAll()
.from(ACte.class, "r")
// bindings
.innerJoin(A.class, "d")
.onExpression("d.id = r.somepropId")
.end()
.end()
.select("r.id")
.end()
.select("s");
then I simply do cb.getResultList() and after I call for the method I get the error above.
What could possibly be wrong with it? I don't really get why it counts 't' as A class field and not an alias for ATemp
Thanks for any advice!
Upd.: for better clarity I decided to post a structure of the A and ATemp entities
#Entity
#Table(name = "a")
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true)
public class A extends SomeParentClassThatHasId {
#Column(name = "prop1")
#Getter
#Setter
private SomeInnerClass prop1;
#Column(name = "prop2")
#Getter
#Setter
private String prop2;
...
}
#Entity
#Table(name = "a_temp")
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true)
public class ATemp extends SomeParentClassThatHasId {
#Column(name = "prop1")
#Getter
#Setter
private SomeInnerClass prop1;
#Column(name = "prop2")
#Getter
#Setter
private String prop2;
...
}
#CTE
#Entity
#Data
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true)
public class ACte extends SomeParentClassThatHasId {
private Long somepropId;
...
}

It's hard to say for sure what is happening (I might need to see a reproducer for this), but it seems to me that the use of .whereExpression("m.id = t.somepropId") might be the cause for this.
You are trying to add a recursive CTE from within a subquery, but it is not possible to refer to aliases in such CTEs. CTEs are fully isolated.
I don't know what your use case is, but if you need some help with that, join the community on Slack where we can dive deeper into this.
I think though, that you should try to write your CTE first, and then try to join against that.

Related

Is JPA Embeddable a Value Object and Why Can't it Represent a Table

PROBLEM: I have read-only data in a table. Its rows have no id - only composite key define its identity. I want it as a Value Object (in DDD terms) in my app.
RESEARCH: But if I put an #Embeddable annotation instead of #Entity with #Id id field, then javax.persistence.metamodel doesn't see it and says Not an embeddable on Metamodel metamodel.embeddable(MyClass.class);. I could wrap it with an #Entity class and autogenerate id, but this is not what I architectually intended to achieve.
QUESTION: Is JPA Embeddable a Value Object? Can Embeddable exist without a parent Entity and represent a Table?
There are many articles on the topic that show this is a real JPA inconvenience:
http://thepaulrayner.com/persisting-value-objects/
https://www.baeldung.com/spring-persisting-ddd-aggregates
https://paucls.wordpress.com/2017/03/04/ddd-building-blocks-value-objects/
https://medium.com/#benoit.averty/domain-driven-design-storing-value-objects-in-a-spring-application-with-a-relational-database-e7a7b555a0e4
Most of them suggest solutions based on normalised relational database, with a header-entity as one table and its value-objects as other separate tables.
My frustration was augmented with the necessity to integrate with a non-normalized read-only table. The table had no id field and meant to store object-values. No bindings with a header-entity table. To map it with JPA was a problem, because only entities with id are mapped.
The solution was to wrap MyValueObject class with MyEntity class, making MyValueObject its composite key:
#Data
#Entity
#Table(schema = "my_schema", name = "my_table")
public class MyEntity {
#EmbeddedId MyValueObject valueObject;
}
As a slight hack, to bypass JPA requirements for default empty constructor and not to break the immutability of Value Object, we add it as private and sacrifice final modifier for fields. Privacy and absence of setters conforms the initial DDD idea of Value Object:
// #Value // Can't use, unfortunately.
#Embeddable
#Immutable
#AllArgsConstructor
#Getter
#NoArgsConstructor(staticName = "private") // Makes MyValueObject() private.
public class MyValueObject implements Serializable {
#Column(name = "field_one")
private String myString;
#Column(name = "field_two")
private Double myDouble;
#Transient private Double notNeeded;
}
Also there is a handful Lombok's #Value annotaion to configure value objects.

#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.

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)

Adding entity doesn't refresh parent's collection

the question and problem is pretty simple, though annoying and I am looking for a global solution, because it's application-wide problem for us.
The code below is really not interesting but I post it for clarification!
We use PostgreSQL database with JPA 2.0 and we generated all the facades and entities, of course we did some editing but not much really.
The problem is that every entity contains a Collection of its children, which however (for us only?) is NOT updated after creation a children element.
The objects are written to database, you can select them easily, but what we really would like to see is the refreshed collection of children in parent object.
Why is this happening? If we (manually) refresh the entity of parent em.refresh(parent) it does the trick but it would mean for us a lot of work in Facades I guess. But maybe there is no other way?
Thanks for support!
/* EDIT */
I guess it has to be some annotation problem or cache or something, but I've already tried
#OneToMany(mappedBy = "idquestion", orphanRemoval=true, fetch= FetchType.EAGER)
and
#Cacheable(false)
didn't work properly.
/* EDIT */
Some sample code for understanding.
Database level:
CREATE TABLE Question (
idQuestion SERIAL,
questionContent VARCHAR,
CONSTRAINT Question_idQuestion_PK PRIMARY KEY (idQuestion)
);
CREATE TABLE Answer (
idAnswer SERIAL,
answerContent VARCHAR,
idQuestion INTEGER,
CONSTRAINT Answer_idAnswer_PK PRIMARY KEY (idAnswer),
CONSTRAINT Answer_idQuestion_FK FOREIGN KEY (idQuestion) REFERENCES Question(idQuestion)
);
Than we have generated some Entities in Netbeans 7.1, all of them look similar to:
#Entity
#Table(name = "question", catalog = "jobfairdb", schema = "public")
#XmlRootElement
#NamedQueries({ BLAH BLAH BLAH...})
public class Question implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name = "idquestion", nullable = false)
private Integer idquestion;
#Size(max = 2147483647)
#Column(name = "questioncontent", length = 2147483647)
private String questioncontent;
#OneToMany(mappedBy = "idquestion", orphanRemoval=true)
private Collection<Answer> answerCollection;
Getters... setters...
We use (again) generated facades for them, all implementing AbstractFacade like:
public abstract class CCAbstractFacade<T> {
private Class<T> entityClass;
public CCAbstractFacade(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public void create(T entity) {
getEntityManager().persist(entity);
}
The father entity is updated automatically if you use container managed transactions and you fetch the collection after the transaction is complete. Otherwise, you have to update yourself the collection.
This article explains in detail this behaviour: JPA implementation patterns: Bidirectional associations
EDIT:
The simplest way to use Container Managed Transactions is to have transaction-type="JTA" in persistence.xml and use Container-Managed Entity Managers.
You seem to be setting the ManyToOne side, but not adding to the OneToMany, you have to do both.
In JPA, and in Java in general you must update both sides of a bi-directional relationship, otherwise the state of your objects will not be in sync. Not doing so, would be wrong in any Java code, not just JPA.
There is no magic in JPA that will do this for you. EclipseLink does have a magic option for this that you could set through a customizer (mapping.setRelationshipPartnerAttributeName()), but it is not recommended, fixing your code to be correct is the best solution.
See,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Object_corruption.2C_one_side_of_the_relationship_is_not_updated_after_updating_the_other_side

Why is this JPA 2.0 mapping giving me an error in Eclipse/JBoss Tools?

I have the following situation:
(source: kawoolutions.com)
JPA 2.0 mappings (It might probably suffice to consider only the Zip and ZipId classes as this is where the error seems to come from):
#Entity
#Table(name = "GeoAreas")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "discriminator", discriminatorType = DiscriminatorType.STRING)
public abstract class GeoArea implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
protected Integer id;
#Column(name = "name")
protected String name;
...
}
#Entity
#Table(name = "Countries")
#DiscriminatorValue(value = "country")
public class Country extends GeoArea
{
#Column(name = "iso_code")
private String isoCode;
#Column(name = "iso_nbr")
private String isoNbr;
#Column(name = "dial_code")
private Integer dialCode = null;
...
}
#Entity
#Table(name = "Zips")
#IdClass(value = ZipId.class)
public class Zip implements Serializable
{
#Id
#Column(name = "code")
private String code;
#Id
#ManyToOne
#JoinColumn(name = "country_code", referencedColumnName = "iso_code")
private Country country = null;
...
}
public class ZipId implements Serializable
{
private String country;
private String code;
...
}
Pretty simple design:
A country is a geo area and inherits the ID from the root class. A ZIP code is unique within its country so it combines an ISO code plus the actual ZIP code as PK. Thus Zips references Countries.iso_code, which has an alternative unique, not-null key on it (reference to non-primary key column!). The Zip.country association gets an #Id annotation and its variable name is the same as the one in its ID class ZipId.
However I get this error message from within Eclipse (also using JBoss Tools):
Validation Message:
"The attribute matching the ID class attribute country does not have the correct type java.lang.String"
Why is this wrong in JPA 2.0 syntax (see #Id annotation on Zip.country)? I don't think it is. After all the types of Zip.country and ZipId.country can't be the same for JPA 2 because of the #Id annotation on the #ManyToOne and the PK being a simple integer, which becomes the ID class counterpart. Can anyone check/confirm this please?
Could this be a bug, probably in JBoss Tools? (Which software component is reporting the above bug? When putting the 3 tables and entity classes into a new JavaSE project there's no error shown with the exact code...)
Answering own question...
The way I modeled the reference, I use a String because the FK points to the iso_code column in the Countries table which is a CHAR(2), so basically my mapping is right. However, the problem is that JPA 2.0 doesn't allow anything but references to primary key columns. This is what the Eclipse Dali JPA validator shows.
Taken from "Pro JPA 2.0" by Keith/Schincariol p.283 top, "Basic Rules for Derived Identifiers" (rule #6): "If an id attribute in an entity is a relationship, then the type of the matching attribute in the id class is of the same type as the primary key type of the target entity in the relationship (whether the primary key type is a simple type, an id class, or an embedded id class)."
Personal addendum:
I disagree with JPA 2.0 having this limitation. JPA 1.0 mappings allow references to non-PK columns. Note, that using JPA 1.0 mappings instead isn't what I'm looking for. I'd rather be interested in the reason why this restriction was imposed on JPA 2.0. The JPA 2.0 is definitely limiting.
I'd say focus your attention on the CompoundIdentity relationship. See this question, and my answer there
Help Mapping a Composite Foreign Key in JPA 2.0
ZipId has no "country" field in your case
I have not tested your code, but it looks pretty much related to the use of the #PrimareKeyJoinColumn annotation.
The JPA 2.0 specification in section 11.1.40 states:
The PrimaryKeyJoinColumn annotation is
used to join the primary table of an
entity subclass in the JOINED mapping
strategy to the primary table of its
superclass; it is used within a
SecondaryTable annotation to join a
secondary table to a primary table;
and it may be used in a OneToOne
mapping in which the primary key of
the referencing entity is used as a
foreign key to the referenced
entity[108].
The example in the spec looks like your case.
#Entity
#Table(name="CUST")
#Inheritance(strategy=JOINED)
#DiscriminatorValue("CUST")
public class Customer { ... }
#Entity
#Table(name="VCUST")
#DiscriminatorValue("VCUST")
#PrimaryKeyJoinColumn(name="CUST_ID")
public class ValuedCustomer extends Customer { ... }
I hope that helps!