What is the right way do validate a jpa query programmatically. Hibernate validates all annotation based named queries on entities. But how can I call this validation routine on programmatically builded jpa queries, to check for errors?
#Entity
public class Foo {
#Id
public int id;
public String name;
}
main(...) {
Query q = getEntityManager().createQuery("select e from " + Foo.class.getName() + " e where e.name = 'x' ");
// validate q here
}
Don't. Integration test the hell out of your code, using a real database with the same schema as your production environment.
Think about it: if you create a malformed query, that's a programming bug. What are you going to do with the information? Tell the user that a JPA query is malformed? All you can realistically do is log the error and tell the user "something bad happened". You'll know it's a malformed query when you check the logs later anyway...
Edit
It might also be worth investigating the createQuery() call by feeding it bad data - the javadoc here suggests that it can throw a HibernateException, and I'm not sure what it could do with a string other than validate it...
Either you can use createQuery, or you need to put your class name while writing a JPQL.
Related
I'm new in the world of spring and Spring Data.
I've build a user- and permission management system where a user can grant and remove permissions for another user.
I've digged in the docs but I'm not shure how to handle querys where I just want to insert or manipulate Data in a Table.
In the docs query return type table I couldn't find something like a status or a status reporting object.
What I'm looking for is a pattern that allows me to controle is an operation was successfull. Right now I'm using primitives. When everything worked out I get "1" returned but when I query an empty table I get "0". So I need something with more details.
Thank you for guidance and sharing your experience.
EDIT:
Here is a code example:
#Transactional
#Modifying
#Query(value = "DELETE FROM permissions WHERE producerId=:producerId AND comsumerId=:consumerId", nativeQuery = true)
void clearAllPermissions(#Param("producerId") Long producerId,#Param("consumerId") Long consumerId);
The Method is provided by my repository class.
#Repository
public interface PermissionsRepository extends JpaRepository<ProducerConsumerPermissions, Integer>{
.
.
.
}
I call the class from my service layer which is used by my Controller layer.
I guess it'd be nice to know if this operation was successfull so that I can transport the exeption throught the layer till my frontend and throw a message to the user. But when the Table is empty I get a value of false back when I use Integer as return type.
Make your method return int then you know if any records are deleted.
#Transactional
#Modifying
#Query(value = "DELETE FROM permissions WHERE producerId=:producerId AND comsumerId=:consumerId", nativeQuery = true)
int clearAllPermissions(#Param("producerId") Long producerId,#Param("consumerId") Long consumerId);
If there is a problem it will throw and exception anyway. Spring Data throws DataAccessException that you can catch to see what went wrong.
I just got started with Spring JPA and I am reviewing code written by my coworker.
I see that he has been using the following code to find a Login object using username:
public interface LoginDao extends JpaRepository<Login, Long> {
#Query("SELECT u FROM Login u WHERE LOWER(u.username) = LOWER(:username)")
Login findByUsername(#Param("username") String username);
}
Cant he just create a method like this:
#GET
#Path("{username}")
public Login getOne(#PathParam("username") String username) {
Login login = loginDao.findOne(username);
if (login == null) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
} else {
return login;
}
}
What are the fundamental advantages of using #Query rather than writing a method approach. Or am I plain wrong and both have different purposes.
I personally dont want to write queries inside the code. I think mixing java with sql queries can make code look uglier.
Our stack is java, JPA, Jersey, JAX-RS, Postgreql, Spring Boot, Hibernate
Regards
First, This is not an SQL query, this is a JPQL one. It would be a native SQL query if the nativeQuery attribute was set to true, which is not the case by default.
It is perfectly fine to create queries with JPQL, you will be able to switch from Hibernate to Eclipse Link or another JPA implementation without issues. You should also be able to switch from Postgres to MySQL, SQL Server...
You have to start to worry if your coworker creates queries with #Query(nativeQuery = true, value = "..."), otherwise it looks fine to me.
Second, when you look to your repository declaration, you can see that the ID field for your Login entity is of type Long. That means the loginDao.findOne(..) method wants a Long parameter. Probably a surrogate key which is not username.
This is why your coworker created this new method: to find a Login row by something else than the Long surrogate key. (Here by username which is most likely a business key, but I do not know your application.)
Just for your information: Spring automatically creates queries with the signature of your repository methods (derived queries). For example:
public interface LoginDao extends JpaRepository<Login, Long> {
Login findByUsername(String username);
}
Spring JPA will automatically create a query looking for a field named username and create a WHERE clause with it.
Notice that it is not the same than your coworker query because it will not use the LOWER function.
Some anchors in the JPA documentation about those derived queries:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repository-query-keywords
Another thing about your title:
"Why need #Query when I can write a method in Spring JPA"
Well this #Query writes a method in Spring JPA. :^)
JPARepository has come up with some of the Supported keywords which will write the queries themselves based on your entity.
If we are looking something out of box from what JPARepository provides #Query is useful like - Joining multiple queries, writing native queries etc.
From your code snippet both would do the same
For more info you can refer - https://docs.spring.io/spring-data/jpa/docs/1.4.2.RELEASE/reference/html/jpa.repositories.html
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
I have the following Entities (reduced and renamed for this example)
#Entity
public class Case {
#Id
private Long id;
#ManyToOne(optional=false)
private CourtConfiguration courtConfiguration;
#ElementCollection(fetch=FetchType.EAGER)
private List<String> caseNumbers;
}
Second Entity
#Entity
public class CourtConfiguration {
#Id
private Long id;
String countyId;
String referenceId;
....
}
I am trying to search using JPQL for all Cases that have a certain courtConfiguration countyId and have caseNumbers containing all of a provided set of important caseNumbers.
So my query needs the countyId and set of caseNumbers as parameters. Called countyId and importantCaseNumbers respectively.
I have tried and failed to get it to work.
My query looks like this
String query = "SELECT case FROM Case case JOIN case.caseNumbers caseNumbers WHERE ";
query += "case.caseConfiguration.countyId = :countyId ";
The bit above works until I add my caseNumber conditions.
I have tried a foreach importantNumbers to extend the query and as soon as the list of important numbers goes above one it doesn't work. No values get returned.
for (String importantCaseNum : importantCaseNumbers) {
query += " AND '"+importantCaseNum+"' in (caseNumbers)";
}
Any suggestions/pointers appreciated. I guess what I am looking for is a case.caseNumbers contains (importantNumbers) clause.
Update I have reverted to native SQL for my query as I didn't want to tie myself into hibernate by using HQL. Thanks to #soulcheck and #mikko for helping me out. I'll post up when the hibernate JPA fix is available.
Thanks
Paul
Syntactically correct way to build this JPQL query is with MEMBER OF. But because of problem reported in HHH-5209 it doesn't work with old Hibernate versions (fix version 4.1.8, 4.3.0.Beta1). According bug report HQL version of this query works, so your options includes at least:
Using JPQL query and switching to some other JPA implementation
Using HQL instead and sticking with Hibernate:
query += " AND '"+importantCaseNum+"' in elements(caseNumbers)";
I'm still in the process of learning hibernate/hql and I have a question that's half best practices question/half sanity check.
Let's say I have a class A:
#Entity
public class A
{
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(unique=true)
private String name = "";
//getters, setters, etc. omitted for brevity
}
I want to enforce that every instance of A that gets saved has a unique name (hence the #Column annotation), but I also want to be able to handle the case where there's already an A instance saved that has that name. I see two ways of doing this:
1) I can catch the org.hibernate.exception.ConstraintViolationException that could be thrown during the session.saveOrUpdate() call and try to handle it.
2) I can query for existing instances of A that already have that name in the DAO before calling session.saveOrUpdate().
Right now I'm leaning towards approach 2, because in approach 1 I don't know how to programmatically figure out which constraint was violated (there are a couple of other unique members in A). Right now my DAO.save() code looks roughly like this:
public void save(A a) throws DataAccessException, NonUniqueNameException
{
Session session = sessionFactory.getCurrentSession();
try
{
session.beginTransaction();
Query query = null;
//if id isn't null, make sure we don't count this object as a duplicate
if(obj.getId() == null)
{
query = session.createQuery("select count(a) from A a where a.name = :name").setParameter("name", obj.getName());
}
else
{
query = session.createQuery("select count(a) from A a where a.name = :name " +
"and a.id != :id").setParameter("name", obj.getName()).setParameter("name", obj.getName());
}
Long numNameDuplicates = (Long)query.uniqueResult();
if(numNameDuplicates > 0)
throw new NonUniqueNameException();
session.saveOrUpdate(a);
session.getTransaction().commit();
}
catch(RuntimeException e)
{
session.getTransaction().rollback();
throw new DataAccessException(e); //my own class
}
}
Am I going about this in the right way? Can hibernate tell me programmatically (i.e. not as an error string) which value is violating the uniqueness constraint? By separating the query from the commit, am I inviting thread-safety errors, or am I safe? How is this usually done?
Thanks!
I think that your second approach is best.
To be able to catch the ConstraintViolation exception with any certainty that this particular object caused it, you would need to flush the session immediately after the call to saveOrUpdate. This could introduce performance problems if you need to insert a number of these objects at a time.
Even though you would be testing if the name already exists in the table on every save action, this would still be faster than flushing after every insert. (You could always benchmark to confirm.)
This also allows you to structure your code in such a way that you could call a 'validator' from a different layer. For example, if this unique property is the email of a new user, from the web interface you can call the validation method to determine if the email address is acceptable. If you went with the first option, you would only know if the email was acceptable after trying to insert it.
Approach 1 would be ok if:
There is only one constraint in the entity.
There is only one dirty object in the session.
Remember that the object may not be saved until flush() is called or the transaction commited.
For best error reporting I would:
Use approach two for every constraint violation, so I can give an specific error for each of them..
Implement an interceptor that in case of an constraint exception retries the transaction (a max number of times) so the violation can't be caught in one of the tests. This is only needed depending on the transaction isolation level.