I have some inherited code that is using JPA and is supposed to return a list of all processes that include all listed chemicals - an AND operation. However, the list is always empty. The code that returns the processes that have any of the listed chemicals (OR) seems to be OK. JDK version is 1.7. Hibernate 5.0.2
I have tried looking at the Javadocs, tutorials on JPA and hibernate, etc. But none give me a good feel for the Predicate class.
final CriteriaBuilder cb = getCriteriaBuilder();
final CriteriaQuery<Process> cq = cb.createQuery(Process.class);
final Root<Constituent> constituentRoot = cq.from(Constituent.class);
List<Predicate> clist - new ArrayList<Predicate>();
//chemical_id_list is a List of type Integer = List<Integer> passed to method.
//It contains all of the ids of the chemicals of interest.
for (Integer id: chemical_id_list) {
clist.add(cb.equal(constituentRoot.get(Constituent_.chemical), id));
}
//Code in common with the OR operation, which works..
It seems to me that the cb.equal part of this code is wrong. Constituent_.chemical is an attribute of the Constituent class, not an integer, which is what the "id" parameter is. How could a chemical object ever be "equal" to an integer? Or am I completely misunderstanding something? Thanks for your time.
Here is what is in the Constituent class:
public class Constituent implements Serializable{
private int constituentId;
private String chemicalNotes;
private String labelText;
private String quantity;
private int sort;
private Chemical chemical;
private Phase phase;
private Role role;
private Step step;
//getters and setters
}
Here is what is in the Chemical class:
public class Chemical
{
private int chemicalId;
private String boilingPoint;
private String canonicalFormula;
private String meltingPoint;
private String name;
private String notes;
//getters and setters
}
Here is what is in the Process class, although I do not show the use of it in the code here:
public class Process
{
private int processId;
private String name;
private String notes;
private List<Step> steps;
//Getters and setters not shown.
}
The problem in this query is that you join the chemical only once. Due to that, you check that the id of that 1 chemical is equal to all the Integer in your List. So, as soon as you List contains more than one Integer, the result set is empty.
You need a separate join for each chemical id that you want to check.
This query should return all Constituent that have all Chemicals identified by the chemical_id_list
final CriteriaBuilder cb = getCriteriaBuilder();
final CriteriaQuery<Process> cq = cb.createQuery(Process.class);
final Root<Constituent> constituentRoot = cq.from(Constituent.class);
List<Predicate> clist - new ArrayList<Predicate>();
//chemical_id_list is a List of type Integer = List<Integer> passed to method.
//It contains all of the ids of the chemicals of interest.
for (Integer id: chemical_id_list) {
Join<Constituent, Chemical> chemical = root.join(Constituent_.chemical);
clist.add(cb.equal(chemical.get(Chemical_.id), id));
}
Related
In my Spring Boot apps, I generally use projection in order to return joined table results. However, I am trying to use Java Stream to map and return nested entities to the corresponding DTOs. Here is an example that I generally encountered:
Note: I setup entity relationship using Hibernate and that part is completely ok. For this reason, I omitted the related code for brevity. Just concentrate on mapping entities to the nested DTOs.
Country has many States, States has many Towns...
Country:
public class Country {
private Long id;
private String name;
#OneToMany(...)
private Set<State> states = new HashSet<>();
}
State:
public class State {
private Long id;
private Long population;
#ManyToOne(...)
private Country country;
#OneToMany(...)
private Set<Town> towns = new HashSet<>();
}
Town:
public class Town {
private Long id;
private String name;
#ManyToOne()
private State state;
}
I want to get Country list with Country Name, Sum of state population belonging to this country and Town list belonging to the country.
For this purpose, I created the following DTOs, but not sure if it is ok and how can I map the necessary data to this DTO in one step (I don't want to go to database 3 times, instead, I just want to map the country list to the corresponding fields (the list has all of these data as I built the relations properly using Hibernate).
#Data
public class CountryResponse {
private Long id;
private String name;
private Set<StateResponse> states;
private Long population;
private Set<TownResponse> towns;
public CountryResponse(Country country) {
this.id = country.getId();
this.name = country.getName();
// ???
this.states = country.getStates.stream().map(StateResponse::new)
.collect(Collectors.toSet());
this.towns = this.states.stream().map(TownResponse::new)
.collect(Collectors.toSet());
}
}
How can I do this? I would also consider using MapStruct if it is better for this scenario?
To get the sum of state populations, you can use the following stream code
country.getStates().stream().map(State::getPopulation).sum();
and the list of towns can be fetched as follows
country.getStates().stream().map(State::getTowns).flatMap(Set::stream).collect(Collectors.toList());
I am trying to extract data using composite key of other table, but here problem is, I have list of composite key. Below are the table.
#Embeddable
public class IdRangePk implements Serializable {
#Column("START_RANGE")
private String startRange;
#Column("END_RANGE")
private String endRange;
// default constructor
}
#Entity
#Table(name = "ID_RANGE")
public class IdRange {
#EmbeddedId
private IdRangePk idRangePk;
#Column(name = "PARTY_ID")
private String partyId;
#Column("STATUS")
private String status; // expired or active
// constructors, other fields, getters and setters
}
Here, ID_RANGE have composite primary key (START_RANGE, END_RANGE). So same PARTY_ID can have multiple combination of start & end range.
STATUS can be either "EXPIRED" or "ACTIVE".
#Entity
#Table(name = "MESSAGE")
public class Message {
#Id
#Column("MESSAGE_Id")
private String id;
#Column(name = "PARTY_ID")
private String partyId;
#Column("START_RANGE")
private String startRange;
#Column("END_RANGE")
private String endRange;
// default constructor
// constructors, other fields, getters and setters
}
Here, I need to extract the messages having active ranges for a given PARTY_ID. Also MESSAGE_ID is the primary key.
So I divided it into two steps
Extracting active ranges for given party id
#Repository
public interface IdRangeRepo extends JpaRepository<IdRange, IdRangePk> {
List<IdRange> findByPartyIdAndStatus(String partyId, String status);
}
List<IdRange> idRanges = findByPartyIdAndStatus("123", "ACTIVE");
Extracting message from list of active IdRange
List<String> startRange = new ArrayList<>();
List<String> endRange = new ArrayList<>();
idRanges.stream().forEach(range -> {
startRange.add(range.getStartRange());
endRange.add(range.getEndRange())
});
List<Message> activeRangeMessage = findByPartyIdAndStartRangeInAndEndRangeIn("123", startRange, endRange);
#Repository
public interface MessageRepo extends JpaRepository<Message, String> {
List<IdRange> findByPartyIdAndStartRangeInAndEndRangeIn(String partyId, List<String> startRange, List<String> endRange);
}
My second step query is not right, it is extracting more rows than the expected as query is counter by individually field instead of whole (for startRange & endRange which is a composite key). Can someone please help me correct my query or provide an easiest way to extract rows. I have used derived method but #Query will also do.
I have a DDBB with three tables: loan, person and loan_person_rel and the respective POJO for each table.
Loan
private int line;
private Double balance;
private Double expired;
private int state;
private int defaultDays;
private Branch branch;
private String balanceHistory;
private String expiredHistory;
private Long loanCode;
private List<Person> persons;
private String extraInfo;
private LoanTypes loanType;
private String nomPro;
//The class contains the getters and setters :)
Person
private String name;
private String documentNumber;
private String workEnterprise;
private String workCity;
private String workAddress;
private String workNeighborhood;
private String workPhone;
private String occupation;
private String homePhone;
private String cellPhone;
private String phone3;
private String phone4;
private String homeAddress;
private String homeCity;
private String homeNeighborhood;
private String email;
private String relationship;
private List<Loan> loans;
//The class contains the getters and setters :)
Loan_person_rel
private String personId;
private String loanId;
private int type;
//The class contains the getters and setters :)
How i can build a JOOQ select or some method for retrieve the data and fill the class loan with the field persons populated?
jOOQ 3.15 solutoin using MULTISET
Starting with jOOQ 3.15, the standard SQL MULTISET operator was introduced, which is emulated using SQL/XML or SQL/JSON if needed. For simplicity, I'm assuming your Loan and Person classes are Java 16 records:
List<Loan> result =
ctx.select(
// Project the loan columns you need
LOAN.LINE,
LOAN.BALANCE,
..
multiset(
select(PERSON.NAME, PERSON.DOCUMENT_NUMBER, ...)
.from(PERSON)
.join(LOAN_PERSON_REL)
.on(PERSON.PERSON_ID.eq(LOAN.PERSON_REL.PERSON_ID))
.where(LOAN_PERSON_REL.LOAN_ID.eq(LOAN.LOAN_ID))
).as("persons").convertFrom(r -> r.map(Records.mapping(Person::new)))
)
.from(LOAN)
.fetch(Records.mapping(Loan::new));
The mapping into the Loan and Person constructor references is type safe and reflection free, using the new jOOQ 3.15 ad-hoc converter feature.
Unlike JPA based ORMs, jOOQ doesn't offer object graph persistence, i.e. your Person objects can't contain identity-based references back to Loan objects. Instead, this approach projects data in tree form, which may be fine for your use-cases.
jOOQ 3.14 solution using SQL/XML or SQL/JSON
Starting with jOOQ 3.14, the preferred approach here is to nest your collections directly in SQL using SQL/XML or SQL/JSON. You could write a query like this:
List<Loan> result =
ctx.select(
// Project the loan columns you need, or all of them using LOAN.asterisk()
LOAN.LINE,
LOAN.BALANCE,
...
field(select(
jsonArrayAgg(jsonObject(
key("name").value(PERSON.NAME),
key("documentNumber").value(PERSON.DOCUMENT_NUMBER),
...
))
.from(PERSON)
.join(LOAN_PERSON_REL)
.on(PERSON.PERSON_ID.eq(LOAN.PERSON_REL.PERSON_ID))
.where(LOAN_PERSON_REL.LOAN_ID.eq(LOAN.LOAN_ID))
)).as("persons")
)
.from(LOAN)
.fetchInto(Loan.class);
The same restriction about this fetching trees instead of graphs applies.
Note that JSON_ARRAYAGG() aggregates empty sets into NULL, not into an empty []. If that's a problem, use COALESCE()
It must be so way:
List<Loan*> list = dsl.selectFrom(Loan).fetch(this::recordToPojo);
private Loan recordToPojo(final LoanRecord record) {
return new Loan(
record.getLine(),
record.getBalance(),
....
);
}
*Loan - name of pojo!
I have 3 tables named role,permission and role_permission and their corresponding pojos
public class RoleTO
{
private int roleId;
private String roleName;
private int applicationId;
}
,
public class RolePermissionsTO
{
private int role_permissionId;
private int roleId;
private int permissionId;
}
,
public class PermissionTO
{
private int permissionId;
private String permission;
private String permissionDesc;
private int applicationId;
}
Now I have a method in my dao class
public List<PermissionTO> getPermissions(int applicationId, String roleName)throws HibernateException
{
Session session = getCurrentSession();
String hql = "SELECT P FROM PermissionTO P,Role R where P.applicationId=R.applicationId and P.applicationId=:applicationId and P.roleName=:roleName";
Query query = session.createQuery(hql);
query.setParameter("applicationId",applicationId);
query.setParameter("roleName",roleName);
return query.list();
}
But I need these three tables to be connected so that I get all the permissions in the permission class for the given application Id and the roleName linking the three tables.
Can anybody help
SELECT P FROM PermissionTO P,Role R
where P.applicationId=R.applicationId
AND R.applicationId=:applicationId
AND R.roleName=:roleName";
OR
Criteria cr = session.createCriteria(PermissionTO.class, "permissionTo");
cr.createAlias("permissionTo.permissionId", "rolePermissionsTo"); // inner join by default
cr.createAlias("rolePermissionsTo.roleId", "roleTo");
cr.add(Restrictions.eq("roleTo.applicationId", applicationId)); // applicationId set value to parameter
cr.add(Restrictions.eq("roleTo.roleName", roleName)); // roleName set value to parameter
List list = cr.list();
I also have a similar issue, but couldnt find a solution.
but in your query I find that you should be using some constraint to connect the RoleTO to RolePermissinTo using RoleId and ApplicationId and then use this result to get the corresponding PermissionTO list for each given role.
I am not expert with queries, i guess this should be the logic
I'm using DBUtils in my simple project. I have Item and Person entity class (persons and items as tables in database). This simplified the class to better show what I mean. Now i need to get list of items with login names using BeanListHandler. To do this i added login property to Item, but this is ugly solution. Is something better to do that and use advantages of BeanListHandler?
public class Person {
private Long id;
private String login;
}
public class Item {
private Long id;
private String name;
// ... a lot more properties of item
private Long personId; // this is real column in "items" table
private String login; // UGLY (login is not in "items" table, only for BeanListHandler)
}
QueryRunner q = new QueryRunner(getDataSource());
String sql = "select i.*, p.login from items i, persons p where p.id = i.personId";
List<Item> l = (List<Item>) q.query(sql, new BeanListHandler<Item>(Item.class));