I'm currently working on a fetaure that will allow the system to search public services receipts by the combination of 6 parameters which can be null meaning that receipts shouldn't be filtered by this parameter: accountNumber, amountRangeMin, amountRangeMax, dateRangeMin, dateRangeMax, publicServiceId. However making a method for each combination of the parameters is not an option, I'm thinking that there must be a better way, at first my approach was as following:
On my Service I have this method:
public Map<String,Object> findPublicServiceReceiptsByParams(Integer accountNumber, BigDecimal amountRangeMin,
BigDecimal amountRangeMax, LocalDate dateRangeMin, LocalDate dateRangeMax, Integer publicServiceId) {
Map<String,Object> publicServiceReceipts = new HashMap<String,Object>();
String accountNumberFilter = !(accountNumber==null) ? accountNumber.toString() : "AccountNumberTableName";
String amountRangeMinFilter = !(amountRangeMin==null) ? amountRangeMin.toString() : "table.AmountColumnName";
String amountRangeMaxFilter = !(amountRangeMax==null) ? amountRangeMax.toString() : "table.AmountColumnName";
String dateRangeMinFilter = !(dateRangeMin==null) ? dateRangeMin.toString() : "Table.ReceiptCreationDateColumn";
String dateRangeMaxFilter = !(dateRangeMax==null) ? dateRangeMax.toString() : "Table.ReceiptCreationDateColumn";
String publicServiceIdFilter = !(publicServiceId==null) ? publicServiceId.toString() : "table.publicServiceIdColumn";
publicServiceReceipts = publicServiceReceiptRepository.findPublicServiceReceiptsByParams(accountNumberFilter,
amountRangeMinFilter, amountRangeMaxFilter, dateRangeMinFilter, dateRangeMaxFilter,
publicServiceIdFilter);
return publicServiceReceipts;
}
And then in my repository I had:
final static String FIND_PUBLIC_SERVICES_BY_ARGS = "Select (Insert whatever logic should go in here to select columns from receipts the where clause is the one that matters)"
+ " WHERE ACT.ACT_AccountNumber=:accountNumberFilter\n"
+ " AND PSE.PSE_Id=:publicServiceIdFilter\n"
+ " AND PSR.PSR_CreateDate BETWEEN :dateRangeMinFilter AND :dateRangeMaxFilter\n"
+ " AND PSR.PSR_Amount BETWEEN :amountRangeMinFilter AND :amountRangeMaxFilter\n"
+ " order by PSR.PSR_CreateDate desc";
#Query(nativeQuery = true, value = FIND_PUBLIC_SERVICES_BY_ARGS)
Map<String, Object> findPublicServiceReceiptsByParams(#Param("accountNumberFilter") String accountNumberFilter,
#Param("amountRangeMinFilter") String amountRangeMinFilter,
#Param("amountRangeMaxFilter") String amountRangeMaxFilter,
#Param("dateRangeMinFilter") String dateRangeMinFilter,
#Param("dateRangeMaxFilter") String dateRangeMaxFilter,
#Param("publicServiceIdFilter") String publicServiceIdFilter);
}
My reasoning was that if a parameter was null meant that whoever consumed the Web Service is not interested in that paramater so if that happens I set that variable as the Column Name so that it wouldn't affect in the WHERE clause and in theory make it simpler, but what I found was that It would send the names as Strings so it wouldn't be recognized as an sql statement which was the flaw in my thinking and as I said there must be another way other than writing each method for each combination, I appreciate any help :).
You should use the Criteria API, which was designed for creating dynamic queries. Named queries aren't really meant to be used in this case.
With it you can do something like this:
#PersistenceContext
EntityManager em;
List<YourEntity> method(String argument) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<YourEntity> cq = cb.createQuery(YourEntity.class);
Root<YourEntity> root = cq.from(YourEntity.class);
List<Predicate> predicates = new ArrayList<>();
if (argument == null) {
predicates.add(cb.equal(root.get("yourAttribute"), argument);
}
// rest of your logic goes here
cq.where(predicates.toArray(new Predicate[]{}));
return em.createQuery(cq).getResultList();
}
I found a way to fix this, I did it like this (I'm going to show only the native Query since it's the inly thing that i changed):
DECLARE #actNum varchar(50),#crdNum varchar(50),#pseId varchar(50),#dateMin varchar(50),#dateMax varchar(50),#amountMin varchar(50),#amountMax varchar(50)
SET #actNum = :actNum
SET #crdNum = :crdNum
SET #pseId = :pseId
SET #dateMin = :dateMin
SET #dateMax = :dateMax
SET #amountMin = :amountMin
SET #amountMax = :amountMax
--Whatever Select with joins statement
WHERE ACT.ACT_AccountNumber = CASE WHEN #actNum = 'N/A'
THEN ACT.ACT_AccountNumber
ELSE #actNum END
AND CRD_CardNumber = CASE WHEN #crdNum = 'N/A'
THEN CRD_CardNumber
ELSE #crdNum END
AND PSE.PSE_Id= CASE WHEN #pseId = 'N/A'
THEN PSE.PSE_Id
ELSE #pseId END
AND PSR.PSR_CreateDate >= CASE WHEN #dateMin = 'N/A'
THEN PSR.PSR_CreateDate
ELSE #dateMin END
AND PSR.PSR_CreateDate <= CASE WHEN #dateMax = 'N/A'
THEN PSR.PSR_CreateDate
ELSE #dateMax END
AND PSR.PSR_Amount BETWEEN CASE WHEN #amountMin = 'N/A'
THEN PSR.PSR_Amount
ELSE #amountMin END
AND CASE WHEN #amountMax = 'N/A'
THEN PSR.PSR_Amount
ELSE #amountMax END
ORDER BY PSR.PSR_CreateDate DESC
The backend will send the parameters as either "N/A" (if it shouldn't be used to filter data) or the actual value, this worked fine for me!
Related
I am using hibernate-generic-dao for a searching function. Since I only need to show one of the records if they have same value on a column field. But I am not sure how to achieve this by the search / filter functions.
package com.googlecode.genericdao.search;
PersonContact domain object:
...
#Column(name = "group_key", length = 20)
public String getGroupKey() {
return groupKey;
}
#Formula(value = "(SELECT status from person_contact m " +
" WHERE m.case = case AND m.movement_id = movement_id )")
public String getActiveRecord() {
return activeRecord;
}
...
Search search = new Search();
search.addFilterNotNull("groupKey"); //groupKey is the field I want to use "group by / unqiue" with it
search.addFilterEqual("type","C");
search.addFilterCustom("{activeRecord} != 'I' ");
search.setMaxResults(limit);//for paging
search.setFirstResult(startIdx);
SearchResult<PersonContact> resultObj = PersonContactDAO.searchAndCount(search);
You should probably ask this question by opening an issue in the repository for that project here: https://github.com/vincentruan/hibernate-generic-dao
It seems though as if the project is abandoned, so unless you feel like digging into the details, you should probably try to get away from it.
Currently, I am trying to make a JPQL query in my repository within my Spring project,
this is my current code for the repository
#Query("select d.denda from DataTransaksi d WHERE d.tanggal= 1170130 AND d.nama = Suratno AND d.masaPajak=2016")
Collection<DataTransaksiModel> findAllDenda();
However, d.tanggal, d.nama, and d.masaPajak will not always be the same. I want to use the method here
if(file.getContentType().equalsIgnoreCase("application/vnd.ms-excel")) {
InputStreamReader input = new InputStreamReader(file.getInputStream());
CSVParser csvParser = CSVFormat.EXCEL.withFirstRecordAsHeader().parse(input);
for (CSVRecord record : csvParser) {
String tanggal = record.get("Tanggal");
String nama = record.get("Nama WP");
String masaPajak = record.get("Masa Pajak");
String denda = record.get("Denda");
String jumlahSetoran = record.get("Jumlah Setoran");
String pokok = record.get("Pokok");
String luasTanah = record.get("L.Tanah");
String luasBangunan = record.get("L. Bangunan");
Where d.tanggal, d.nama, and d.masaPajakis based on tanggal, nama, and masaPajakfrom the CSV that is uploaded.
Is there a way to make the value of d.tanggal, d.nama, and d.masaPajakdynamic and follow the variable that we set?
Just use query placeholders
#Query("select d.denda from DataTransaksi d WHERE d.tanggal=:x AND d.nama = :y AND d.masaPajak=:z")
Collection<DataTransaksiModel> findAllDenda(Longx,String y,Long z);
Adjust x,y,z types to suit your needs. Also you can renam params to be more descriptive
There are two ways you can do this, One way is to use indexed query params. Index value and the method parameter index should match in this case as below.
#Query("select d.denda from DataTransaksi d WHERE d.tanggal= ?1 AND d.nama = ?2 AND d.masaPajak=?3")
Collection<DataTransaksiModel> findAllDenda( Long x, String y, Long z); // These should be in exact order.
Other way is to use the named query parameters, In this way, you are free to define the mapping between the method parameter and the query parameter as below
#Query("select d.denda from DataTransaksi d WHERE d.tanggal= :x AND d.nama = :y AND d.masaPajak=:z")
Collection<DataTransaksiModel> findAllDenda( #Param("x") Long x, #Param("y") String y, #Param("z") Long z);
I'm on RavenDB 3.5.35183. I have a type:
import com.mysema.query.annotations.QueryEntity;
#QueryEntity
public class CountryLayerCount
{
public String countryName;
public int layerCount;
}
and the following query:
private int getCountryLayerCount(String countryName, IDocumentSession currentSession)
{
QCountryLayerCount countryLayerCountSurrogate = QCountryLayerCount.countryLayerCount;
IRavenQueryable<CountryLayerCount> levelDepthQuery = currentSession.query(CountryLayerCount.class, "CountryLayerCount/ByName").where(countryLayerCountSurrogate.countryName.eq(countryName));
CountryLayerCount countryLayerCount = new CountryLayerCount();
try (CloseableIterator<StreamResult<CountryLayerCount>> results = currentSession.advanced().stream(levelDepthQuery))
{
while(results.hasNext())
{
StreamResult<CountryLayerCount> srclc = results.next();
System.out.println(srclc.getKey());
CountryLayerCount clc = srclc.getDocument();
countryLayerCount = clc;
break;
}
}
catch(Exception e)
{
}
return countryLayerCount.layerCount;
}
The query executes successfully, and shows the correct ID for the document I'm retrieving (e.g. "CountryLayerCount/123"), but its data members are both null. The where clause also works fine, the country name is used to retrieve individual countries. This is so simple, but I can't see where I've gone wrong. The StreamResult contains the correct key, but getDocument() doesn't work - or, rather, it doesn't contain an object. The collection has string IDs.
In the db logger, I can see the request coming in:
Receive Request # 29: GET - geodata - http://localhost:8888/databases/geodata/streams/query/CountryLayerCount/ByName?&query=CountryName:Germany
Request # 29: GET - 22 ms - geodata - 200 - http://localhost:8888/databases/geodata/streams/query/CountryLayerCount/ByName?&query=CountryName:Germany
which, when plugged into the browser, correctly gives me:
{"Results":[{"countryName":"Germany","layerCount":5,"#metadata":{"Raven-Entity-Name":"CountryLayerCounts","Raven-Clr-Type":"DbUtilityFunctions.CountryLayerCount, DbUtilityFunctions","#id":"CountryLayerCounts/212","Temp-Index-Score":0.0,"Last-Modified":"2018-02-03T09:41:36.3165473Z","Raven-Last-Modified":"2018-02-03T09:41:36.3165473","#etag":"01000000-0000-008B-0000-0000000000D7","SerializedSizeOnDisk":164}}
]}
The index definition:
from country in docs.CountryLayerCounts
select new {
CountryName = country.countryName
}
AFAIK, one doesn't have to index all the fields of the object to retrieve it in its entirety, right ? In other words, I just need to index the field(s) to find the object, not all the fields I want to retrieve; at least that was my understanding...
Thanks !
The problem is related to incorrect casing.
For example:
try (IDocumentSession sesion = store.openSession()) {
CountryLayerCount c1 = new CountryLayerCount();
c1.layerCount = 5;
c1.countryName = "Germany";
sesion.store(c1);
sesion.saveChanges();
}
Is saved as:
{
"LayerCount": 5,
"CountryName": "Germany"
}
Please notice we use upper case letters in json for property names (this only applies to 3.X versions).
So in order to make it work, please update json properties names + edit your index:
from country in docs.CountryLayerCounts
select new {
CountryName = country.CountryName
}
Btw. If you have per country aggregation, then you can simply query using:
QCountryLayerCount countryLayerCountSurrogate =
QCountryLayerCount.countryLayerCount;
CountryLayerCount levelDepthQuery = currentSession
.query(CountryLayerCount.class, "CountryLayerCount/ByName")
.where(countryLayerCountSurrogate.countryName.eq(countryName))
.single();
so as part of some work I've been doing I was given a file with WebServices that are being used in a Swift application. I have zero familiarity with WebServices and only know Java through syntax understanding. I need to call one of these gets with a parameter from the swift application. What I'm trying to figure out first and foremost is how I can call one of these webservices with a parameter from the URL it's associated with. For example down below I want to call the method
http://localhost:9000/ListVehicleByPlateNumber
and I want to specify the parameter through the URL say something like
http://localhost:9000/ListVehicleByPlateNumber?para="123"
But this doesn't assign any value to the parameter and I'm not getting results. If I hardcode so that the string used in the function is = "123" it gives me the results I'm looking for. I just need to know how I can pass this parameter through the url, syntax-wise.
Routes file
GET /ListVehicleByPlateNumber controllers.NewVehicle.listVehicleByPlateNumber(para: String ?="")
Controller
public Result listVehicleByPlateNumber(String para){
NewVehicleModel v = new NewVehicleModel();
List<NewVehicleModel> vehiclesC = v.searchByPlateVehicle(para);
ObjectNode wrapper = Json.newObject();
ObjectNode msg = Json.newObject();
if(vehiclesC != null) {
msg.set("VehicleList", toJson(vehiclesC));
wrapper.set("success", msg);
return ok(wrapper);
}else{
msg.put("error", "There are no vehicles with the plate number");
wrapper.set("error", msg);
return badRequest(wrapper);
}
}
Where it's called
public List<NewVehicleModel> searchByPlateVehicle(String plateNumber){
Transaction t = Ebean.beginTransaction();
List<NewVehicleModel> vehicles = new ArrayList<>();
try {
String sql = "SELECT V.idNewVehicle, V.VehicleType,V.PlateNumber,V.VehicleJurisdiction,V.State,V.Vin,V.Year, " +
"V.Make,V.modelos,V.RegistrationNumber,V.InsuranceCompany,V.PurchaseDate,V.ExpirationDate,V.idPersonaFK " +
"FROM NewVehicle V " +
"WHERE V.PlateNumber = :plateNumber";
RawSql rawSql = RawSqlBuilder.parse(sql)
.columnMapping("V.idNewVehicle", "idNewVehicle")
.columnMapping("V.State", "state")
.columnMapping("V.VehicleType", "vehicleType")
.columnMapping("V.PlateNumber", "plateNumber")
.columnMapping("V.VehicleJurisdiction", "vehicleJurisdiction")
.columnMapping("V.Vin", "vin")
.columnMapping("V.Year", "year")
.columnMapping("V.Make", "make")
.columnMapping("V.modelos", "modelos")
.columnMapping("V.RegistrationNumber", "registrationNumber")
.columnMapping("V.InsuranceCompany", "insuranceCompany")
.columnMapping("V.PurchaseDate", "purchaseDate")
.columnMapping("V.ExpirationDate", "expirationDate")
.columnMapping("V.idPersonaFK", "idPersonaFK")
.create();
Query<NewVehicleModel> query = Ebean.find(NewVehicleModel.class);
query.setRawSql(rawSql)
.setParameter("plateNumber", plateNumber);
vehicles = query.findList();
t.commit();
}
catch (Exception e){
System.out.println(e.getMessage());
}finally {
t.end();
}
return vehicles;
}
Found my own answer. I ended up casting from Integer to String here's how it looks in routes
GET /ListVehicleByPlateNumber/:para controllers.NewVehicle.listVehicleByPlateNumber(para: Integer )
Controller
public Result listVehicleByPlateNumber(int para){
String p = String.valueOf(para);
URI Format for value 123 example.
http://localhost:9000/ListVehicleByPlateNumber/123
I want to create a query that query only rows that have an empty list.
The list in my model :
public static final Finder<Long, BankDebit> find = new Finder<>(Long.class, BankDebit.class);
#ManyToMany(cascade = CascadeType.ALL)
public List<Mandate> mandates;
The function that do the query :
public static ExpressionList<BankDebit> findFilter(sepaId, mandat, day ....) {
ExpressionList<BankDebit> exp = find
.fetch("creditor")
.fetch("debitor")
.fetch("mandates")
.where()
.eq("sepa.id", sepaId);
if (day > 0) {
dateMax = dateMax.withDayOfMonth(day);
exp.eq("executionDate", dateMax.toDate());
}
if (!mandat.isEmpty())
exp.eq("mandates.id", 0); // here is the problem
return exp
}
I want to query only the BankDebit that have an empty list of mandates. I tried to do it with .isNull("mandates"), .isNull("mandates.id"), .lt("mandates.id", 1), .eq("mandates.id", null) and a lot more, nothing ever worked...
I don't understund how I'm supposed to do. Do a rawSql would be very painful (I didnt paste the whole code of the function)
I tried a lot of things and reached many 4th page on google (never a good sign). I just ran out of ideas.
Huh, you were faster actually I wanted to suggest you similar solution, probably lighter as doesn't require object mapping:
List<Integer> idsWithoutMandates = new ArrayList<>();
List<SqlRow> rowList = Ebean.createSqlQuery("SELECT debits.id id " +
"FROM bank_debit AS debits " +
"LEFT JOIN bank_debit_mandate AS jointable ON (debits.id = jointable.bank_debit_id) " +
"WHERE (jointable.mandate_id IS NULL OR jointable.mandate_id = 0)").findList();
for (SqlRow sqlRow : rowList) idsWithoutMandates.add(sqlRow.getInteger("id"));
List<BankDebit> debitsWithoutMandates = BankDebit.find.where().in("id", idsWithoutMandates).findList();
I found out that although .isNull() doesn't work, .isNotNull() did work. So I made a little ugly modification to use the existing ones to find the others...
if (!mandat.isEmpty()) {
List<BankDebit> tmp = find.fetch("mandates").where().eq("sepa.id", sepaId).isNotNull("mandates.id").findList();
List<Long> ids = Lists.newArrayList();
for (BankDebit bd : tmp) {
ids.add(bd.id);
}
exp.not(Expr.in("id", ids));
}