i want add some more complicated selections as below on date coloumn
select TO_DATE ('04-JAN-2015','DD-MM-YYYY'), to_char(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'FMDAY') day,
(case when to_char(trunc(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'mm'), 'FMDAY') = 'SUNDAY' then to_number(to_char(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'W'))
else ceil((to_char(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'dd') + 1 - to_char(next_day(trunc(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'mm'), 'SUNDAY'), 'dd'))/7)
end)+1 week_no
from dual
here breakupFields is list of string which leads to date column
final QueryBuilder distinctDateTimeFilter = new QueryBuilder() {
final List<String> breakupFields = new ArrayList<String>(fields.length);
for (final String f : fields)
if (!Strings.isEmpty(f)) {
breakupFields.add(f);
}
}
final QueryBuilder distinctDateTimeFilter = new QueryBuilder() {
#Override
public CriteriaQuery buildQuery(CriteriaBuilder cb, CriteriaQuery query, Predicate p, List orders, Root rt) {
Expression selection = null;
selection = cb.function("TO_CHAR", String.class, cb.sum(CriteriaQueryUtils.getPath(rt, breakupFields), cb.literal(miliSecToAdd)),
cb.literal("W-MM-YYYY"));
return query.select(selection).distinct(true).where(p).orderBy(orders);
}
}
final List<Object> objs = new ArrayList<Object>(ds.executeQuery(classObject, Object.class, distinctDateTimeFilter, ef, session,
SuperUserSecurityContext.class));
Can any one suggest how to add
(case when to_char(trunc(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'mm'), 'FMDAY') = 'SUNDAY' then to_number(to_char(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'W'))
else ceil((to_char(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'dd') + 1 - to_char(next_day(trunc(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'mm'), 'SUNDAY'), 'dd'))/7)
end)+1 to criteria builder
You can introduce a column in your Entity marked as
#Formula(value="the expression")
See more here and use the column in the queries.
Or you can define native SQL query with the desired entity class mapped and use fuly native SQL.
Related
I have a parent Entity called CustomerDetailsEntity. It has following one to many mapping to another Entity IBPSDiscrepancyFieldsEntity.
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "Customer_discrepancy_fk", referencedColumnName = "customer_detail_id")
private Set<IBPSDiscrepancyFieldsEntity> ibpsDiscrepancyFieldsEntity;
Now, say for id=123, in IBPSDiscrepancyFieldsEntity Table, I have 4 rows of data.
Now when I hit criteria search on CustomerDetailsEntity I am getting 4 results with same data instead of one. This is how my search criteria is written:
public Map<String, Object> search(Long slrId, String param, String ibpsStatus, Pageable pageable) {
Criteria criteria = getSession().createCriteria(CustomerDetailsEntity.class);
criteria.add(Restrictions.eq("slrDtlId.sellerDtlId", slrId));
criteria.addOrder(Order.desc("modifiedOn"));
if (param != null && !param.isEmpty()) {
Criterion fName = Restrictions.like("customerFirstName", param+"%");
Criterion lName = Restrictions.like("customerLastName", param+"%");
Criterion appNo = Restrictions.like("loanAppNumber", "%"+param+"%");
Disjunction orExp = Restrictions.or(fName, lName, appNo);
criteria.add(orExp);
}
if (ibpsStatus != null && !ibpsStatus.isEmpty()) {
if (ibpsStatus.equalsIgnoreCase(Constant.IBPS_DISCREPANT)) {
criteria.add(Restrictions.eq("ibpsStatus", IBPSStatus.IBPS_DISCREPANT));
} else if (ibpsStatus.equalsIgnoreCase(Constant.IBPS_RESOLVED)) {
criteria.add(Restrictions.eq("ibpsStatus", IBPSStatus.IBPS_RESOLVED));
}
} else {
Criterion discrepant = Restrictions.eq("ibpsStatus", IBPSStatus.IBPS_DISCREPANT);
Criterion resolved = Restrictions.eq("ibpsStatus", IBPSStatus.IBPS_RESOLVED);
LogicalExpression orExp = Restrictions.or(discrepant, resolved);
criteria.add(orExp);
}
criteria.setFirstResult(pageable.getPageSize() * pageable.getPageNumber());
criteria.setMaxResults(pageable.getPageSize());
List<CustomerDetailsEntity> result = (List<CustomerDetailsEntity>) criteria.list();
Map<String,Object> countResultMap = new HashMap<>(2);
countResultMap.put(Constant.QUERY_RESULT, result);
logger.info("##### checking here");
logger.info("--->"+result.size());
criteria.setFirstResult(0);
Long count = (Long) criteria.setProjection(Projections.rowCount()).uniqueResult();
logger.info("Total Count : "+count);
countResultMap.put(Constant.TOTAL_COUNT, count);
return countResultMap;
}
How do I get only 1 result of CustomerDetailsEntity id=123 when I search, instead of 4 duplicate values. In the logs, result.size() shows as 4. Is the Issue with my mapping or criteria method? Please help.
I have my one table like UserTable.
#Entity
public class UserTable{
#PrimaryKey(autoGenerate = true)
private int userId;
private String userName;
private String userEmailId;
// Below code is getter and setter of this class.
}
#Dao
public interface UserDao {
#Query("SELECT * FROM userTable")
public List<UserTable> loadAllUsers();
#Insert
public long insertUserTable(UserTable userTable);
#Insert
public long[] insertUserTables(UserTable... userTables);
#Update
public int updateUserTable(UserTable userTable);
#Delete
public int deleteUserTable(UserTable userTable);
#RawQuery
public abstract List<UserTable> loadAllUserListByGivenIds
(SupportSQLiteQuery query);
public default List<UserTable> loadAllUserListByIds(long[] userIds) {
List<UserTable> list;
ArrayList<Object> argsList = new ArrayList<>();
String selectQuery = "SELECT * FROM UserTable WHERE userId IN (?);";
argsList.add(userIds);
SimpleSQLiteQuery simpleSQLiteQuery = new SimpleSQLiteQuery(selectQuery, argsList.toArray());
list = loadAllUserListByGivenIds(simpleSQLiteQuery);
return list;
}
}
// Now in My MainActivity.class file, I have use following code:
List<UserTable> userList= databaseClient
.getAppDatabase()
.userDao()
.loadAllUserListByIds(new long[]{1L,2L});
My query is running in normal database, but when I was pass array of user ids then, in #RawQuery() method of dao class is not supported for "IN" clause used in where condition "WHERE userId IN (?)".
How, I will use "IN" clause in #RawQuery() of room database.
Much easier to use an #Query it's as simple as:-
#Query("SELECT * FROM UserTable WHERE userId IN (:idList)")
public List<UserTable> getWhatever(long[] idList);
You'd then use getWhatever(new long[]{1L,2L})
If you need it an #rawQuery though you could do it like (used previous answer code for my convenience) :-
private List<TableXEntity> loadAllUserListByIds(int order,long[] idList) {
StringBuilder idListAsCSV = new StringBuilder(); //<<<<<<<<<<
boolean afterFirst = false; //<<<<<<<<<<
//<<<<<<<<<< all of the loop to create the CSV
for (Long l: idList) {
if (afterFirst) {
idListAsCSV.append(",");
}
afterFirst = true;
idListAsCSV.append(String.valueOf(l));
}
StringBuilder sb = new StringBuilder("SELECT * FROM ").append(DBHelper.TableX.NAME);
sb.append(" WHERE " + DBHelper.TableX.COLUMN_ID + " IN(").append(idListAsCSV).append(") "); //<<<<<<<<<<
switch (order) {
case DBHelper.TableX.FIRSTNAME_DESCENDING:
sb.append(DBHelper.TableX.ORDER_BY_FIRSTNAME_DESC);
break;
case DBHelper.TableX.FIRSTNAME_ASCENDING:
sb.append(DBHelper.TableX.ORDER_BY_FIRSTNAME_ASC);
break;
case DBHelper.TableX.LASTNAME_DESCENDING:
sb.append(DBHelper.TableX.ORDER_BY_LASTNAME_DESC);
break;
case DBHelper.TableX.LASTNAME_ASCENDING:
sb.append(DBHelper.TableX.ORDER_BY_LASTNAME_ASC);
break;
default:
break;
}
sb.append(";");
return roomDao.rawq(new SimpleSQLiteQuery(sb.toString(),null));
}
i.e. provide a CSV (although I vaguely recall being able to pass an array)
To use bind arguments (the recommended way as binding arguments protects against SQL injection) then you need a ? for each value and a corresponding array of objects.
So for 3 id's you need IN(?,?,?) and the actual values, the bind arguments, in an Object[]. The following is an example that does this noting that it shows 2 ways of building the Object[] (the bind arguments/values):-
private List<TableXEntity> loadByidList(long[] idlist) {
List<Object> bindargs = new ArrayList<>(); // way 1
Object[] args4Bind = new Object[idlist.length]; // way 2
StringBuilder placeholders = new StringBuilder(); // for the ? placeholders
/* Build the sql before the place holders */
StringBuilder sql = new StringBuilder("SELECT * FROM ")
.append(DBHelper.TableX.NAME)
.append(" WHERE ")
.append(DBHelper.TableX.COLUMN_ID)
.append(" IN (");
boolean afterfirst = false;
int i = 0; /* using for each so have index counter (as opposed to for(int i=0 ....) */
for (long l: idlist) {
bindargs.add(l); // for way 1
args4Bind[i++] = String.valueOf(l); // for way 2
if (afterfirst) {
placeholders.append(",");
}
afterfirst = true;
placeholders.append("?");
}
/* finalise the SQL */
sql.append(placeholders.toString())
.append(");");
//return roomDao.rawq(new SimpleSQLiteQuery(sql.toString(),bindargs.toArray())); // way 1
return roomDao.rawq(new SimpleSQLiteQuery(sql.toString(),args4Bind)); // way 2
}
Please try this, here it has working!
Try this simple trick to pass the arguments for IN operator-
List<Object> argList = new ArrayList<>();
argList.add("3");
argList.add("6");
Then prepare your raw query string:
Note- Match your argument list size with '?' size
String selectQuery = "SELECT * FROM task WHERE id IN (?,?)";
After this pass the raw query string to SimpleSQLiteQuery-
SimpleSQLiteQuery rawQuery = new SimpleSQLiteQuery(selectQuery, args.toArray());
Then fetch the List using DAO:
List<UserTable> taskList1=DatabaseClient
.getInstance(getApplicationContext())
.getAppDatabase()
.userTableDAO()
.getAllList(query);
We can do it in kotlin in the more simpler way.
Let's create two helper methos
object Helper {
fun sqlIn(list: List<Any>, bindArgs: MutableList<Any>): String {
bindArgs.apply { this.addAll(list) }
return "IN (${list.joinToString(",") { "?" }})"
}
fun sqlNotIn(list: List<Any>, bindArgs: MutableList<Any>): String = "NOT ${sqlIn(list, bindArgs)}"
}
Then you can use it in anywhere else
val ids = listOf(1, 2, 3)
val ownerId = 10
val bindArgs = mutableListOf<Any>()
val query = "SELECT * FROM posts WHERE id ${Helper.sqlIn(ids, bindArgs)} AND owner_id = ?"
bindArgs.add(ownerId)
dao.query(
SimpleSQLiteQuery(query, bindArgs.toTypedArray())
)
I have to make a query in criteria api of two tables that is to say I want to select in the query all the fields of the two tables.
Here I leave the models that I want to select (I have the metamodels of each of them) and I give you an example of how to select the data
of each of them in different consultations
public List<Empleado> empleadoB() {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Empleado> criteria = cb.createQuery(Empleado.class);
Root<Empleado> member = criteria.from(Empleado.class);
criteria.select(member).orderBy(cb.asc(member.get(Empleado_.p_ap)));
return em.createQuery(criteria).getResultList();
}
public List<Empleado2> empleadosNom() {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Empleado2> criteria = cb.createQuery(Empleado2.class);
Root<Empleado> member = criteria.from(Empleado.class);
Root<Tipo_docs> tDoc = criteria.from(Tipo_docs.class);
Root<Estad_civil> eCivil = criteria.from(Estad_civil.class);
Root<Profesiones> prof = criteria.from(Profesiones.class);
Root<Eps> eps = criteria.from(Eps.class);
Root<Pensiones> pens = criteria.from(Pensiones.class);
Root<Usuario> useri = criteria.from(Usuario.class);
Root<Cargos> cargo = criteria.from(Cargos.class);
criteria.select(cb.construct(Empleado2.class, member.get(Empleado_.id_emp),
member.get(Empleado_.p_ap), member.get(Empleado_.s_ap),
member.get(Empleado_.nombre), member.get(Empleado_.tipo_doc),
member.get(Empleado_.numero_doc), member.get(Empleado_.lugar_exp),
member.get(Empleado_.fecha_nac), member.get(Empleado_.lugar_nac),
member.get(Empleado_.estadocivil), member.get(Empleado_.direc),
member.get(Empleado_.tel), member.get(Empleado_.profesion),
cb.selectCase() .when(cb.isNull(member.get(Empleado_.matricula_prof)), "No Registrado") .otherwise(member.get(Empleado_.matricula_prof)),
member.get(Empleado_.mail),member.get(Empleado_.eps),
cb.selectCase() .when(cb.isNull(member.get(Empleado_.pensiones)), (long)0) .otherwise(member.get(Empleado_.pensiones)),
cb.selectCase() .when(cb.isNull(member.get(Empleado_.usu)), "No Existe") .otherwise(member.get(Empleado_.usu)),
tDoc.get(Tipo_docs_.desc_tdoc),
eCivil.get(Estad_civil_.desc_ecivil), prof.get(Profesiones_.nombre_prof),
eps.get(Eps_.nom_eps),
cb.selectCase() .when(cb.isNull(member.get(Empleado_.pensiones)), "No Aplica") .otherwise(pens.get(Pensiones_.nom_pension)),
cargo.get(Cargos_.id_cargo), cargo.get(Cargos_.desc_cargo) ));
criteria.where(cb.and(cb.equal(member.get(Empleado_.tipo_doc), tDoc.get(Tipo_docs_.id_entidad)),
cb.equal(member.get(Empleado_.estadocivil), eCivil.get(Estad_civil_.id_ecivil)),
cb.equal(member.get(Empleado_.profesion), prof.get(Profesiones_.id_prof)),
cb.equal(member.get(Empleado_.eps), eps.get(Eps_.id_eps)),
cb.or(cb.equal(member.get(Empleado_.usu), useri.get(Usuario_.usuario)), cb.isNull(member.get(Empleado_.usu))),
cb.or(cb.equal(member.get(Empleado_.pensiones), pens.get(Pensiones_.id_pension)), cb.isNull(member.get(Empleado_.pensiones))) ));
criteria.distinct(true);
return em.createQuery(criteria).getResultList();
}
I have two tables that looks like:
Table_0
ID Description
5 description5
6 description6
7 description7
8 description8
Table_1
ID Table_0_ID Status
1 5 LOADED
2 6 LOADED
3 7 LOADED
4 7 LOADED
Table_2
ID Table_1_ID
1 1
2 2
3 3
4 4
my expected result when I invoke findAll query is that it eliminates duplicates so result will return (for both tables) three rows (table_1 id from 1 to 3).
I wrote both named query and criteria builder query but the last one seems to be by 4 times faster. I wonder why. Am I making a mistake?
Here is the code:
query = "SELECT OBJECT(sb) FROM Table_2 sb WHERE sb.Table_1.id IN (SELECT MAX(maxsr.id) FROM Table_1 maxsr WHERE maxsr.status = LOADED GROUP BY maxsr.Table_0.id)")
criteria builder:
final EntityManager em = getEntityManager();
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<Table_2> criteriaQuery = cb.createQuery(Table_2.class);
final Root<Table_2> root = criteriaQuery.from(Table_2.class);
Predicate p = getPredicateOnList(data, cb, root, gateSession);
if (p != null) {
criteriaQuery.where(p);
}
final Query q = getEntityManager().createQuery(criteriaQuery);
return q.getResultList();
}
method getPredicateOnList
private Predicate getPredicateOnList(final PaginationData data, final CriteriaBuilder cb, final Root<Table_2> root) {
final Join<Table_2, Table_1> readingJoin = root.join("Table_1");
boolean filterUnloaded = false;
boolean selectMax = true;
for (KeyValuePair pair : data.getRequestParams()) {
if (pair.getKey().equalsIgnoreCase("filterUnloaded")) {
filterUnloaded = ParsingUtils.parseBoolean(pair.getValue(), false);
}
if (pair.getKey().equalsIgnoreCase("selectMax")) {
selectMax = ParsingUtils.parseBoolean(pair.getValue(), true);
}
}
Predicate predicate = null;
if (selectMax) {
List<Long> maxReadingIds = getMaxReadingIds(gateSession.getId(), filterUnloaded);
if (maxReadingIds == null || maxReadingIds.isEmpty()) {
//do nothing
} else {
predicate = readingJoin.get("id").in(maxReadingIds);
}
}
return predicate;
}
method getMaxReadingIds
private List<Long> getMaxReadingIds(Long sessionId, boolean filterUnloaded) {
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<Long> maxReadingIdQuery = cb.createQuery(Long.class);
final Root<Table_1> Table_1Root = maxReadingIdQuery.from(Table_1.class);
final Path<Long> idGet = Table_1Root.get("id");
maxReadingIdQuery.select(cb.greatest(idGet));
final Join<Table_1, Table_0> join = Table_1Root.join("Table_0");
maxReadingIdQuery.groupBy(join.get("id"));
Predicate predicate = null;
if (filterUnloaded) {
predicate = cb.equal(Table_1Root.get("status"), LOADED);
}
//omiss sessionId parameter
if (predicate != null) {
maxReadingIdQuery.where(predicate);
}
final Query q = getEntityManager().createQuery(maxReadingIdQuery);
return q.getResultList();
}
(I made some semplification, if code is not well defined please tell me and I supply more information)
Both results are correct but criteria builder is faster.
I'm trying to create this CriteriaQuery with a SelectCase but I'm getting an error. These are my Criteria variables:
CriteriaBuilder cb;
CriteriaQuery cq;
Root<Stransaction> transaction;
Join<Stransaction, Customer> customerJoin;
Join<Customer, Country> countryJoin;
Join<Stransaction, Supplier> supplierJoin;
Join<Stransaction, Productfamily> familyJoin;
In my model everything is joined to Transaction through ID's (Country, Supplier, Customer, Family).
After joining everything with criteria, what I want is this (I know this is a performance punch in the face, I'll fix it later. I'm just learning Criteria):
SUM(CASE WHEN COUNTRY.COUNTRYNAME = :COUNTRYNAME THEN Transaction.CFR ELSE 0)
But this line is giving me an error:
selections.add(cb.sum(cb.<Number>selectCase().when(cb.equal(countryJoin.get("countryname"), country.getCountryname()), (Numeric)transaction.get("cfr")).otherwise(0)));
Especifically here:
(Numeric)transaction.get("cfr")
Because that can't be casted to Numeric. How can I send the Transaction.cfr to the selectCase()?
This is the whole method:
private void generateSelect(FilterRegion filter) throws Exception
{
List<Country> countries = delegate.country().findAll();
List<Selection<?>> selections = new LinkedList<>();
if(filter.getContext() == FAMILY_CTX)
{
selections.add(familyJoin.get("prodfamname"));
cq.groupBy(familyJoin.get("prodfamname"));
}
else if(filter.getContext() == CUSTOMER_CTX){
selections.add(customerJoin.get("customername"));
cq.groupBy(customerJoin.get("customername"));
}
else if(filter.getContext() == SUPPLIER_CTX){
selections.add(supplierJoin.get("suppliername"));
cq.groupBy(supplierJoin.get("suppliername"));
}
for(Country country : countries)
selections.add(cb.sum(cb.<Number>selectCase().when(cb.equal(countryJoin.get("countryname"), country.getCountryname()), (Numeric)transaction.get("cfr")).otherwise(0)));
cq.multiselect(selections);
}
transaction.get("cfr").as(Number.class)