JPA Eclipselink PostgreSQL Query field for Null values - jpa

I stumbled over a very strange behaviour with JPA Eclipselink and PostgreSQL.
create table test(id bigint, name varchar(255))
insert into test values(1, "hello")
insert into test values(2, null)
Java EE Entity Bean:
#Entity
#Table(name = "test")
public class Test implements Serializable {
#Id
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
Stateless Bean with simple JPA Query:
TypedQuery<Test> q = em.createQuery("select t from Test t where t.name = :name", Test.class);
q.setParameter("name", null);
List<Test> list = q.getResultList();
for (Test t : list) {
System.out.println(t.getId() + " " + t.getName());
}
Result:
Aspected: Row with id=2 with null in name field
Current behaviour: No rows returned
When I use "is null" instead of example above in JPA Query. Then I do get something back.
What does JPA do?
select * from test where t.name = null
should this not be the same as
select * from test where t.name is null

JPQL also has the 'is null' comparator that you are required to use to compare to null values. While some JPA providers can interpret and determine that the value passed into an equality is null, and therefore use "IS NULL" in the generated SQL, this is generally not viable when using parameters when the value can change as it means the statement underneath the query must change as well.

Related

Numeric types mapping issue in Spring Data R2dbc with postgres

I tried to use Spring Data R2dbc/Postgres in a sample application.
Spring Boot 2.4.0-M2
R2dbc Postgres (managed by Spring Boot)
Spring Data R2dbc 1.2.0-M2(managed by Spring Boot)
The table scripts.
CREATE SEQUENCE IF NOT EXISTS ORDERS_ID_SEQ;
CREATE TABLE IF NOT EXISTS ORDERS(
ID INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('ORDERS_ID_SEQ') ,
CUST_ID BIGINT NOT NULL,
AMOUNT REAL NOT NULL
);
ALTER SEQUENCE ORDERS_ID_SEQ OWNED BY ORDERS.ID;
The data.sql:
-- INSERT SAMPLE DATA
DELETE FROM ORDERS;
INSERT INTO ORDERS(CUST_ID, AMOUNT) VALUES (1, 100.2);
I use a ResourceDatabasePopulator to populate the data, it works.
But when I was trying to save the data by Repository, failed.
#Table(value = "ORDERS")
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class Order implements Serializable {
#Id
#Column(value = "ID")
private Integer id;
#Column(value = "CUST_ID")
private Long customerId;
// use BigDecimal or Java Money API in the real-world application.
#Column(value = "AMOUNT")
private Double amount;
}
public interface OrderRepository extends R2dbcRepository<Order,Integer> {
}
// in application runner.
orders .save(Order.builder().customerId(c.getId()).amount(201.0).build())
It threw an exception like this:
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.UnsupportedOperationException: Binding parameters is not supported for the statement 'INSERT INTO ORDERS (CUST_ID, AMOUNT) VALUES (?, ?)'
Caused by: java.lang.UnsupportedOperationException: Binding parameters is not supported for the statement 'INSERT INTO ORDERS (CUST_ID, AMOUNT) VALUES (?, ?)'
at io.r2dbc.postgresql.SimpleQueryPostgresqlStatement.bind(SimpleQueryPostgresqlStatement.java:78) ~[r2dbc-postgresql-0.8.4.RELEASE.jar:0.8.4.RELEASE]
at io.r2dbc.postgresql.SimpleQueryPostgresqlStatement.bind(SimpleQueryPostgresqlStatement.java:44) ~[r2dbc-postgresql-0.8.4.RELEASE.jar:0.8.4.RELEASE]
The complete codes is here.
Updated: Give up extending from AbstractR2dbcConfiguration, and get resovled when following the official guide.

spring boot JPA returns null object when it finds a null attribute eg date is null

I wanted to load an object from the database that contains more
attributes, one of which is null (e.g : date), using spring boot JPA
that returns a null object because it found the date is null despite
the object exists in the database. Please who can who can tell me how I solve this problem
#Entity #IdClass(Exemple.class) #Table(name= "table") public class
Exemple implements Serializable{
#Id #Column(columnDefinition = "id_activite") private Integer idActivite;
//the Problem is here
#Column(columnDefinition = "date_debut_prevue") #Nullale private Calendar dateDebutPrevue;
//Other attribute and Id
#Id ...
// Getters && setters
}
//Query Repository
#Query("SELECT ar FROM table ar where ar.idActivity = ?1 ")
List<Activitie> findAllPositionByIdActivitie(Integer idActivitie);
Your query is a native query and not a JPQL query.
The query must use the Entity name in the FROM clause not the table name.
#Query("SELECT ar FROM Exemple ar where ar.idActivity = ?1 ")
List<Activitie> findAllPositionByIdActivitie(Integer idActivitie);

Rewrite SQL query with JOINS in JPA

I have this SQL query for MariaDB.
select #ref:=id as id, unique_id, reference_id
from mytable
join (select #ref:=id from mytable WHERE unique_id = 55544)tmp
where reference_id=#ref
https://www.db-fiddle.com/f/jKJodfVfvw65aMaVDyFySd/0
How this query can be implemented in HQL query? I would like to use it in JPA?
(Answer largely re-written after comments below)
JPA doesn't have built-in support for hierarchical queries. The main option is a native query.
E.g. with this entity class:
#Entity
public class MyTable {
#Id
#GeneratedValue
private int id;
private int uniqueId;
#ManyToOne
private MyTable reference;
// ... getters and setters ...
}
The following is an example of a native hierachical SQL query (actually against MySQL, just in case):
Query query = entityManager.createNativeQuery(
"select #ref\\:=id as id, unique_id, reference_id\r\n" +
"from my_table\r\n" +
"join (select #ref\\:=?)tmp\r\n" +
"where reference_id=#ref",
MyTable.class);
query.setParameter(1, 1);
query.getResultList();
This was chasing down a chain of references successfully.
(Other alternatives)
There probably aren't too many other options that can do this as a single query. If scalability is less of a concern, adding a back reference would be a simple way to navigate the model:
#OneToMany(mappedBy = "reference")
private Set<MyTable> backReferences;
Those would then be straightforward to recursively navigate. Clearly the relation defaults to lazy loading, so would add little overhead until used.
With #df778899's MyTable in spring-data it could look like:
#Repository
public interface MyRepository extends ...
#Query("select #ref:=id as id, unique_id, reference_id "+
"from mytable join (select #ref:=id from mytable WHERE unique_id = :pUid) tmp "+
"where reference_id=#ref", //just copy paste the query, use :pUid instead of constant...
nativeQuery = true) // and this!
List<MyTable> myCustomHirachicalQuery(#Param("pUid") Integer uid/*String/Long/...*/);
...

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.

Using CriteriaBuilder to select entities referenced by refTable

I'm very new to JPA (based on eclipselink) so be patient with me and my question. I found JPA is an elegant way to handle simple DB statements. But, now I'd like to do some more complicated task.
So let me explain what I want: I've got a table holding person data (lets say the persons name). In an other table I'd like to keep address lists. Because a person way part of several adresslists and one adress list may keep several persons I need an ref-table jo join these collections.
So my DB-tables look like this:
SQL (not exact DDL syntax but it helps to understand!) --without constraints etc
--the person keeps data of persons
CREATE TABLE a_person
(
oid serial PrimaryKey NOT NULL,
vorname character varying(50),
nachname character varying(50) NOT NULL
--
)
--the adresslist is quite simple: just a name of the list is storred
CREATE TABLE a_adressliste (
(
oid serial PrimaryKey NOT NULL,
label character varying(25) NOT NULL
--<more collumns here>
)
--the table refering the list the the containing persons
CREATE TABLE a_listen_person
(
fk_adressliste bigint PrimaryKey NOT NULL, --ref to a_person.oid
fk_person bigint PrimaryKey NOT NULL, --ref to a_adressliste.oid
)
Having this structure and some data in the DB it's easy to select the adress lists using the following SQL statement:
select * from a_person p where p.oid in (
select
lp.fk_person
from
a_listen_person lp, a_adresssliste al
where
lp.fk_adressliste = al.oid AND al.label = {LISTE_LABEL}
)
Now, According tho the structure within the DB, I've got the corresponding JAVA POJO's to these tables (I've skipped the annotations to keep my code a little shorter)
JAVA
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "oid")
private Integer oid;
private String vornmae;
private String nachname;
//getter; setter and the other missing stuff...
}
public class Adressliste implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "oid")
private Integer oid;
private String label;
}
public class ListenPerson implements Serializable {
#EmbeddedId
private ListenPersonPK listenPerson;//build from Person.oid and Adressliste.oid
private Person person;
private Adressliste adresssliste;
//getter; setter and the other missing stuff...
}
Now I've written a finder method in JAVA where I use a CriteriaBuilder to filter the entities by several attributes (according to the Person POJO). But I did not manage to select the Person according to a given list name.
By now my method looks like this:
public TypedQuery<Person> prepareQueryFiltered(Filter filter) {
TypedQuery<Person> retVal;
EntityManager em = this.getEntityManager();
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Person> query = builder.createQuery(Person.class);
Root<Person> cust = query.from(Person.class);
query.select(cust);
List<Predicate> predicateList = new ArrayList<Predicate>();
Predicate predicate;
if (null != filter.getVorname()) {
predicate = builder.like(
builder.lower(cust.<String>get("vorname")),
"%" + filter.getVorname().toLowerCase() + "%");
predicateList.add(predicate);
}
if (null != filter.getNachname()) {
predicate = builder.like(
builder.lower(cust.<String>get("nachname")),
"%" + filter.getNachname().toLowerCase() + "%");
predicateList.add(predicate);
}
//some more filtered attributes ...
query.where(predicateList.toArray(new Predicate[predicateList.size()]));
retVal = em.createQuery(query);
return retVal;
}
As you can imagine the filter attribute keeps all the data to filter my entities with. But how does the code look like if I'd like to get all the person entities belonging to a given list name?
I started to use 'Subquery's but I did not get the correct syntax. Could you please give me some hints?
For examples of sub-queries in the Criteria API see,
http://en.wikibooks.org/wiki/Java_Persistence/Criteria#Subquery
Also, I don't think you need a sub-query, a simple join should work.
Select p from Person p, ListenPerson l where l.person = p and l.adresssliste.label = :label