Displaying values from aggregating functions on JSP - java

I'm using Hibernate and JPA. I have entity class CompanyRate.
CompanyRate.java
#Entity
public class CompanyRate {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int idRate;
#Column(nullable = false)
private int atmosphere;
#Column(nullable = true)
private int salary;
#Column(nullable = false)
private int opportunityToDevelop;
#Column(nullable = false)
private int socialPackage;
#ManyToOne(targetEntity = User.class)
private User user;
#ManyToOne(targetEntity = Company.class)
Company company;
}
I want to display AVG values of atmosphere, salary, opportunityToDevelop, socialPackage where company.idComapny = ? on JSP.
Ofc I can use getResultList() on executed query and diplay it on JSP using . But I wanna assign each result for single variable. What is the best way to do it?

What you're asking is equivalent to the following SQL:
SELECT c.id,
AVG(t.atmosphere),
AVG(t.salary),
AVG(t.opportunityToDevelop),
AVG(t.socialPackage)
FROM table t, company c
WHERE t.companyid = c.id
AND c.id IN (:companyIds)
GROUP BY c.id;
Translating the above into HQL/JPQL or leveraging Hibernate or JPA criteria APIs should be a trivial exercise.
Just be sure to call setParameter("companyIds", listOfCompanyIds) on the constructed Query and then call getResultList() or list() depending on the API you use.

Related

How to prevent additional query for OneToOne relation

I have tables:
users (id, name, email, password)
user_statuses (user_id, is_premium, is_advanced, user_rank_id)
user_ranks (id, name, ordinal)
So the relation between User and UserStatus is 1-1, and I have following entity clasess:
#Entity
#Table(name = "users")
#Getter
#Setter
#NoArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String email;
private String password;
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private UserStatus status;
}
#Entity
#Table(name = "user_statuses")
#Getter
#Setter
#NoArgsConstructor
public class UserStatus {
#Id
private long id;
#MapsId
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
private boolean isPremium;
private boolean isAdvanced;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_rank_id")
private UserRank rank;
}
#Entity
#Table(name = "user_ranks")
#Getter
#Setter
#NoArgsConstructor
public class UserRank {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private int ordinal;
}
Then i created endpoint "/users/{id}" which should return user's email address as a string:
#GetMapping("/users/{id}")
public String getUserEmail(#PathVariable("id") long userId) {
User user = service.getUser(userId);
return user.getEmail();
}
When I call above endpoint I get user's email address as a response, however looking at the console log I see that hibernate executed 2 queries but noone asked him to do so:
First one for fetching the user:
SELECT
user0_.id AS id1_2_0_,
user0_.email AS email2_2_0_,
user0_.name AS name3_2_0_,
user0_.password AS password4_2_0_
FROM
users user0_
WHERE
user0_.id = 1;
And second one for fetching User Status that is associated with this User object:
SELECT
userstatus0_.user_id AS user_id1_1_0_,
userstatus0_.is_advanced AS is_advan2_1_0_,
userstatus0_.is_premium AS is_premi3_1_0_,
userstatus0_.user_rank_id AS user_ran4_1_0_
FROM
user_statuses userstatus0_
WHERE
userstatus0_.user_id = 1;
So I am confused: Why is hibernate running second query when I set fetch = FetchType.LAZY on each relation... It looks like that LAZY is ignored for #OneToOne annotation?
I do not use EntityGraph.
How to stop hibernate for running second query?
EDIT
So, it turns out Hibernate ignores my Lazy hint because it needs to decide should it initialize property with NULL or ProxyObject which makes sense. This link explains it well:
https://thorben-janssen.com/hibernate-tip-lazy-loading-one-to-one/
However this link also suggests that the best way to model this is Unidirectional One to One and it says that I can always fetch UserStatus based on User's ID (because both tables "shares" primary key)
However this confuses me a little bit, because I can fetch both rows using single query (SELECT * FROM users LEFT JOIN user_statuses ON users.id = user_statuses.user_id), but with approach described in the link I need 2 queries, and as far as I know (which I might be wrong) is 1 query is better than executing 2 queries, also if I want to fetch 25 users and their User Statuses, then I would also need 2 queries, one for fetching users and then fetching corespoinding user statuses and finally write nested for each loops to join these objects. I could have just executed one single query to fetch everything...
It is possible to make OTO lazy even if it's not the owning side. You just need to mark it as optional = false. This way Hibernate will know that it can safely a create proxy (and null is not possible) as the association always exists. Note, though it really must be non-optional - the 2nd entity must always exist. Otherwise you'll get an exception once Hibernate tries to load it lazily.
As for the number of queries, with native Hibernate (not JPA!) you can select org.hibernate.annotations.FetchMode. Which gives options to:
Use a separate select
Or use a join to load association
Alternatively, you can stay with JPA and write a JPQL query and use fetch join to keep it as a single query.
PS: before doing additional select Hibernate will check if the element already exists within the Session. If it is, then no select is going to be issued. But with fetch join or FetchMode.JOIN you won't have this luxury - join will always happen.
For one to one relation in hibernate it is always loading reference object whether you keep Fetch type Lazy or Eager. So alternate solution is select only those columns which are needed, it should not contain that reference column. So in this case hibernate will not fire another query.
Query for below class will be :
#Query("select new Example(id,field1) from Example")
#Entity
#Table(name = "example")
class Example implements Serializable {
private static final long serialVersionUID = 1L;
public Example(Long id, String field1) {
this.id = id;
this.field1 = field1;
}
#Id
#Column(name = "id", nullable = false, updatable = false)
private Long id;
#OneToOne(mappedBy = "example", fetch = LAZY, cascade = ALL)
private CustomerDetails customerDetails;
#Column(name = "field1", nullable = false, updatable = false)
private String field1;
}

