Undirectional many to one JPQL select foreign keys - jpa

I read different posts, but I couldnt find a solution that works. I think the OneToMany relation causes the problem. I have to following entities:
// Mandant.class (client)
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "FK_MANDANT")
private List<Leistung> leistungen = new ArrayList<>();
// Mitarbeiter.class (employee)
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "FK_MITARBEITER")
private List<Leistung> leistungen = new ArrayList<>();
// Kategorie.class (category)
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "FK_KATEGORIE")
private List<Leistung> leistungen = new ArrayList<>();
which have undirectional OnetoMany relations. I wrote a method filterLeistungen(...) in the LeistungsRepository.class. Now I want to filter the payments (Leistungen) by vague amount of filters. So I compound the filters with a Stringbuilder to a JPQL-Select.
sb.append("l.mandant = ");
sb.append(mandant.getId());
sb.append(" AND ");
Now I create a query with createQuery().
EntityManager entityManager = PREntityManagerFactory.getInstance().createEntityManager();
RepositoryUtils<List<Leistung>> repositoryUtils = new RepositoryUtils<>();
JpaFunction<List<Leistung>> function = () ->
entityManager.createQuery(sb.toString(), Leistung.class).getResultList();
return repositoryUtils.withoutTransaction(entityManager, function);
But when I execute the query I get the following exception:
Exception in thread "main" java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Problem compiling [SELECT l FROM Leistung l WHERE l.fakturierungsdatum IS NULL AND l.mandant = 1 ORDER BY l.datum].
[64, 73] The state field path 'l.mandant' cannot be resolved to a valid type.
I also tried l.mandant.id or l.fk_mandant.id or l.fk_mandant. I use EclipseLink and a MySQL database. All entities have a primary key called id. Is it possible to filter payments (Leistungen) by foreign keys? The payment doesn't know the other entities, because of the undirectional relation. But on the database, the payment has the foreign key attributes, so it should be possible?

I got a tip in a german forum. With the createNativeQuery() method I'm able to use the database dialect, here MySQL. But I think this isn't a really nice solution, because there is no guarantee for platform independence.

Related

How to join fetch an attribute of element in collection with QueryDSL JPA

Entities as below:
class A {
Long id;
#ManyToMany
#JoinTable(name = "rel_a_b", joinColumns = #JoinColumn(name = "a_id"), inverseJoinColumns = #JoinColumn(name = "b_id"))
Set<B> bSet;
}
class B {
Long id;
#ManyToMany(mappedBy = "bSet")
Set<A> aSet;
#ManyToMany
#JoinTable(name = "rel_b_c", joinColumns = #JoinColumn(name = "b_id"), inverseJoinColumns = #JoinColumn(name = "c_id"))
Set<C> cSet;
}
class C {
Long id;
#ManyToMany(mappedBy = "cSet")
Set<B> bSet;
}
I need to select A entities and join fetch bSet and cSet in B entity. Using JPA Criteria, codes as below:
final Fetch<A, B> bSetFetch = rootA.fetch("bSet", JoinType.LEFT);
bSetFetch.fetch("cSet", JoinType.LEFT);
are working perfectly, but I can't achieve this with QueryDSL. I tried
final QA a = QA.a;
jpaQuery
.from(a)
.leftJoin(a.bSet, QB.b).fetchJoin()
.leftJoin(QB.b.cSet).fetchJoin()
.select(a)
but it throws exception that
query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=b,role=A.b,tableName=`b`,tableAlias=b4_,origin=a a2_,columns={a2_.id ,className=B}}] [select a
from A a
left join fetch a.bSet as b
left join fetch b.cSet]]
, if without fetchJoin(), the results don't include bSet and cSet. Could anyone solve this?
The fetch joins are applied correctly from a QueryDSL point of view. We can also observe this from the fact that the produced JPQL query looks correct.
The limitation here is that Hibernate only allows FETCH JOINS if the owner of the fetch association is projected in the select clause. cSet is an association on B, so you would need to project your b's or omit the fetch join for cSet. For example:
jpaQuery
.from(a)
.leftJoin(a.bSet, QB.b).fetchJoin()
.leftJoin(QB.b.cSet).fetchJoin()
.select(a, b)
Now this will result in duplicate results for a due to the cardinality of bSet. That is just a limitation of fetch joins in Hibernate.
Alternatively, you could consider specifying a fetch graph for the query:
EntityGraph postGraph = em.getEntityGraph("post");
query.setHint("javax.persistence.fetchgraph", postGraph);
For more information on using EntityGraphs see https://www.baeldung.com/jpa-entity-graph

