Generic DAO search with group by / unqiue - java

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.

Related

Search by the combination of multiple parameters via Native Query

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!

Java Mongo DB Unable to get valid next id

I am trying insert an item in MongoDB using Java MongoDB driver.Before inserting I am trying to get nextId to insert,but not sure why I am always getting nextId as 4 .I am using below given method to get nextId before inserting any item in Mongo.
private Long getNextIdValue(DBCollection dbCollection) {
Long nextSequenceNumber = 1L;
DBObject query = new BasicDBObject();
query.put("id", -1);
DBCursor cursor = dbCollection.find().sort(query).limit(1);
while (cursor.hasNext()) {
DBObject itemDBObj = cursor.next();
nextSequenceNumber = new Long(itemDBObj.get("id").toString()) + 1;
}
return nextSequenceNumber;
}
I have total 13 record in my mongodb collection.What I am doing wrong here?
Please don't do that. You don't need create a bad management id situation as the driver already do this in the best way, just use the right type and annotation for the field:
#Id
#ObjectId
private String id;
Then write a generic method to insert all entites:
public T create(T entity) throws MongoException, IOException {
WriteResult<? extends Object, String> result = jacksonDB.insert(entity);
return (T) result.getSavedObject();
}
This will create a time-based indexed hash for id's which is pretty much more powerful than get the "next id".
https://www.tutorialspoint.com/mongodb/mongodb_objectid.htm
How can you perform Arithmetic operations like +1 to String
nextSequenceNumber = new Long(itemDBObj.get("id").toString()) + 1;
Try to create a Sequence collection like this.
{"id":"MySequence","sequence":1}
Then use Update to increment the id
// Query for sequence collection
Query query = new Query(new Criteria().where("id").is("MySequence"));
//Increment the sequence by 1
Update update = new Update();
update.inc("sequence", 1);
FindAndModifyOptions findAndModifyOptions = new FindAndModifyOptions();
findAndModifyOptions.returnNew(true);
SequenceCollection sequenceCollection = mongoOperations.findAndModify(query, update,findAndModifyOptions, SequenceCollection.class);
return sequenceModel.getSequence();
I found the work around using b.collection.count().I simply find the total count and incremented by 1 to assign id to my object.

Group values from a parameter list in a loop according to the prefix of the parameter

I am fetching a param list from my jsp which I need to identify according to the prefix so that I can set the values in my entity class.
The parameter names looks like the below:
List<String> reqParamNames = Arrays.asList("1_component_role", "2_component_role", "3_component_role",
"4_component_role", "1_survey_wt", "2_survey_wt", "3_survey_wt", "4_survey_wt", "1dynaGroup1", "1component_role1", "1wt1", "2dynaGroup1", "2component_role1", "2wt1", "3dynaGroup1",
"3component_role1", "3wt1", "4dynaGroup1", "4component_role1", "4wt1");
Now, from the above list, I need to get the param according to the prefix, i.e.1,2,3,4 etc. Once grouped correctly, I would need to set it to my Entity class so that I can save the parameters in my table using Hibernate.
I am unable to set the values for the dynamic table.
#RequestMapping(value = { "dynamicSettings/persist" }, method = RequestMethod.POST)
public String saveComponents(HttpServletRequest request, HttpServletResponse response,
Model model) {
LOG.debug("Entering persist area :: ");
Locale locale = LocaleUtil.getLocale();
// ToDo: validation for form
//For dynamic tables
List<String> reqParamNames = (List<String>) Collections.list((Enumeration<String>)request.getParameterNames());
for(int i =0; i < reqParamNames.size(); i++){
System.out.println("Param names are {} ::"+ reqParamNames.get(i));
String paramName = reqParamNames.get(i);
Matcher m = Pattern.compile("[^0-9]*([0-9]+).*").matcher(paramName);
if (m.matches()) {
System.out.println("Number ::" +m.group(1)); // Need to comment/remove this post development
}
System.out.println("ParamNumber ::" +""+m.group(1));
String attributeValue = request.getParameter(paramName);
System.out.println("Param Name ::"+paramName+"::: Attribute value ::"+attributeValue);
DynamicComponentSettings dynamicSettings = new DynamicComponentSettings();
if( i == paramNumber){
String group_type = request.getParameter("groupType"+i);
String component_role = request.getParameter("component_role"+i);
String survey_weight = request.getParameter("wt"+i);
System.out.println("Group Type ::"+group_type+ "::Component Role::" +component_role+ "::Survey Weight::"+survey_weight);
if(!StringUtils.isEmpty(group_type) && StringUtils.isEmpty(component_role)&&!StringUtils.isEmpty(survey_weight)){
Double survey_wt = Double.parseDouble(survey_weight);
dynamicSettings.setSurvey_wt(survey_wt);
dynamicSettings.setGroup_type(group_type);
dynamicSettings.setComponent_role(component_role);
}
}
dynamicComponentService.saveDynamicComponents(dynamicSettings);
}
**//For concrete table**
List<DynamicComponentSettings> resultList = dynamicComponentService.loadAllDynamicComponents();
for(DynamicComponentSettings component : resultList)
{
String _survey_wt = request.getParameter(component.getPk1().toString() + "survey_wt");
String _groupRoleType = request.getParameter(component.getPk1().toString() + "group_type");
String _componentRole = request.getParameter(component.getPk1().toString() + "component_role");
if(!StringUtils.isEmpty(_survey_wt) && StringUtils.isEmpty(_groupRoleType)&&!StringUtils.isEmpty(_componentRole)){
Double survey_wt = Double.parseDouble(_survey_wt);
component.setSurvey_wt(survey_wt);
component.setGroup_type(_groupRoleType);
component.setComponent_role(_componentRole);
}
dynamicComponentService.saveDynamicComponents(component);
}
return "redirect:" + "some url";
}
The concrete table works correctly, i.e. saving values correctly.
Entity class
<package declaration>
<imports>
#Entity
#Table(name = "dynamic_components")
public class DynamicComponentSettings {
/** The pk1. */
#Id
#SequenceGenerator(name = "dynamic_components_seq", sequenceName = "dynamic_components_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.AUTO, generator = "dynamic_components_seq")
private Long pk1;
private String group_type;
private String component_role;
private Double survey_wt;
<getters and setters>
}
Please provide your inputs and provide guidance as how to save the dynamic table values.
I managed to figure out the problems and fix it. There are multiple problems in that code sample. For eg. the loops are incorrect, the approach to match with the main loop was horribly wrong.
I had to get the prefix and suffix for each item, loop correctly[i.e. first get the suffix and prefix of the table and then based on suffix match, iterate through the prefix like rows] and put each dynamic table data with suffix being the key and prefix+data+suffix being the values into a Map containing linked lists. After that, I had to loop again through the Map containing the lists to set the values to my entity class correctly.
Please pardon me for not posting the code again as the method is quite long and may not be of much help. In any case, if someone does want to see how this was solved, please let me know.
Thanks for your time.

