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
Related
So I have a parent Class with two OneToMany child classes one of which have another two One to many child classes and I'm executing the following query:
#Query("Select distinct fg "
+ " from FaqGroupEntity fg "
+ " left join fg.names fgn "
+ " left join fg.faqs f "
+ " left join f.content fc "
+ " left join f.tags ft "
+ " where fgn.language.id = :language "
+ " and (fc.language.id is null or fc.language.id = :language) "
+ " and (ft.language.id is null or ft.language.id = :language)")
List<FaqGroupEntity> findAllByLanguage(#Param("language") int language);
It seems to be doing the query right but it's populating each of the "faq" children with all the values regardless of language.
Here's The FaqGroupEntity
#Entity
#Table(name = "FAQ_GROUP")
#Getter #Setter
public class FaqGroupEntity {
#GeneratedValue(generator = "UUID")
#GenericGenerator(
name = "UUID",
strategy = "org.hibernate.id.UUIDGenerator"
)
#Id
#Column(name = "ID")
private String id;
#OneToMany(mappedBy="group" ,cascade = CascadeType.ALL, orphanRemoval = true)
private List<FaqGroupNameEntity> names;
#OneToMany
#JoinColumn(name = "GROUP_ID")
private List<FaqEntity> faqs;
}
Hi everyone, I'm having problems. I have implemented TypedQuery in a module of my web system and everything works perfectly. But when making another module and implementing TypedQuery again it doesn't work anymore I would appreciate your opinion.
I leave a portion of my code with example.
#Service
public class AccountService {
private EntityManager em;
private AccountRepository accountRepository;
public AccountService(EntityManager em, AccountRepository accountRepository) {
this.em = em;
this.accountRepository = accountRepository;
}
public List<Account> getChartOfAccounts() {
List<Account> retList = null;
TypedQuery<Account> query = em.createQuery(
"select new com.javatmp.module.accounting.entity.Account("
+ "acct.id, acct.accountCode, acct.name, acct.parentAccountId, "
+ "sum(case when att.amount > 0 then att.amount else 0 end),"
+ "sum(case when att.amount < 0 then (att.amount * -1) else 0 end), "
+ "sum(case when coalesce(att.amount, 0) > 0 then (abs(coalesce(att.amount, 0)) * coalesce(at.debitSign, 0)) "
+ "else (abs(coalesce(att.amount, 0)) * coalesce(at.creditSign, 0)) end), "
+ "acct.accountGroupId, acct.cashFlowId, ag.name, at.name, at.debitSign, at.creditSign, at.reportTypeId)"
+ " from Account acct"
+ " left outer join AccountTransaction att on acct.id = att.accountId"
+ " left outer join Transaction trans on att.transactionId = trans.id"
+ " left outer join AccountGroup ag on acct.accountGroupId = ag.id"
+ " left outer join AccountType at on at.id = ag.accountType"
+ " group by acct.id, acct.accountCode, acct.name, acct.parentAccountId, acct.accountGroupId,"
+ "acct.cashFlowId, ag.name, at.name, at.debitSign, at.creditSign, at.reportTypeId"
+ "", Account.class
);
retList = query.getResultList();
return retList;
}
The previous code worked very well now that I have tried to make a new module, it does not work, it indicates that the variables have not been declared. ex.
#Service
public class BudgetService {
private EntityManager em;
private BudgetRepository BudgetRepository;
public BudgetService(EntityManager em, BudgetRepository budgetRepository) {
this.em = em;
this.budgetRepository = budgetRepository;
}
public List<Budget> getBudgetPage() {
List<Budget> retList = null;
TypedQuery<Budget> query = em.createQuery(
"select new com.javatmp.module.project.entity.Budget("
+ "budget.id, budget.budgetCode, budget.name, "//where the issues start, the variables are not defined
...
+ "", Budget.class
);
retList = query.getResultList();
return retList;
}
In my code i have used List < Tuple > to capture the query results by joining various
tables and mapping to DTO object. This code works fine in the main. But
when i try to create a test case, i'm unable to cover the List < Tuple > code.
Please provide your inputs.
For Ex :
#SuppressWarnings("unchecked")
private List<FeedbackDTO> getList(String sortingProperty, String direction, UUID workflowId, UUID categoryId,
String type) {
StringBuilder query = new StringBuilder(
"select feedback.id, feedback.workflowId, user.userName, user.firstName, "
+ "user.lastName, feedback.submittedDate, country.countryName, "
+ "region.regionName, feedback.comments, feedback.ratings, feedback.acknowledged "
+ "from Feedback feedback, User user, Country country, Region region "
+ "where feedback.userId = user.id and user.countryId = country.id and user.regionId = region.id "
+ "and feedback.workflowId = " + "'" + workflowId + "'" + " and feedback.categoryId = " + "'"
+ categoryId + "'" + " and feedback.type = " + "'" + type + "'" + " order by ");
if (sortingProperty.equalsIgnoreCase("firstName")) {
query.append("user.firstName ");
}
query.append(direction);
Query queryList = entityManager.createNativeQuery(query.toString(), Tuple.class);
List<Tuple> tupleList = queryList.getResultList();
List<FeedbackDTO> feedbackList = new ArrayList<FeedbackDTO>();
for (Tuple tuple : tupleList) {
FeedbackDTO feedbackDTO = new FeedbackDTO();
feedbackDTO.setId(UUID.fromString((String) tuple.get("id")));
feedbackDTO.setUserName((String) tuple.get("userName"));
feedbackDTO.setFirstName((String) tuple.get("firstName"));
feedbackDTO.setLastName((String) tuple.get("lastName"));
feedbackDTO.setCountryName((String) tuple.get("countryName"));
feedbackDTO.setAcknowledged((Boolean) tuple.get("acknowledged"));
feedbackDTO.setRegionName((String) tuple.get("regionName"));
feedbackDTO.setComments((String) tuple.get("comments"));
feedbackDTO.setSubmittedDate((Date) tuple.get("submittedDate"));
feedbackDTO.setRatings((Double) tuple.get("ratings"));
feedbackList.add(feedbackDTO);
}
return feedbackList;
}
In My Test Case:
#Test
public void testGetFeedbackListNoFeedback() {
try {
Query queryList = mock(Query.class);
FeedbackTestDTO feedback = new FeedbackTestDTO();
feedback.setId(id);
feedback.setSolutionId(solutionId);
feedback.setComments("Please swich-on the power");
feedback.setType("Alternate Solution");
List<Object> tupleList = new ArrayList<>();
tupleList.add(feedback);
StringBuilder query = new StringBuilder(
"select * from feedback order by user.firstName ASC");
Sort sort = new Sort("firstName:ASC");
Pageable pageable = PageRequest.of(0, 20, sort);
when(entityManager.createNativeQuery(query.toString(), javax.persistence.Tuple.class)).thenReturn(queryList);
when(queryList.getResultList()).thenReturn(tupleList);
this.mockFeedbackListDTO = feedbackServiceImpl.getFeedbackList(workflowId, categoryId, "", "",
"2019-08-29T01:00:00Z", "2019-08-29T13:00:00Z", "5", pageable, true);
assertEquals(201, this.mockFeedbackListDTO.getCode());
} catch (BusinessException | ParseException be) {
assertEquals("No feedback from users", accessor.getMessage(be.getLocalizedMessage()));
}
}
List<Tuple> tupleList = new ArrayList<>();
NativeQueryTupleTransformer nativeQueryTupleTransformer = new NativeQueryTupleTransformer();
tupleList.add((Tuple)nativeQueryTupleTransformer.transformTuple(new BigDecimal[]{new BigDecimal(123),new BigDecimal(123),new BigDecimal(123)},new String[]{"0","1","2"} ));
tupleList.add((Tuple)nativeQueryTupleTransformer.transformTuple(new BigDecimal[]{new BigDecimal(123),new BigDecimal(123),new BigDecimal(123)},new String[]{"0","1","2"} ));
tupleList.add((Tuple)nativeQueryTupleTransformer.transformTuple(new BigDecimal[]{new BigDecimal(123),new BigDecimal(123),new BigDecimal(123)},new String[]{"0","1","2"} ));
Mockito.when(service.getRecordRangeForPagination(Mockito.anyString(),Mockit.anyLong(),Mockito.anyString())).thenReturn(tupleList);
It looks like your list is empty, therefore your for-loop is never examined.
Add an assertion that the expected size of the list should be of size 3:
List<Tuple> tupleResults = queryResults.getResultList();
// Check that the list is of size 3
Assert.assertTrue(tupleResults.size() == 3);
for(Tuple t : tupleResults) {
myDtoObject.setName(t.get("name")); //0
myDtoObject.setAge(t.get("age")); //1
myDtoObject.setSalary(t.get("salary"); //2
}
#Test
public void test() {
Query query = mock(Query.class);
List<Tuple> tupleList = new ArrayList<>();
when(query.getResultList().size() > 0).thenReturn(tupleList );
}
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;
Why doesn't this query work?
query = "SELECT itm.itemId, itm.itemModel, itm.itemDescription, "
+ " itmImages.imageFileName, part.participant_id "
+ " FROM Users user "
+ " INNER JOIN user.participant part "
+ " INNER JOIN part.addresses addr "
+ " INNER JOIN part.item itm "
+ " INNER JOIN itm.itemImages itmImages "
+ " WHERE user.userType LIKE '%borrow%') AND itm.itemDescription LIKE '%mower%') AND addr.addressType = 'primary'";
It always returns all items, disregards itemDescription LIKE... I checked the database and all of the join ids are fine
This works:
query = "SELECT user"
+ " FROM Users user "
+ " INNER JOIN user.participant part "
+ " INNER JOIN part.addresses addr "
+ " WHERE user.userType LIKE '%borrow%') AND addr.addressType = 'primary'";
I have a table Users. It has a one-to-many association with table Participant.
In Users.java I have..
private Set<Participant> participant = new HashSet<Participant>();
with
#OneToMany
#JoinTable(name = "echomarket.hibernate.Participant")
#JoinColumn(name = "user_id")
public Set<Participant> getParticipant() {
return participant;
}
public void setParticipant(Set<Participant> participant) {
this.participant = participant;
}
This join works fine.
In Participant.java I have
private Set<Addresses> addresses = new HashSet<Addresses>();
with
#OneToMany
#JoinTable(name = "echomarket.hibernate.Addresses")
#JoinColumn(name = "participant_id")
public Set<Addresses> getAddresses() {
return addresses;
}
public void setAddresses(Set<Addresses> addresses) {
this.addresses = addresses;
}
And
private Set item = new HashSet();
With
#OneToMany
#JoinTable(name = "echomarket.hibernate.Items")
#JoinColumn(name = "participant_id")
public Set<Items> getItem() {
return item;
}
No association statement with regard to Users.
In Addresses.java I make no associations.
In Items.java I have
private Set<ItemImages> itemImages = new HashSet<ItemImages>();
with
#OneToMany
#JoinTable(name = "echomarket.hibernate.ItemImages")
#JoinColumn(name = "itemId")
public Set<ItemImages> getItemImages() {
return itemImages;
}
public void setItemImages(Set<ItemImages> itemImages) {
this.itemImages = itemImages;
}
In ItemImages.java I make no associations...
Very much thanks for your help. If you need more information, please just ask...
Liz
Couldn't get the above INNER JOIN query to work-- intended for a search that involves many tables. I tested each JOIN independently successfully, but together the JOIN does not work. Also had problems with query recognizing LIKE statement. I worked really hard to find solutions. So I rewrote with the following, data gathering is split in two query calls. The following works:
public String SearchResults() {
Session sb = null;
Transaction tx = null;
String queryString = "";
String forceString = this.found_zip_codes;
List results = null;
String fromStatement = "";
if (this.lenderOrBorrower == 2) {
this.which = "borrow";
} else {
this.which = "lend";
}
this.imageLibrary = this.which + "_images";
fromStatement = " SELECT part "
+ " FROM Participant part "
+ " INNER JOIN part.addresses addr "
+ " WHERE addr.addressType = 'primary' ";
if (ubean.getComDetailID() != null) {
fromStatement = fromStatement + " AND part.communityId = \'" + ubean.getComDetailID() + "\' ";
} else {
fromStatement = fromStatement + " AND part.communityId = ''";
}
if (this.postalCode.isEmpty() == false) {
fromStatement = fromStatement + " OR addr.postalCode LIKE \'" + this.postalCode + "%\'";
}
try {
sb = hib_session();
tx = sb.beginTransaction();
results = sb.createQuery(fromStatement).list();
tx.commit();
} catch (Exception ex) {
tx.rollback();
Logger.getLogger(SearchesBean.class.getName()).log(Level.SEVERE, null, ex);
} finally {
tx = null;
sb = null;
}
String[] pids = new String[results.size()];
String hold_pid = "";
if (results != null) {
if (results.size() > 0) {
for (int i = 0; i < results.size(); i++) {
Participant cArray = (Participant) results.get(i);
if (cArray.getParticipant_id().isEmpty() == false) {
hold_pid = "\'" + cArray.getParticipant_id() + "\'";
pids[i] = hold_pid;
}
}
hold_pid = String.join(",", pids);
}
}
results = null;
fromStatement = " FROM Items itm WHERE itm.itemType = :which "
+ (hold_pid.isEmpty() ? "" : " AND itm.participant_id IN ( " + hold_pid + " )");
if ((this.startDate.isEmpty() == false) && (this.endDate.isEmpty() == false)) {
try {
queryString = queryString + " OR ";
queryString = queryString + " ( itm.dateCreated >= \'" + this.startDate + "\' AND itm.dateCreated <= \'" + this.endDate + "\' ) ";
} catch (Exception ex) {
Logger.getLogger(SearchesBean.class.getName()).log(Level.INFO, null, ex);
}
}
forceString = this.keyword;
if (forceString.isEmpty() == false) {
queryString = queryString + " OR ";
queryString = queryString + " (itm.itemDescription like \'%" + forceString + "%\' OR itm.itemModel like \'%" + forceString + "%\')";
}
if ((this.categoryId != -2)) {
queryString = queryString + " OR";
queryString = queryString + " itm.categoryId = " + this.categoryId;
}
fromStatement = fromStatement + queryString;
try {
sb = hib_session();
tx = sb.beginTransaction();
results = sb.createQuery(fromStatement).setParameter("which", this.which).list();
tx.commit();
} catch (Exception ex) {
tx.rollback();
Logger.getLogger(SearchesBean.class.getName()).log(Level.SEVERE, null, ex);
} finally {
tx = null;
sb = null;
}
this.itemDetail = results;
return "search";
}