I want to split XML response into pages because I have too many XML items to send back. I tried this:
XML request:
<?xml version="1.0" encoding="UTF-8"?>
<reconcile>
<start_date>2018-04-08T11:02:44</start_date>
<end_date>2019-10-08T11:02:44</end_date>
<page>1</page>
</reconcile>
JAXB:
#XmlRootElement(name = "reconcile")
#XmlAccessorType(XmlAccessType.FIELD)
public class Reconcile {
#XmlElement(name = "start_date")
#XmlJavaTypeAdapter(LocalDateTimeXmlAdapter.class)
private LocalDateTime start_date;
#XmlElement(name = "end_date")
#XmlJavaTypeAdapter(LocalDateTimeXmlAdapter.class)
private LocalDateTime end_date;
#XmlElement(name = "page")
private String page;
...../// getters and setters
}
SQL query:
public List<PaymentTransactions> transactionsByDate(LocalDateTime start_date, LocalDateTime end_date, Merchants merchant, Terminals terminal) throws Exception {
String hql = "select e from " + PaymentTransactions.class.getName() + " e where e.created_at >= ? and e.created_at <= ?";
Query query = entityManager.createQuery(hql).setParameter(0, start_date).setParameter(1, end_date);
List<PaymentTransactions> paymentTransactions = (List<PaymentTransactions>) query.getResultList();
return paymentTransactions;
}
Return XML:
List<PaymentTransactions> paymentTransactions = transactionsService
.transactionsByDate(reconcile.getStart_date(), reconcile.getEnd_date(), merchant, terminal);
ReconcilePaymentResponses pr = new ReconcilePaymentResponses();
pr.setPage("1");
pr.setPages_count("10");
pr.setPer_page("4");
pr.setTotal_count(String.valueOf(paymentTransactions.size()));
for (int e = 0; e < paymentTransactions.size(); e++) {
PaymentTransactions pt = paymentTransactions.get(e);
ReconcilePaymentResponse obj = new ReconcilePaymentResponse();
obj.setTransaction_type(pt.getType());
pr.getPaymentResponse().add(obj);
}
return pr;
XML Response:
<?xml version='1.0' encoding='UTF-8'?>
<payment_responses page="1" per_page="4" total_count="5" pages_count="10">
<payment_response>
<transaction_type>Type</transaction_type>
</payment_response>
<payment_response>
<transaction_type>Type</transaction_type>
</payment_response>
<payment_response>
<transaction_type>Type</transaction_type>
</payment_response>
.........
</payment_responses>
I would like somehow to split <payment_response>....</payment_response> into pages in order to reduce memory overhead. For example when I send 1 I would like to return the first 10.
How I can implement this?
What do you think about something like this? I'm sorry it is untested code but something like this should work.
I created a new PageInfo class to store the paging info. Added a query to get the total number of rows and set my page_info. Then limited the number of results from your query. Finally set the values to ReconcilePaymentResponse.
Class PageInfo {
int current_page;
int page_count;
int per_page;
int total_page;
//constructor
public PageInfo(int current_page, int page_count, int per_page) {
//assign them
}
//getters
//setters
}
SQL Query:
public List<PaymentTransactions> transactionsByDate(LocalDateTime start_date, LocalDateTime end_date, Merchants merchant, Terminals terminal,
PageInfo pageInfo) throws Exception {
//figure out number of total rows
String count_hql = "select count(*) from " + PaymentTransactions.class.getName() + " e where e.created_at >= ? and e.created_at <= ?";
Query count_query = entityManager.createQuery(count_hql);
int count = countQuery.uniqueResult();
//figure out total pages
int total_page = (int)Math.ceil(count/(double)pageInfo.getPerPage());
pageInfo.setTotal_Page(total_page);
String hql = "select e from " + PaymentTransactions.class.getName() + " e where e.created_at >= ? and e.created_at <= ?";
Query query = entityManager.createQuery(hql)
//set starting point
.setFirstResult((pageInfo.getCurrentPage()-1) * pageInfo.getPerPage)
//set max rows to return
.setMaxResults(pageInfo.getPerPage)
.setParameter(0, start_date).setParameter(1, end_date);
List<PaymentTransactions> paymentTransactions = (List<PaymentTransactions>) query.getResultList();
return paymentTransactions;
}
Return XML:
//initialize PageInfo with desired values
PageInfo page_info = new PageInfo(1,10,4);
List<PaymentTransactions> paymentTransactions = transactionsService
.transactionsByDate(reconcile.getStart_date(), reconcile.getEnd_date(), merchant, terminal, page_info); // pass in page_info
ReconcilePaymentResponses pr = new ReconcilePaymentResponses();
pr.setPage(page_info.getCurrentPage());
pr.setPages_count(page_info.getPageCount());
pr.setPer_page(page_info.getPerPage());
pr.setTotal_count(String.valueOf(paymentTransactions.size()));
for (int e = 0; e < paymentTransactions.size(); e++) {
PaymentTransactions pt = paymentTransactions.get(e);
ReconcilePaymentResponse obj = new ReconcilePaymentResponse();
obj.setTransaction_type(pt.getType());
pr.getPaymentResponse().add(obj);
}
return pr;
Related
I have a scenario in which I want to use the hibernate annotation #Filter (activated only if the condition is satisfied) but the condition needs to use alias (person as in the code snippet, because there is an ambiguous column in personId) from the original one which is written within Criteria. Note that the filter condition is written in sql. Besides the root.alias("person") don't work in this situation.
The model class:
#Entity
#Table(name = "PERSON")
#NoArgsConstructor
#Getter
#Setter
#Inheritance(strategy = InheritanceType.JOINED)
#FilterDef(name = "authorizePerson", parameters = {#ParamDef(name="userCode", type = "string")})
#Filter(name = "authorizePerson", condition =
" NOT EXISTS ("
+ " SELECT"
+ " multilevel1_.id_dossier,"
+ " multilevel1_.type_dossier"
+ " FROM"
+ " dossier_multiniv multilevel1_"
+ " WHERE"
+ " multilevel1_.id_dossier = TO_CHAR(**person**.id_per)"
+ " AND multilevel1_.type_dossier = 'PERSONNE'"
+ " )"
+ "OR EXISTS ("
+ " SELECT"
+ " multilevel2_.id_dossier,"
+ " multilevel2_.cd_util"
+ " FROM"
+ " v_droits_multiniv_allusers multilevel2_"
+ " WHERE"
+ " multilevel2_.cd_util = :userCode"
+ " AND multilevel2_.id_dossier = TO_CHAR(**person**.id_per)"
+ " AND multilevel2_.type_dossier = 'PERSONNE'"
+ " )", deduceAliasInjectionPoints = true)
public class PersonPO implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID_PER")
private Long personId;
#Column(name = "AFFICH_PER")
private String archive;
#Column(name = "AUTH_ERROR")
private Long numberOfConnectionErrors;
// bi-directional many-to-one association to Categper
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_PER")
private PersonCategoryPO personCategory;
...
}
Then the service:
#Transactional
#Override
public Pageable<IPersonBO> findAllPersons(String stringFilter, Page page, Sort sort, String userCode) {
try {
Predicate filterPredicate = null;
CriteriaQuery<PersonPO> cq;
Root<PersonPO> root;
CriteriaBuilder cb = em.getCriteriaBuilder();
Predicate allPredicates = cb.equal(cb.literal(1), 1);
if (!stringFilter.isBlank()) {
SearchCondition<PersonPO> filter = new FiqlParser<PersonPO>(PersonPO.class).parse(stringFilter);
JPACriteriaQueryVisitor<PersonPO, PersonPO> jpa = new JPACriteriaQueryVisitor<PersonPO, PersonPO>(em,
PersonPO.class, PersonPO.class);
filter.accept(jpa);
cq = jpa.getQuery();
root = (Root<PersonPO>) cq.getRoots().iterator().next();
filterPredicate = jpa.getQuery().getRestriction();
allPredicates = filterPredicate;
} else {
cq = cb.createQuery(PersonPO.class);
root =cq.from(PersonPO.class);
}
cq.select(root.alias("person"));
Fetch<PersonPO, PersonCategoryPO> pc = root.fetch("personCategory");
Fetch<PersonCategoryPO, CategoryPO> category = pc.fetch("category");
CriteriaQuery<Long> cqCount = cb.createQuery(Long.class);
cqCount.select(cb.count(cqCount.from(PersonPO.class)));
if (multiLevelIsActif()) {
if (userCode != null) {
Filter filter = session.enableFilter("authorizePerson");
filter.setParameter("userCode", userCode);
}
}
cq.where(allPredicates);
cqCount.where(allPredicates);
// sort
JpaUtils.handleSort(sort, cq, cb, root);
// get results
TypedQuery<PersonPO> query = em.createQuery(cq);
if (page != null) {
query.setFirstResult(page.getFirstResult()).setMaxResults(page.getPageSize());
}
List<IPersonBO> items = query.getResultStream().map(c -> getPerson((PersonPO) c))
.collect(Collectors.toList());
// count results
Long totalSize = 0l;
totalSize = em.createQuery(cqCount).getSingleResult();
return new Pageable<IPersonBO>(items, totalSize, page);
} catch (Exception e) {
log.error(e.getMessage());
return null;
}
Why the sql-show still generates query and never consider the alias (person) like below ?
select
personpo0_.ID_PER as id_per1_102_,
...
from
RKHATERCHI2.PERSONNE personpo0_
...
where
1=1
order by
personpo0_.ID_PER desc
Is there a way how to enforce the alias in JPA/Criteria so that I can use it in the filter condition?
Your thoughts are appreciated.
Take a look into the documentation about how to specify table aliases in filter conditions: https://docs.jboss.org/hibernate/orm/5.6/userguide/html_single/Hibernate_User_Guide.html#pc-filter-sql-fragment-alias
Usng java I can make a recursive tree parent child .
But usint this java traitement it consumes too much process and memory.
I want to simplify this method using a recursive postgres method and use the result in the java method.
I work with Spring boot version 2 and I use JPA and hibernate version 5.2
the #RestController class
#GetMapping("/collegeBook")
#Timed
public ResponseEntity<List<CollegeDTO>> getAllCollegeBook(Pageable pageable, #RequestParam(name="lang", required=false) String lang) {
Page<CollegeDTO> page = collegeService.findAllCollege(pageable, lang);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/collegeBook");
return ResponseEntity.ok().headers(headers).body(page.getContent());
}
and this is the method in service class
public Page<CollegeDTO> findAllCollege(Pageable pageable, String lang) {
List<CollegeDTO> list=collegeRepository.findCollegeList(pageable, lang);
List<CollegeDTO> unitChildList=getChildrenList(list,lang,pageable);
return new PageImpl<CollegeDTO>(unitChildList);
}
private List<CollegeDTO> getChildrenList(
List<CollegeDTO> collegeList, String lang,Pageable pageable) {
for(CollegeDTO collegeDTO : collegeList) {
List<CollegeDTO> childrenListEntity = collegeRepository.findCollegeByParentId(pageable, lang,CollegeDTO.getValue());
collegeDTO.setChildren(getChildrenList(childrenListEntity,lang,pageable));
}
return collegeList;
}
this is the Repository method
public List<CollegeDTO> findCollegeList(Pageable pageable, String lang) {
String querystr = "SELECT ";
querystr += " collegelang.name AS text ,";
querystr += " college.id AS value ,";
querystr += " (select count(*) from student where student.college_id = college.id) as nbr ";
querystr += " FROM college college ";
querystr += " LEFT OUTER JOIN collegelang collegel ON collegelang.college_id = college.id ";
querystr += " Where collegelang.lang = :lang and college.status ='1' and parentid is null order by app_order asc";
log.debug("-- Query:" + querystr);
Query query = em.createNativeQuery(querystr, "CollegeDTOMap");
query.setParameter("lang", lang);
List<CollegeDTO> collegeDTOs = query.getResultList();
return collegeDTOs;
}
public List<CollegeDTO> findCollegeByParentId(Pageable pageable, String lang, String idParent) {
String querystr = "SELECT ";
querystr += " collegelang.name AS text ,";
querystr += " college.id AS value ,";
querystr += " (select count(*) from student where student.college_id = college.id) as nbr";
querystr += " FROM college college ";
querystr += " LEFT OUTER JOIN collegelang collegelang ON collegelang.college_id = college.id ";
querystr += " Where collegelang.lang = :lang and college.status ='1' and un.parentid = :idParent ";
log.debug("-- Query:" + querystr);
Query query = em.createNativeQuery(querystr, "CollegeDTOMap");
query.setParameter("lang", lang);
query.setParameter("idParent", idParent);
List<CollegeDTO> collegeDTOs = query.getResultList();
return collegeDTOs;
}
CollegeDTO class
public class CollegeDTO{
private String text;
private String value;
private String nbr;
private List<CollegeDTO> children;
public CollegeDTO(String text,String value,String nbr, List<CollegeDTO> children) {
super();
this.text = text;
this.value = value;
this.nbr = nbr;
this.children = children;
}
//getter and setter .......
}
College class
#SqlResultSetMapping(
name = "CollegeDTOMap",
classes = #ConstructorResult(
targetClass = CollegeDTO.class,
columns = {
#ColumnResult(name = "text"),
#ColumnResult(name = "value"),
#ColumnResult(name = "nbr")
}
)
)
public class College {
..............
..............
}
Updated :
I try with this query but I didn't find the way to use the result in java traitement .
My goals is to return Page<CollegeDTO>
which contain child having CollegeDTO as a type
with recursive nodes as (
select college.id AS value, CAST(collegelang.name AS varchar) AS text,
(select count(*) from student where student.college_id = college.id) as nbr,
ARRAY[college.id,collegelang.name,(select count(*) from student where student.college_id = college.id)]
as parents,college.app_order
FROM college college
LEFT OUTER JOIN collegelang collegelang ON collegelang.college_id = college.id
Where collegelang.lang = 'AR' and college.status ='1' and parentid is null
union all
select o.id AS value, CAST(collegelang.name AS varchar) AS text,(select count(*) from student where student.college_id = college.id) as nbr
,parents || ARRAY[o.id,collegelang.name,(select count(*) from student where student.college_id = college.id)]
,o.app_order
FROM college o
join nodes n on n.value = o.parentid
LEFT OUTER JOIN collegelang collegelang ON collegelang.unit_id = o.id and collegelang.lang='AR'
where o.status ='1'
)
select CAST(text AS varchar) as text,value as value,nbr as nbr, parents as children from nodes
order by app_order asc
I try to modify College class
#SqlResultSetMapping(
name = "CollegeDTOMap",
classes = #ConstructorResult(
targetClass = CollegeDTO.class,
columns = {
#ColumnResult(name = "text"),
#ColumnResult(name = "value"),
#ColumnResult(name = "nbr"),
#ColumnResult(name = "children") // add this line
}
)
)
public class College {
..............
..............
}
but I have this error using the previous query
Caused by: org.hibernate.HibernateException: Could not resolve column name in result set [children]
Caused by: org.hibernate.MappingException: No Dialect mapping for JDBC type: 2003
i have a query which will return single record with 2 columns from Table
i want to get the result to a List each element hold a column value , but i keep getting ClassCastExceptoion
this is the code :
public List<String> getStatus(Long requestId) {
List result = new ArrayList();
if (requestId != null)
{
StringBuilder querySBuilder = new StringBuilder();
querySBuilder.append(" select R.request_status_id , L.request_status_desc ");
querySBuilder.append(" from Table1 R join Table2 L ");
querySBuilder.append(" on R.request_status_id = L.REQUEST_STATUS_Id ");
querySBuilder.append(" where R.REQUEST_ID = " + requestId);
System.out.print(querySBuilder.toString());
List resultList =
em.createNativeQuery(querySBuilder.toString()).getResultList();
Vector resultVec = (Vector)resultList.get(0);
int id = ((BigDecimal)resultVec.elementAt(0)).intValue();
String statusName = ((String)resultVec.elementAt(0));
System.out.println("id" + id);
System.out.println("name " + statusName);
result.add(id);
result.add(statusName);
if (resultVec == null || resultVec.isEmpty()) {
return new ArrayList<String>();
}
return result;
}
return null;
}
The pattern I would use would be to phrase the incoming native result set as a List<Object[]>, where each object array represents a single record:
Query q = em.createNativeQuery(querySBuilder.toString());
List<Object[]> result = q.getResultList();
for (Object[] row : result) {
int id = (Integer)row[0];
String statusName = (String)row[1];
// do something with the id and statusName from above
}
I think it is a typo.
String statusName = ((String)resultVec.elementAt(0));
The elementAt(1) should be used instead of elementAt(0).
I am fetching data from database in method BillnAmountFetch and getting value in main method I want to assign fetched velue to arrayList ar.
My code to fetch data from database
public List<Object[]> BillnAmountFetch(long cid) {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
List<Object[]> obj = null;
try {
String hql = "select count(billNo), sum(total), invoiceDate from BillDetails "
+ "where client.id=:cid "
+ "group by invoiceDate "
+ "order by invoiceDate DESC";
Query query = session.createQuery(hql);
query.setParameter("cid", cid);
obj = query.list();
tx.commit();
} catch (HibernateException e) {
if (tx != null) {
e.printStackTrace();
tx.rollback();
}
} finally {
session.close();
}
return obj;
}
Following is my code to print data received from above method
public static void main(String[] args) {
BillDAO bdo = new BillDAO();
List<Object[]> lst = bdo.BillnAmountFetch(1);
BillDetails bd = new BillDetails();
ArrayList<BillDetails> ar = new ArrayList<BillDetails>();
Object[] count = lst.get(0);
Object[] amount = lst.get(1);
Object[] invoice_dts = lst.get(2);
System.out.println("-----------Total Bills---------- ");
for (int x = 0; x < count.length; x++) {
System.out.println("Total bills " + count[x]);
}
System.out.println("-------Total Amount--------- ");
for (int x = 0; x < amount.length; x++) {
System.out.println("Amount " + amount[x]);
}
System.out.println("----------Total Invoice date---------- ");
for (int x = 0; x < invoice_dts.length; x++) {
System.out.println("dates " + invoice_dts[x]);
}
}
Output of the above program is
-----------Total Bills----------
Total bills 3
Total bills 7281.00
Total bills 2014-07-15
-------Total Amount---------
Amount 7
Amount 14841.00
Amount 2014-07-12
----------Total Invoice date----------
dates 3
dates 1294.00
dates 2014-07-11
BillDetails.java
public class BillDetails implements java.io.Serializable {
private Date invoiceDate;
private long totalBills;
private BigDecimal totalAmount;
//getter and setter
}
How to properly assing values to ArrayList object ar
EDIT: If not able to assign in ar then can we assign all count to one array or list similar for others.
I have to display values in jsp page.
I am fetching data from database in method BillnAmountFetch and getting value in main method I want to assign fetched velue to arrayList ar.
You can't, you'll have to do the copy yourself and cast/convert the elements appropriately.
Note that BillnAmountFetch returns List<Object[]> (a list of object arrays), and you're saying you want to assign it to ArrayList<BillDetails>.
So there are three major issues there:
A List<Object[]> is allowed to contain Object[] instances, but an ArrayList<BillDetails> is not allowed to contain Object[] instances; the items in the list are required to be BillDetails instances.
List<Object[]> could be any kind of list (LinkedList, Stack), it doesn't have to be an ArrayList, but ar is declared as an ArrayList (specifically).
It seems unlikely that BillDetails is assignment-compatible with Object[] (an array of objects).
Apart from T. J. Crowders suggestion you can map your model entity as Hibernate entity like below:
#Entity
public class BillDetails implements java.io.Serializable {
// some other related annotations
private Date invoiceDate;
private long totalBills;
private BigDecimal totalAmount;
//getter and setter
}
then you can get that automatic binding of ArrayList< BillDetails > by Hibernate, when you do this :
Query query = session.createQuery(hql , BillDetails.Class);
you need to search furthermore about it though:
I got the solution. I may not be able to express my question properly.
I want Scalar results.
What I want is below
public List<BillDetails> BillnAmountFetch(long cid) {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
ArrayList<BillDetails> ar = new ArrayList<BillDetails>();
BillDetails bd = null;
try {
String hql = "select count(billNo), sum(total), invoiceDate from BillDetails "
+ "where client.id=:cid "
+ "group by invoiceDate "
+ "order by invoiceDate DESC";
Query query;
query = session.createQuery(hql);
query.setParameter("cid", cid);
Iterator results = query.list().iterator();
while (results.hasNext()) {
Object[] row = (Object[]) results.next();
Long count = (Long) row[0];
BigDecimal amount = (BigDecimal) row[1];
Date dt = (Date) row[2];
System.out.println(count + " " + amount + " " + dt);
bd = new BillDetails();
bd.setTotalBills(count);
bd.setTotalAmount(amount);
bd.setInvoiceDate(dt);
ar.add(bd);
}
System.out.println("--------------------------");
tx.commit();
} catch (HibernateException e) {
if (tx != null) {
e.printStackTrace();
tx.rollback();
}
} finally {
session.close();
}
return ar;
}
public static void main(String[] args) {
BillDAO bdo = new BillDAO();
ArrayList<BillDetails> ar = (ArrayList<BillDetails>) bdo.BillnAmountFetch(1);
System.out.println("In main method");
for(BillDetails b:ar){
System.out.println(b.getTotalBills() + " " + b.getTotalAmount() + " " + b.getInvoiceDate());
}
}
I've the following query:
select u from User u where upper(u.name) like upper(?1)
How this would be, but ignoring the accents in u.name and ?1?
I have found a way to implement this. Basically there are 2 steps:
normalize the parameter (in your case 1) and
normalize the data from the DB
Example
public User getByName(String paramName) {
.....
}
Original HQL Query :
String queryString = "select u from User u where u.name : =paramName";
New Query:
paramName = normalizer(paramName );
String queryString = "select u from User u where " + replacer(u.name) +" : =paramName";
And the 2 methods:
public static String normalizer(String s) {
return Normalizer.normalize(s, Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", "");
}
public String replacer(String param) {
String t1 = "àâäçéèëêùûüôöïî";
String t2 = "aaaceeeeuuuooii";
String s = param;
for (int i = 0; i < t1.length(); i++) {
s = "replace(" + s + ",'" + t1.charAt(i) + "','" + t2.charAt(i) + "')";
}
return s;
}
What database are you using? In most of them you can send a specific SQL command to set the collation.
In portuguese, i missed the accents in some characters, so i added more characters in the replacer method:
public String replacer(String param) {
String t1 = "áãàâäçéèëêùûüúóôöïîíÁÀÂÄÃÇÉÈËÊÙÛÜÚÓÔÖÏÎÍ";
String t2 = "aaaaaceeeeuuuuoooiiiAAAAÃCEEEEUUUUOOOIII";
String s = param;
for (int i = 0; i < t1.length(); i++) {
s = "replace(" + s + ",'" + t1.charAt(i) + "','" + t2.charAt(i) + "')";
}
return s;
}
With CriteriaBuilder you can use the following Specification
public static Specification<User> byNameIgnoringCaseAndAccents(String paramName) {
return (root, cq, cb) -> {
String paramNameNormalized = paramName != null ? normalize(paramName.toLowerCase()) : null;
String characters = "áãàâäçéèëêùûüúóôöïîíÁÀÂÄÃÇÉÈËÊÙÛÜÚÓÔÖÏÎÍ";
String replacements = "aaaaaceeeeuuuuoooiiiAAAAÃCEEEEUUUUOOOIII";
Expression<String> replacedName = root.get("name");
for (int i = 0; i < characters.length(); i++) {
replacedName = cb.function("REPLACE", String.class, replacedName, cb.literal(characters.charAt(i)), cb.literal(replacements.charAt(i)));
}
replacedName = cb.lower(replacedName);
return cb.like(cb.literal(paramNameNormalized), cb.concat("%", cb.concat(replacedName, "%")));
};
}
public static String normalize(String s) {
return s != null ? Normalizer.normalize(s, Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", "") : s;
}