How do I extract properties from Entities in Google App Engine Datastore using Java

I am using Google App Engine and trying to query / pull data from the Datastores. I have followed nearly 20 different tutorials without any luck.
Here is a picture of my Datastore and the respective sample data I have stored in there:
Here is some of the code I have to pull the data:
//To obtain the keys
final DatastoreService dss=DatastoreServiceFactory.getDatastoreService();
final Query query=new Query("Coupon");
List<Key> keys = new ArrayList<Key>();
//Put the keys into a list for iteration
for (final Entity entity : dss.prepare(query).asIterable(FetchOptions.Builder.withLimit(100000))) {
keys.add(entity.getKey());
}
try {
for (int i = 0; i < keys.size(); i++){
Entity myEntity = new Entity("Coupon", keys.get(i));
System.out.println("Size of the Keys array = " + keys.size());
String description = (String) myEntity.getProperty("desc");
String endDate = (String) myEntity.getProperty("endDate");
System.out.println("Description = " + description);
System.out.println("End Date: " + endDate);
//Map here is empty...
Map<String, Object> test = myEntity.getProperties();
System.out.println("MAP SIZE = " + test.size());
}
} catch (Exception e){
e.printStackTrace();
}
**OUPUT:**
Size of the Keys array = 2
Description = null
End date = null
MAP SIZE = 0
I have no clue why the description and end date are null. It is clearly pulling in the right Entity as the size shows 2, which matches the picture shown. Also, when I print the keys out, it matches as well
(Something like this: for the keys.get(i).toString(); -- Entity [!global:Coupon(123)/Coupon(no-id-yet)]:
. Or: Key String = !global:Coupon(5730827476402176)
I have followed the documentation (here) and some examples (here) to the best of my ability but I cannot seem to figure it out. Does anyone have any recommendations or experience in how to obtain the properties from Entities once you have them without them returning null?
I have gone through the following Stackoverflow questions without any success so please do not close this with a simple duplicate question marker on it:
1) How do i get all child entities in Google App Engine (Low-level API)
2) Storing hierarchical data in Google App Engine Datastore?
3) How do you use list properties in Google App Engine datastore in Java?
4) Mass updates in Google App Engine Datastore
5) Checking if Entity exists in google app engine datastore. .
have you tried this?
//Put the keys into a list for iteration
for (final Entity entity : dss.prepare(query).asIterable (FetchOptions.Builder.withLimit(100000))) {
String description = (String) entity.getProperty("desc");
String endDate = (String) entity.getProperty("endDate");
System.out.println("Description = " + description);
System.out.println("End Date: " + endDate);
}
In your example, you creating entity and it is expected that properties will be empty
Eureka! Many thanks to all that answered. Patrice and user2649908 especially thank you as you led me to the answer.
So, Patrice was entirely correct in that I was querying to get the keys, building a new entity, and then trying to parse the newly created (empty) entity.
The solution was to utilize PersistenceManager to parse the data and then use getter/ accessor methods to do so. The link for persistence manager (which I more or less just copied directly from as it worked perfectly) is here:
How to use JDO persistence manager?
Once I setup the persistence manager, I was able to get it to pull the data using this code:
try {
for (int i = 0; i < keys.size(); i++){
//See the link for How to use JDO persistence manager on how to use this
PersistenceManager pm = MyPersistenceManagerClass.getPM();
//Need to cast it here because it returns an object
Coupon coupon = (Coupon) pm.getObjectById(Coupon.class, keys.get(i));
System.out.println("Created by = " + coupon.getCreatedBy());
System.out.println("Description = " + coupon.getDesc());
System.out.println("Modified by = " + coupon.getModifiedBy());
}
} catch (Exception e){
e.printStackTrace();
}

Ebean finder list size condition

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));
}

Categories

Resources