ebean order by with oneToMany property with pagination - pagination

I'm trying to get paginates orders, sorted on the cration date of the current version (currentVersion = true)
Here is the Order class :
#Entity
public class Offer extends Model {
#Id
public Long id;
....
#OneToMany(mappedBy = "offer", fetch = FetchType.EAGER)
public List<Version> versions;
...
}
Here is the version class
#Entity
public class Version extends Model {
#Id
public Long id;
#ManyToOne
#JoinColumn(name = "OFFER_ID")
public Offer offer;
public Boolean currentVersion = false;
public Date creation;
...
}
Here is the fetch code as I found on eBean documentation :
Query<Offre> query = Ebean.find(Offer.class);
List<Offer> offers = query.fetch("versions")
.where()
.eq("versions.currentVersion", true)
.orderBy("versions.creation desc nulls last")
.setFirstRow(0)
.setMaxRows(10)
.findPagedList().getList();
Here is the expected SQL
SELECT * FROM
(SELECT /*+ FIRST_ROWS(10) */ rownum rn_, a.* FROM
(SELECT t0.id c0,
...
t0.OPTLOCK c32
FROM offer t0
INNER JOIN version t1 on t0.id = t1.OFFER_ID
ORDER BY t1.creation
) a
WHERE rownum <= 10
) ; --bind()
Here is the actual SQL
First one:
SELECT *
FROM
(SELECT
/*+ FIRST_ROWS(10) */
rownum rn_,
a.*
FROM
( SELECT DISTINCT t0.id c0,
...
t0.id
...
FROM offer t0
JOIN versions u1 ON u1.OFFER_ID = t0.id
WHERE u1.current_version = true
AND u1.current_version = true
ORDER BY t0.id
) a
WHERE rownum <= 10
) ;
Second one :
SELECT t0.OFFER_ID c0,
t0.id c1,
...
t0.creation c3,
...
t0.current_version c12,
...
t0.OFFER_ID c30,
...
FROM versions t0
WHERE (t0.OFFER_ID) IN (990,991,992,993,994,995,996,997,998,999)
ORDER BY t0.creation;
Question : What do I need to change to get the expected SQL ?
Thank you very much for help
Versions used :
ebean 7.6.1 from play 2.5.14

I found a walk-around:
Instead of
List<Offer> offers = query.fetch("versions")
.where()
.eq("versions.currentVersion", true)
.orderBy("versions.creation desc nulls last")
.setFirstRow(0)
.setMaxRows(10)
.findPagedList().getList();
I've found :
List<Offer> offers = query.fetch("versions")
.where()
.eq("versions.currentVersion", true)
.orderBy("u1.creation desc") // <-- line with the change
.setFirstRow(0)
.setMaxRows(10)
.findPagedList().getList();
I make sure that the join between offer and versions is made only once and than assume that version join alias will be u1.
At least, it's doning the job.
Bye

Related

Android Studio - Kotlin - SQLite Database isn't changing the ID for one of the corresponding tables