JHipster 4.14.4 : Registering a user with additional information

Following the steps described here I get an org.hibernate.cfg.RecoverableException: Unable to find column with logical name: id in org.hibernate.mapping.Table(user_extra) and its related supertables and secondary tables. It doesn't matter, if I change the entries in the liquibase changelogs regarding "user_extra.user_id". Even if I erase the liquibase changelogs (without the initial_schema) and/or remove the h2-database ( with ./gradlew clean) - every time I get the error.
The link mentioned above is close to the tipp on the jhipster.tech documentation, so I think I am missing something, but I am searching since hours for my failure(s)...
How does jhipster know to map the jhi_user.id to user_extra.user_id (OneToOne) if there is no explicit declaration for this in UserExtra.json? Is the declaration in the domain object UserExtra.java with
#OneToOne
#MapsId
private User user;
enough to trigger it?
Can somebody give me a hint, where Spring Data JPA maps the user_id from the user_extra-table to the #Id Long id of the domain object UserExtra?
My code generated with JHipster 4.14.4 is on github.
Please clarify my questions above, if you like!
Nevertheless I have found my mistakes. Unfortunately, there are no additional ManyToMany-relationships to UserExtra mentioned in the jhipster.tech-tipp - I had to change the user_extra.id to user_extra.user_id not only in the liquibase-changelogs, but also in the domain classes, which are linked ManyToMany to Spring MVC domain class UserExtra!
I faced the same issue, and Based on Jochen Gebsattel's answer, this is what I did:
Update the 202xxxxx_added_entity_constraints_UserExtra.xml
where I had
referencedColumnNames="id"
referencedTableName="user_extra"
I replaced by
referencedColumnNames="user_id"
referencedTableName="user_extra"
user_id being the name of the column containing the foreign key, in my user_extra entity/table
Update the UserExtra class (here again, replace id by user_id)
Where I had many to many relations with other entities, for example
#JoinTable(name = "user_extra_store",
joinColumns = #JoinColumn(name = "user_extra_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "store_id", referencedColumnName = "id"))
I replaced with
#JoinTable(name = "user_extra_store",
joinColumns = #JoinColumn(name = "user_extra_id", referencedColumnName = "user_id"),
inverseJoinColumns = #JoinColumn(name = "store_id", referencedColumnName = "id"))
Note: If you created a new liquibase changelog file in step 1 (instead of update), don't forget to reference it in the master.xml

How to use mappedBy with more than one value JPA

I have 2 tables:
Currency and Rate
The currency's primary key is referenced by the inputCurrency and outputCurrency foreigh keys in the Rate.
How can I map these in order to cascadeALL?
I tried in Rate (the table that contains the foreigh keys)
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "INPUT_CURRENCY")
private Currency inputCurrency;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "OUTPUT_CURRENCY")
private Currency ouputCurrency;
But if I delete an item from currency It results a primary key violation.I can't remove a Currency becouse it is referenced by inputCurrency or outputCurrency in Rate.
If I want to annotate the list of rates in Currency, how can I say mappedBy = "inputCurrency" and "outputCurrency" ?
#OneToMany(cascade = CascadeType.ALL,mappedBy ="inputCurrency,outputCurrency")
List<Rate> rates;
The solution is using two list of rates in Currency: one for the fk inputCurrency
and another for the fk outputCurrency
#OneToMany(cascade = CascadeType.ALL,mappedBy ="inputCurrency")
List<Rate> ratesIC;
#OneToMany(cascade = CascadeType.ALL,mappedBy ="outputCurrency")
List<Rate> ratesOC;
You can settle with two collections:
List<Rate> inputRate;
List<Rate> outputRate;
Each with a corresponding mappedBy
and a third #Transient List<Rate> rates;
You can use a #PostLoad event to unite the two persistent lists into the transient one.
And similarly, when adding/removing elements to one of the persistent lists, make sure to add/remove from the joint list.