different result by executing query in console and namedQuery

in my java web application i need to inquire a list of deposits from a view named VwDepositsInfo by customerNumber.
when i execute my query:
select * from VW_DEPOSIT_INFO v where v.CUSTOMER_NUMBER=:customerNo
in database console my resultList size is 2 and have something like this:
1-{depositTypeDesc="shortTerm"} {depositTypeCode="850"}
2-{depositTypeDesc="longTerm"} {depositTypeCode="2"}
but when i test my code that includes a namedQuery:
#NamedQuery(
name = "inquireAccountByCustomerNumber",
query = "select c from VWDepositInfo c where c.customerNumber=:customerNo"
)
i get a resultList with size 2 but both the same, sth like this:
1-{depositTypeDesc="shortTerm"} {depositTypeCode="850"}
2-{depositTypeDesc="shortTerm"} {depositTypeCode="850"}
when i make it nativeQuery with defining the result class:
Query query = entityManager.createNativeQuery("select * from VW_DEPOSIT_INFO v where v.CUSTOMER_NUMBER=:customerNo", VWDepositInfo.class);
again i get the wrong results.
finally i tried nativeQuery without defining the result class:
Query query = entityManager.createNativeQuery("select * from VW_DEPOSIT_INFO v where v.CUSTOMER_NUMBER=:customerNo");
and result was as i expected to be.
and this is my VwDepositsInfo.class:
#Entity
#Table(name = "VW_DEPOSIT_INFO")
#Audited(withModifiedFlag = false)
#NamedQueries(
{#NamedQuery(
name = "inquireAccountByCustomerNumber",
query = "select c from VWDepositInfo c where c.customerNumber=:customerNo"
)
}
)
public class VWDepositInfo implements Serializable {
#Id
#Column(name = "CUSTOMER_NUMBER")
private Long customerNumber;
#Column(name = "BRANCH_CODE")
private Long branchCode;
#Column(name = "DEPOSIT_TYPE_CODE")
private Long depositTypeCode;
#Column(name = "DEPOSIT_SERIAL")
private Long depositSerial;
#Column(name = "DEPOSIT_TYPE_DESC")
private String depositTypeDesc;
#Column(name = "CURRENCY_TYPE_DESC")
private String currencyTypeDesc;
#Column(name = "DEPOSIT_OPEN_DATE")
private Date depositOpenDate;
Does anyone know why this is happening???
VW = view?
You probably need to specify the master key
use #id for unique field :)
you probably need more than one field with #id for a unique row.
for example both of DEPOSIT_TYPE_CODE and customerNumber

Create Hibernat Query with CriteriaBuilder including Join, Group By and Count in Java

In my Java programm I got two entities Invoice and Category (like insurance, salary, car,...). Each Invoice belongs to exactly one Category. Now I want to display a table which lists all existing categories and the number if times they are actually used for an invoice. Like this:
The SQL would look like this:
select categories.name, categories.id, count(invoice.id) as usages
from categories
left join invoice on categories.id = invoice.category_id
group by categories.id
As I'm pretty new to Hibernate I studied the official documentation, but the documentation does only show examples based on a Person and Address relationship. However in my case Category does not have a direct linking to Invoice (it is the other way around: each invoice has a category id). So I played around with the code and came up with the following source code. Which obviously has one serious drawback as I do not get the categories without invoices:
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<CategoryWrapper> criteria = criteriaBuilder.createQuery(CategoryWrapper.class);
Root<Invoice> invoiceRoot = criteria.from(Invoice.class);
Join<Invoice, Category> invoiceJoin = invoiceRoot.join(Invoice_.category);
criteria.select(
criteriaBuilder.construct(
CategoryWrapper.class,
invoiceJoin.get(Category_.id),
invoiceJoin.get(Category_.name),
criteriaBuilder.count(invoiceRoot.get(Invoice_.id))
)
);
criteria.groupBy(invoiceJoin.get(Category_.id));
return session.createQuery(criteria).getResultList();
My question is: How can I get all categories with the number of usages even if no invoice exists for a specific category?
Entities
#Entity
#Table(name = "categories")
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
[... getter and setter ]
}
#Entity
#Table(name = "invoice")
public class Invoice {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "date")
private Date date;
#ManyToOne
#JoinColumn(name = "category_id")
private Category category;
#Column(name = "description")
private String description;
#Column(name = "amount")
private String amount;
#Column(name = "income", columnDefinition = "BOOLEAN")
private boolean income;
[...getter and setter...]
}
EDIT1
updated the SQL to use a left join. However using a LEFT join in the java code does not work.
Join<Invoice, Category> invoiceJoin = invoiceRoot.join(Invoice_.category, JoinType.LEFT);
I only get the categories with invoices (as shown in the image above). But in the database more categories are available:
EDIT2
As invoices is the left table and categories is the right table I tried to use RIGHT join
Join<Invoice, Category> invoiceJoin = invoiceRoot.join(Invoice_.category, JoinType.RIGHT);
but that did not work
Caused by: java.lang.UnsupportedOperationException: RIGHT JOIN not
supported
EDIT3
I checked the console output: even if I use a JoinType.LEFT (as shown in EDIT1) the programm will still use a INNER join:
select category1_.id as col_0_0_, category1_.name as col_1_0_, count(invoice0_.id) as col_2_0_ from invoice invoice0_ inner join categories category1_ on invoice0_.category_id=category1_.id group by category1_.id
But why?