I'm trying to implement my first SQLite Database in an Android App regarding obtaining location coordinates to keep track of where the user has been.
I'm trying to add information from my entry into two tables:
a Location table that contains information of the places name, id, latitude, and longitude information &
a CheckIn table that contains information of the places address, corresponding location_id to know which location it corresponds to, latitude, longitude, and time of check in.
Whenever I try to do this, my entry is never updated for the Locations table, solely the CheckIn table, despite using the insert() function to insert into the Locations table as well the id is not updating for the Location table.
I've went through my app in a debugger and I can't figure out what's causing the problem here, as there's no error and the program proceeds just fine to add in the necessary info for the CheckIn table.
I've tried checking StackOverFlow but I can't quite find anything that has been able to help fix my problem. If there's anyone who could help me, it'd be greatly appreciated
My add function:
fun addLoc_CheckIn(Entry: Locations)
{
val selectQuery = "SELECT * FROM $LOCATIONS ORDER BY ID"
val db = this.readableDatabase
val cursor = db.rawQuery(selectQuery, null)
var con = 0
if (cursor.moveToFirst())
{
while (cursor.moveToNext())
{
val pSLong = cursor.getDouble(cursor.getColumnIndex(SLONG))
val pCLong = cursor.getDouble(cursor.getColumnIndex(CLONG))
val pSLat = cursor.getDouble(cursor.getColumnIndex(SLAT))
val pCLat = cursor.getDouble(cursor.getColumnIndex(CLAT))
val Theta = (pCLong * Entry.cLong) + (pSLong * Entry.sLong)
var dist = (pSLat * Entry.sLat) + (pCLat * Entry.cLat * Theta)
// dist = (Math.acos(dist) * 180.00 / Math.PI) * (60 * 1.1516 * 1.609344) / 1000
dist = Math.acos(dist) * 6380000
if (dist <= 30)
{
con = 1
val db1 = this.writableDatabase
val values = ContentValues()
values.put(LOC_ID, cursor.getInt(cursor.getColumnIndex(ID)))
values.put(ADDRESS, Entry.Checks[0].Address)
values.put(LATI, Entry.Lat)
values.put(LONGI, Entry.Long)
values.put(TIME, Entry.Checks[0].Date_Time)
db1.insert(CHECKINS, null, values)
break
}
}
}
if (con == 0)
{
val db1 = this.writableDatabase
val values = ContentValues()
values.put(LOC_NAME, Entry.Name)
values.put(LAT, Entry.Lat)
values.put(LONG, Entry.Long)
values.put(CLAT, Entry.cLat)
values.put(SLAT, Entry.sLat)
values.put(CLONG, Entry.cLong)
values.put(SLONG, Entry.sLong)
Entry.Id = db1.insert(LOCATIONS, null, values)
val cvalues = ContentValues()
cvalues.put(LOC_ID, Entry.Id)
cvalues.put(ADDRESS, Entry.Checks[0].Address)
cvalues.put(LATI, Entry.Lat)
cvalues.put(LONGI, Entry.Long)
cvalues.put(TIME, Entry.Checks[0].Date_Time)
db1.insert(CHECKINS, null, cvalues)
}
}
My OnCreate function with the corresponding companion object:
companion object {
private val DATABASE_NAME = "LocationsDB"
private val DATABASE_VERSION = 1
// 1st Table - Unique Check Ins
private val LOCATIONS = "LOCATIONS"
private val ID = "ID"
private val LOC_NAME = "LOC NAME"
private val LAT = "LAT"
private val LONG = "LONG"
private val CLAT = "CLAT"
private val SLAT = "SLAT"
private val CLONG = "CLONG"
private val SLONG = "SLONG"
// 2nd Table - Repeated Check Ins
private val CHECKINS = "CHECKINS"
private val CHECKIN_ID = "CHECKIN_ID"
private val LOC_ID = "LOC_ID"
private val ADDRESS = "ADDRESS"
private val TIME = "TIME"
private val LATI = "LAT"
private val LONGI = "LONG"
}
override fun onCreate(p0: SQLiteDatabase?) {
val LOCATION_QUERY = "CREATE TABLE " + LOCATIONS + "(" + ID +
" INTEGER PRIMARY KEY AUTOINCREMENT, " + LOC_NAME +
" TEXT, " + LAT + " INTEGER, " + LONG + " INTEGER, " +
CLAT + " INTEGER, "+ SLAT + " INTEGER, " + CLONG + " INTEGER, "+ SLONG + " INTEGER " + ")"
val CHECKIN_QUERY = "CREATE TABLE " + CHECKINS + "(" +
LOC_ID + " INTEGER, " + CHECKIN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + LATI + " INTEGER, " + LONGI + " INTEGER, " + ADDRESS +
" TEXT, " + TIME + " TEXT " + ")"
p0!!.execSQL(LOCATION_QUERY)
p0.execSQL(CHECKIN_QUERY)
}
Now, in my constructor for the Location class and the CheckIns class, I have the id's set to -1, which is what the id for the location remains, even after using the insert() function. Now, this doesn't cause me any issues with regards to adding in my CheckIns as well incrementing the ids in my CheckIns table and I doubt it's causing an issue but I figured it'd be best to include the information, just in case.
I believe that you have an issue with the name of the column due to using
private val LOC_NAME = "LOC NAME"
A column name cannot have a space unless it is enclosed in special characters as per SQL As Understood By SQLite - SQLite Keywords.
This isn't an issue when the table is create (the column name will be LOC). However, when you attempt to insert you will get a syntax error, the row will not be inserted but as you are using the SQLiteDatabase insert method, the error is trapped and processing continues.
However, in the log you would see something similar to :-
2019-10-29 15:47:35.119 12189-12189/aso.so58600930insert E/SQLiteLog: (1) near "NAME": syntax error
2019-10-29 15:47:35.121 12189-12189/aso.so58600930insert E/SQLiteDatabase: Error inserting LOC NAME=MyLoc LAT=100 CLAT=120 LONG=110 SLAT=140 CLONG=130 SLONG=150
android.database.sqlite.SQLiteException: near "NAME": syntax error (code 1 SQLITE_ERROR): , while compiling: INSERT INTO LOCATIONS(LOC NAME,LAT,CLAT,LONG,SLAT,CLONG,SLONG) VALUES (?,?,?,?,?,?,?)
You could circumvent the above by using :-
val db1 = this.writableDatabase
val values = ContentValues()
values.put("LOC", Entry.Name)
values.put(LAT, Entry.Lat)
values.put(LONG, Entry.Long)
values.put(CLAT, Entry.cLat)
values.put(SLAT, Entry.sLat)
values.put(CLONG, Entry.cLong)
values.put(SLONG, Entry.sLong)
Entry.Id = db1.insert(LOCATIONS, null, values)
However, it is not suggested that you use the above BUT that instead you correct the name, e.g. using :-
private val LOC_NAME = "LOC_NAME"
then clear the App's data or uninstall the App and then rerun the App.
This fix assumes that you are developing the App and can afford to lose any existing data. You could retain data but this is a little more complicated as you basically have to create a new table with the appropriate column name, copy the data from the original table, rename or drop the original table and then rename the new table to be the original name.

