Hibernate search query, restrict by child classes? - java

Consider the following setup.
Space.java
class Space {
Id id;
ParkingCampus campus;
}
class ParkingCampus {
Id id;
String country;
}
This is not the exact structure of my project but it is close enough for what I am trying to understand.
How would I be able to run a query on my 'Space' object which only returns instances where the child class 'ParkingCampus' has the String 'country' set to a specific value, eg: "UK".
I was thinking something like:
sessionFactory.getCurrentSession()
.createCriteria(String.class)
.add(Restrictions.eq("country", "UK"))
.list();
But i'm not sure if that would compile correctly. So does Hibernate by default do a 'deep' search in an attempt to map results to my restriction criteria or do I need to do something else to specify the query to work in this way?
Any help would be greatly appreciated!

Use Space as the base criteria, create an alias for the parking campus, and add a restriction on the alias' child country to UK.
However, keep in mind that your implementation seems a bit off, IMO. There should be a table with a compound key of parkingCampusId and spaceId, rather than the Space owning the id.
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Space.class, "space");
criteria.createAlias("space.parkingCampus", "campus");
criteria.add(Restrictions.eq("campus.country", "UK");

Related

Hibernate implementation by pass in different types of params

So I am able to get Company information by passing in Company ID with Hibernate, code is as follows:
public Company getCompanyById(Integer companyId) {
Company company = (Company) getCurrentSession().get(Company.class, companyId);
return company;
}
Now what I am trying to do is to pass in company name, address and cell phone number to get the company. The method in my mind is like this:
public Company getCompanyByNameAddressCellphone(String companyName, String address, Integer cellPhoneNumber);
How do I implement it?
You can use Hibernate Criteria to achieve this. It would look something like:
public Company getCompanyByNameAddressCellphone(String companyName, String address, Integer cellPhoneNumber) {
Criteria criteria = getCurrentSession().createCriteria(Company.class);
criteria.add(Restrictions.eq("companyName", companyName));
criteria.add(Restrictions.eq("address", address));
criteria.add(Restrictions.eq("cellPhoneNumber", cellPhoneNumber));
return criteria.uniqueResult();
}
In this scenario, the String values provided to the Restrictions#eq call are the property names of your Company entity.
If you don't want to match exact String values and prefer using like, you can use Restrictions#like or Restrictions#ilike (case insensitive version).
You can either use Criteria/DetachedCriteria or HQL. A Criteria instance or a Query instance can both be retrieved from the session (referenced by the same getCurrentSession() call in your example).
Not sure what version of Hibernate you're working with, but here is the documentation on querying: https://docs.jboss.org/hibernate/core/3.3/reference/en-US/html/objectstate.html#objectstate-querying
the key points are that your property names (Company.name, Company.address, etc.) are used to query rather than the DB column names, which your code shouldn't be expected to know.
My personal preference is for DetachedCriteria. Used with the Restrictions API, you can accomplish about 85% of your querying needs. DetachedCriteria's execution flow is slightly different and documented here:
https://docs.jboss.org/hibernate/core/3.3/reference/en-US/html/querycriteria.html#querycriteria-detachedqueries

Using GROUP BY with OpenJPA

I'm using OpenJPA to load comments from my database into comment objects. Comment objects also have categories, sources (string), and comment time fields. This works great with OpenJPA, and I like my Comment object to the Comment table, and all is right with the world.
For a summary view, I'm interested in doing a GROUP BY query on the categories and sources so that for each source, I can see a break down of how many comments are available.
SELECT source, category, count(category) FROM Comments GROUP BY source,category
Now, my idea was to create this query using the entity manager, and have it somehow use a CommentSummary object instead of the Comment object. I have no idea how to tell OpenJPA how to do this. It seems like all the examples of using the GROUP BY do not consider also getting the base objects themselves.
I tried creating a view called 'CommentSummary', but OpenJPA wanted to modify the table to add an id field - perhaps if I simply told it the source and category fields were the primary keys it would work. I'm just a little confused that this isn't addressed directly anywhere that I can understand maps to my problem.
Has anyone done this successfully? What should I be doing differently?
Assuming you have an object CommentSummary with constructor that takes parameters source, category and count, you might try with:
SELECT NEW x.y.z.CommentSummary(c.source, c.category, count(c.category)) FROM Comments c GROUP BY c.source, c.category
CommentSummary object:
package x.y.z;
public class CommentSummary {
private String source;
private String category;
private int categoryCount;
// attribute getters/setters
public CommentSummary(String source, String category, int count) {
this.source = source;
this.category = category;
this.count = count;
}
}

set table name with hibernate name parameters

I need to set a table name dynamically so that I use query.setText(tname,abc)
e.g: select a.name from :tname where a.id = '2'
I used setText() because when I use setString() it says "tname is a invalid parameter" because I assume that Hibernate adds '' when setting string parameters.
But even setText() does not help and gives the same exception.
How can I set the table name dynamically?
Reply to PSR:
So you mean replace table name as a java string replacement. But then we can not take support of sql injections prevention etc from hibernate right? Also How we bind parameters in hibernate in a situation where like statement,
Eg: name like "%:name%"
This also gives me Illegal argument exception: Parameter does not exist as a named parameter when i try to bind it using query.setString(name,"def");
Hibernate will not do this for you, because it works with PreparedStatements, and you can't prepare a statement where the table being queried isn't known yet.
I don't see why you would be exposing table names to end users, so preventing SQL injection doing a regular string substitution should be easy. You use some sort of business logic to determine the correct table from a list that only you know. The table name isn't coming from user input at all.
Depending on your choice of RDBMS, you may find a discriminator column, or table inheritance with partitioning to be a better way of handling a situation where identical queries are made against different tables.
It is not possible to set table name dynamically.You can set dynamically column names.it is not possible to set table name
try like this
select a.name from '+table name+'where a.id = '2'
In my opinion, There are 2 ways to resolve this issue:
1- If you are using Spring and Hibernate together, you could use SpEL and it would be like #{#entityName} as it is described here
#Entity
public class User {
#Id
#GeneratedValue
Long id;
String lastname;
}
public interface UserRepository extends JpaRepository<User,Long> {
#Query("select u from #{#entityName} u where u.lastname = ?1")
List<User> findByLastname(String lastname);
}
2-You could use CriteriaBuilder like
CriteriaQuery<YourEntity> cr = cb.createQuery(YourEntity.class);
Root<YourEntity> root = cr.from(YourEntity.class);
cr.select(root);
I copied the source codes from the provided links and they are described there much better

match all the contents of a list in restrictions in hibernate query

I want to achieve above requirement.
I have a class like
public class Test{private String id;private List<Items> items;}
Item class like
public class Item{private String name; private String state;}
I want to query all item's states in a particular restriction group.
Let's say.
There are states like {Done,Done need to confirm,Reject}
and I want to have a query the all the item's states in {Done,Done need to confirm} group.
If there's is a one item's state not in above states should not give as a result for the query.
I check with this kind of thing.I want to know whether i'm doing this correctly or way to do it.
criteria.add(Restrictions.in("item.state", states));
Using the criteria like you tried:
criteria.add(Restrictions.in("item.state", states));
will give you the following error
org.hibernate.QueryException: could not resolve property: item of: Test
This is because you don't have an "item" property in your Test class.
To make it work, you need to create an alias for your Item and use it in your restriction:
Criteria crit = hibSession.createCriteria(Test.class);
crit.createAlias("items", "item_");
crit.add(Restrictions.in("item_.state", states));

HQL get a value from method

I would like in HQL to use the result of a abstract method in my "where" clause. Can this be done?
Something like this:
#NamedQuery(name="getMailRelations", query="Select mr from MailRelation mr where mr.getOccurrences() > 5"
I have a mapped super class something like this
#Entity
#Table(name="Mail_Entity", schema="relations")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="relationType", discriminatorType=DiscriminatorType.STRING)
#PersistenceContext(name="domas")
public abstract class MailRelation {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#ManyToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH, CascadeType.PERSIST})
#JoinColumn(name="mailId", referencedColumnName="mailId", nullable=false)
private Mail mail;
public void setMail(Mail mail) {
this.mail = mail;
if(!mail.getMailRelations().contains(this))
mail.addMailRelation(this);
}
public abstract int getOccurrences();
public Mail getMail() {
return mail;
// and more code......
No, that is impossible. The HQL code is translated into SQL and executed on the database. Generally Java methods can't be translated into SQL, and the database does not have any access to your Java code.
If you have a problem like this, there are for example these three possibilities to handle it. None of these possibilities is perfect.
1) You write the logic of the method in HQL (or SQL) using WHERE, GROUP BY and HAVING. In your example the getOccurrences() method seems to return a number of rows, which perhaps can be handled by `HAVING COUNT(...) > 5'.
2) You use database stored procedures. These are p. ex. procedures written in PL/SQL (in the case of Oracle). They can be accessed in select statements. But you loose the independency of the chosen database.
3) You load more rows than necessary and filter later in your Java code.
The solution is up to you, but I'm adding some additional options you can consider:
If you manage to precalculate the hash in all cases, use a parametrized named query:
#NamedQuery(name="getMailRelations", query="Select mr from MailRelation mr where :occurrences > 5"
then, you can call the query and add the parameter "occurrences":
String precalculatedHash = //your code here.
entityManager.createNamedQuery("getMailRelations",MailRelation.class).setParameter("occurrences", precalculatedHash).getResultList();
Another option is to go a little deeper with your hash logic, and determine what do you want to achieve with it. With that in mind you can use Criteria API to create a query and add all the restrictions represented by that hash. This can be a little tricky, so discard this option if the hash proves to be too context-depending (and I mean if it relies a lot on what do you have persisted, and the context of your application).
The third option is to bring all the results (or the smallest set of results possible, through either parameters or, again, Criteria API), and make your particular filtering logic.

Categories