Hibernate Criteria One to Many issues

I am trying to use Hibernate Criteria api to fetch only the topics based on the USER_ID but have no idea how to do it using the criteria.
My Tables are "topic_users" (below)
and "topics" table (below)
I know how to do it using SQL, this would be something like:
SELECT TOPICNAME
FROM topic_users INNER JOIN topics on topic_users.TOPICS_TOPICS_ID = topics.TOPICS_ID
WHERE topic_users.USER_ID = 1
This will return all TOPICNAME of USER_ID 1 which is exactly what I want but how I can do this with Hibernate Criteria. So far I have this in my Repository class (see below) but this will only return a highly nested JSON array. I could loop through the objects, use a DTO and build my response or try the Hibernate createSQLQuery method that will let me call a native SQL statement directly (haven't tried that yet)...but I am trying to learn the Criteria so I hope anyone can answer my query.
#Repository("userTopicsDao")
public class UserTopicsDaoImpl extends AbstractDao<Integer, UserTopics>implements UserTopicsDao {
#Override
public List<UserTopics> findMyTopics(int userId) {
Criteria crit = createEntityCriteria();
crit.add(Restrictions.eq("userId", userId));
List<UserTopics> userTopicsList = (List<UserTopics>)crit.list();
return userTopicsList;
}
and my TOPIC_USERS Entity where I have mapped the TOPICS
#Entity
#Table(name="TOPIC_USERS")
public class UserTopics {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="TOPICUSER_ID")
private Integer id;
#Column(name="USER_ID")
private Integer userId;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "TOPICS_ID")
private Set<Topics> topicsUser;
//getter and setters
Ok starting from the ground up.. you entity classes should look like this:
#Entity
#Table(name="TOPIC_USERS")
public class UserTopics {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="TOPICUSER_ID")
private Integer id;
#Column(name="USER_ID")
private Integer userId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "TOPICS_TOPICS_ID")
private Topics topics;
Your Topics class should look like this:
#Entity
#Table(name="TOPICS")
public class Topic {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="TOPICUS_ID")
private Integer id;
#Column(name="TOPICNAME")
private Integer topicName;
#OneToMany(mappedBy = "topics")
private Set<UserTopics> userTopics;
Finally the Criteria:
Version 1) You get entire entity:
Criteria c = session.createCriteria(Topics.class, "topics");
c.createAlias("topics.userTopics", "userTopics");
c.add(Restrictions.eq("userTopics.userId", userId));
return c.list(); // here you return List<Topics>
Version 2) You project only the topicname:
Criteria c = session.createCriteria(Topics.class, "topics");
c.createAlias("topics.userTopics", "userTopics");
c.add(Restrictions.eq("userTopics.userId", userId));
c.setProjection(Projections.property("topics.topicName"));
List<Object[]> results = (List<Object[]>)c.list();
// Here you have to manually get the topicname from Object[] table.
}