Entities not referenced by another entity

In the following relationship, I would like to find instances of entity A not referenced by B.
#Entity
public class A {
}
#Entity
public class B {
#OneToOne(fetch = FetchType.LAZY)
#NotFound(action = NotFoundAction.IGNORE)
#JoinColumn(name = "a_id")
private A a;
}
How can I write a JPQL query for this?
You can do the left join with A and check if b.id is null to find all
rows of A which doesn't have reference in B.
JPQL:
SELECT a FROM A a LEFT JOIN B b on b.a = a where b.id IS NULL
Mysql:
select a.id from A a left join B b on b.a_id = a.id where b.id is NULL;

Cassandra CQL where clause parentheses

Having this Entity
#Table(keyspace = KEYSPACE)
public class CE_TimeSeries extends Entity implements TimeSeriesPoint{
#PartitionKey(1)
private String typeId;
#ClusteringColumn(value=1, asc=true)
private String userId;
#ClusteringColumn(value=2, asc=true)
private Date startDate;
#Column
private Date endDate;
#Column
private int groupInterval;
#Column
private int interval;
}
This CQL
SELECT startDate, endDate, groupInterval, interval FROM CE_TimeSeries WHERE typeId
= :typeId and userId = :userId and ( endDate >= :fromDate or ( startDate >=
:fromDate and startDate <= :toDate ) )
Give the Exception :
Caused by: com.datastax.driver.core.exceptions.SyntaxError: line 1:142
mismatched input 'or' expecting ')' (... ( endDate >= :fromDate [or] (...)
While I don't actually see a question here, I'll assume that you are wondering why you are getting an exception. There are two things wrong with your query.
CQL does not allow the use of OR in the WHERE clause.
CQL does not allow parens in the WHERE clause. Plus, not having OR available kind of precludes the need for parens.
The bottom line, is that CQL is not SQL, and the logic you can apply in the WHERE clause is largely dependent on the storage model.

JPQL BETWEEN Date Interval

I tried to get all MainIntervals in a certain date interval. But I get always null.
Here is the JPA Entity
#Table(name="MAIN_INTERVAL")
#NamedQueries({
#NamedQuery(name = MainInterval.FIND_ALL, query = " select m from MainInterval m"),
#NamedQuery(name = MainInterval.FIND_BETWEEN,
query = "select m from MainInterval m where m.mainIntervalStart
BETWEEN :startDate AND :endDate
AND m.mainIntervalEnd BETWEEN :startDate AND :endDate"
) })
public class MainInterval implements Serializable {
public static final String FIND_ALL = "MainInterval.findAll";
public static final String FIND_BETWEEN = "MainInterval.findBetween";
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="MAIN_INTERVAL_ID")
private Long id;
#Column(name="MAIN_INTERVAL_START")
#Temporal(javax.persistence.TemporalType.DATE)
private Date mainIntervalStart;
#Column(name="MAIN_INTERVAL_END")
#Temporal(javax.persistence.TemporalType.DATE)
private Date mainIntervalEnd; }
And in the EJB SessionBeans I have the method:
public List<MainInterval> findMainIntervalsBetween(Date startDate, Date endDate){
List<MainInterval> resultList = em.createNamedQuery(MainInterval.FIND_BETWEEN, MainInterval.class)
.setParameter("startDate", startDate, TemporalType.DATE).setParameter("endDate", endDate, TemporalType.DATE).getResultList();
return resultList;
}
But when I call it from JSF with CDI the resultList is always null. Although I have some MainIntervals that meet the conditions between startDate and endDate.
I would be very grateful to every answer or links for tutorial.
Best regards!
Your query is:
select m from MainInterval m where m.mainIntervalStart
BETWEEN :startDate AND :endDate
AND m.mainIntervalEnd BETWEEN :startDate AND :endDate
Your first row's interval is [01.05.2012, 31.05.2012], and your second row's interval is [01.05.2012, 01.08.2012]. The arguments of the query are 10.05.2012, 20.05.2012.
So, for your first row:
m.mainIntervalStart BETWEEN :startDate AND :endDate
01.05.2012 BETWEEN 10.05.2012 AND 20.05.2012 : false
So the first row is not returned.
For your second row:
m.mainIntervalStart BETWEEN :startDate AND :endDate
01.05.2012 BETWEEN 10.05.2012 AND 20.05.2012 : false
So the second row isn't returned either.
Everything looks normal to me.