JPQL Query working in testing, not in production

I have two Entities related by a ManyToMany and I want to select them via a named Query. This works in my test (with a H2 DB set up) and throws exceptions at runtime (with postgresql set up). Other than the H2 and PG I am hard pressed to find differences between test and production.
The Entities and the Query look like so (abbreviated):
#Entity(name = "Enrichment")
#Table(name = "mh_Enrichment")
NamedQueries({
#NamedQuery(name = "findByLink",
query = "SELECT e FROM Enrichment e INNER JOIN e.links l WHERE l.link in (:links)") })
public class EnrichmentImpl {
#Id
#Column(name = "enrichmentId")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToMany
#JoinTable(name = "mh_EnrichmentLinks", joinColumns = { #JoinColumn(name = "EnrichmentId",
referencedColumnName = "enrichmentId") }, inverseJoinColumns = { #JoinColumn(name = "Link",
referencedColumnName = "link") })
private List<Link> links;
}
#Entity(name = "Link")
#Table(name = "mh_enrichment_link")
public class LinksImpl {
#Id
#Column(name = "link", length = 1024)
private String link;
}
Upon running the query with a String value in production I get:
Internal Exception: org.postgresql.util.PSQLException: ERROR: operator does not exist: character varying = bigint
Hinweis: No operator matches the given name and argument type(s). You might need to add explicit type casts.
Position: 215
Error Code: 0
Call: SELECT t1.enrichmentId FROM mh_enrichment_link t0, mh_EnrichmentLinks t2, mh_Enrichment t1 WHERE ((t0.link IN (?)) AND ((t2.EnrichmentId = t1.enrichmentId) AND (t0.link = t2.Link)))
Any ideas what's wrong? It is the query, isn't it?
The query is supposed to retrieve a list of Enrichments that are related to the given link.
Update #1
As requested: the tables in the DB look as follows:
For entity Link
CREATE TABLE mh_enrichment_link
(
link character varying(1024) NOT NULL,
CONSTRAINT mh_enrichment_link_pkey PRIMARY KEY (link)
)
For entity Enrichment
CREATE TABLE mh_enrichment
(
enrichmentid bigint NOT NULL,
CONSTRAINT mh_enrichment_pkey PRIMARY KEY (enrichmentid)
)
For the relation (See answer, this was where it went wrong)
CREATE TABLE mh_enrichmentlinks
(
link character varying(1024) NOT NULL,
CONSTRAINT mh_enrichment_link_pkey PRIMARY KEY (link)
)
The issue was fixed by dropping all related tables and having JPA regenerate them. Table definitions didn't match Entity definitions.
Thats also the quite obviously the reason why the test worked and the production didn't. In testing the tables are generated on runtime, in production they existed already (with an outdated definition).
Side note: The query is correct and does what it should.

jpa OneToMany & ManyToOne

i have a oneToMany and ManyToOne mapping in my models:
class User
#OneToMany (cascade = { CascadeType.REFRESH, CascadeType.DETACH }, fetch = FetchType.EAGER)
private Set<Judgement> judgements;
class Judgement
#ManyToOne(cascade = { CascadeType.REFRESH, CascadeType.DETACH})
#JoinColumn(name = "user_id")
private User judge;
and in DB, i have to tables as Users and Judgements, when i tried to run my code, it showed error as:
Caused by: org.postgresql.util.PSQLException: ERROR: relation "users_judgements" does not exist
does that mean i have to create the table users_judgements by hand, jpa cannot automatically create the relationship for me? RoR can do it...Thanks.
If you have a foreign key from USER table, user_id, in JUDGMENT table then there is no need to have another table. That will be the #JoinColumn(name = "user_id").
See if you are missing something; I can not see any "mappedBy" attribute, which will be "user" in your case.
Please take a look at this article; page 4 that the link will take you, give details about one-to-many relationship. It will be worth reading the whole article.
http://www.javaworld.com/javaworld/jw-01-2008/jw-01-jpa2.html?page=4
Also, the part 1 of the same tutorial is good for basics;
http://www.javaworld.com/javaworld/jw-01-2008/jw-01-jpa1.html