Hibernate Criteria select using embedded object (tuple)

In my case I have a SQL query which looks like:
select * from event_instance where (object_id, object_type) in
(<LIST OF TUPLES RETRIEVED FROM SUBQUERY>);
I want to map this on Hibernate Entities and I have a problem with this query. My mapping looks like that:
#Entity
#Table(name="event_instance")
public class AuditEvent {
<OTHER_FIELDS>
#Column( name = "object_type", nullable = false)
private String objectType;
#Column( name ="object_id" , nullable = false)
private Integer objectId;
}
and second entity:
#Entity
#Table(schema = "els" ,name = "acg_objects")
public class AcgObject implements Serializable{
#Id
#Column(name = "acg_id")
private String acgId;
#Id
#Column(name="object_type")
private String objectType;
#Id
#Column(name="object_id")
private Integer objectId;
<OTHER FIELDS>
}
I already run query for getting AcgObjects and for my DAO I'm getting List only thing I want to do is query a touple using criteria like:
crit.add(Restrictions.in("objectType,objectId",<List of tuples>);
Is it possible? I was trying to use #Embedded object but don't know how exactly construct a query for it. Please help
You can do that not in standard SQL nor using criteria; you have to split in two distinct restrictions or using a Session.SQLQuery() if you want to use specific RDBMS (look at SQL WHERE.. IN clause multiple columns for an explanation)

Categories