JPA Eclipselink Subquery in where clause with between, CriteriaBuilder and metamodel

I want to do this query with metamodel but I can't!! I dont know how to do this.
MYSQL QUERY (with this query I want to get all the rows from the Clases table that are teaching in this moment):
SELECT * FROM clases cl
WHERE CURRENT_TIME() BETWEEN
(SELECT ml2.inicio FROM modulos ml2 WHERE cl.modulo_id=ml2.modulo_id ) AND
(SELECT ml2.fin FROM modulos ml2 WHERE cl.modulo_id=ml2.modulo_id) AND
cl.fecha=CURRENT_DATE();
These are my entities:
ENTITY MODULOS
#Entity
#Table(name = "modulos")
public class Modulos implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "modulo_id")
private Integer moduloId;
#Basic(optional = false)
#Column(name = "inicio")
#Temporal(TemporalType.TIME)
private Date inicio;
#Basic(optional = false)
#Column(name = "fin")
#Temporal(TemporalType.TIME)
private Date fin;
#Basic(optional = false)
#Column(name = "modulo")
private String modulo;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "modulos")
private List<GruposHorarioHasModulos> gruposHorarioHasModulosList;
//getters and setters...
}
ENTITY CLASES
#Entity
#Table(name = "clases")
public class Clases implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "clase_id")
private Integer claseId;
#Basic(optional = false)
#Column(name = "aula")
private String aula;
#Basic(optional = false)
#Column(name = "fusion")
private boolean fusion = false;
#Basic(optional = false)
#Column(name = "clase_numero")
private Integer claseNumero;
#Basic(optional = false)
#Column(name = "clase_impartida")
private boolean claseImpartida = false;
#JoinColumn(name = "modulo_id", referencedColumnName = "modulo_id")
#ManyToOne(optional = false)
private Modulos modulos;
//getters and setters...
}
I have this:
EntityManager em1 = Persistence.createEntityManagerFactory("myPU").createEntityManager();
CriteriaBuilder cb = em1.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery();
Root<Clases> root = cq.from(Clases.class);
cq.select(root.get(Clases_.claseId));
Subquery<Date> sqOne = cq.subquery(Date.class);
Root<Modulos> root2 = sqOne.from(Modulos.class);
sqOne.select(root2.get(Modulos_.inicio));
Subquery<Date> horaInicio = sqOne.select(root2.get(Modulos_.inicio)).where(
cb.equal(
root2.get(Modulos_.moduloId),
root.get(Clases_.modulos).get(Modulos_.moduloId)
)
);
Subquery<Date> sqTwo = cq.subquery(Date.class);
Root<Modulos> root3 = sqTwo.from(Modulos.class);
Subquery<Date> horaFin = sqTwo.select(root3.get(Modulos_.fin)).where(
cb.equal(
root3.get(Modulos_.moduloId),
root.get(Clases_.modulos).get(Modulos_.moduloId)
)
);
cq.where(cb.between(cb.currentTime(), horaInicio, horaFin));
em1.createQuery(cq).getResultList();
This code gives me the following exception:
Exception in thread "main" java.lang.ClassCastException:
org.eclipse.persistence.internal.jpa.querydef.SubQueryImpl cannot be
cast to org.eclipse.persistence.internal.jpa.querydef.ExpressionImpl
If i change the where clause for this one...
cq.where(cb.between(cb.currentTime(), new Date(), new Date()));
... it works but without my Subquery, then i can see the error came from my subquery, but I don't know why, if I change the where clause for this one...
cq.where(cb.greaterThan(cb.currentTime(), horaInicio));
... I get this:
SELECT t0.clase_id FROM clases t0, clases t1 WHERE (CURRENT_TIME > (SELECT t2.inicio FROM modulos t3, modulos t2 WHERE ((t2.modulo_id = t3.modulo_id) AND (t3.modulo_id = t0.modulo_id))))
I can see the problem are the 2 subqueries in the between clause.
Please I need help with this, I spent 2 weeks looking for an answer but... nothing... help.
I'm using JPA 2.0 with Netbeans and EclipseLink with metamodel generator and Java 6.
I want to do it with metamodels and criteriasbuilder and criteriasquerys
As you can see i need to do a subquery in the where clause and in that where clause I need to do a between where each parameter have a subquery, like this:
SELECT * FROM X WHERE CURRENT_TIME BETWEEN **MY_SUBQUERY_ONE** AND **MY_SUBQUERY_TWO**
Abit late answer but..
Subquery<Date> sqOne = cq.subquery(Date.class);
Root<Modulos> root2 = sqOne.from(Modulos.class);
sqOne.select(root2.get(Modulos_.inicio));
Subquery<Date> horaInicio = sqOne.select(root2.get(Modulos_.inicio)).where(
cb.equal(
root2.get(Modulos_.moduloId),
root.get(Clases_.modulos).get(Modulos_.moduloId)
)
);
Not sure what you are trying to do here, and it might or might not be related to your error. You create sqOne and make a selection. Then you redo the selection, replacing the previous one and copy the result to horaInicio. What is the purpose of this? You might aswell skip sqOne.select(root2.get(Modulos_.inicio)); and keep using sqOne instead of horaInicio.
Also, I'm not sure you can mix and match Roots created from different queries.

Resources