Hibernate Dynamic #Where clause - java

I am having #Where at #Table level.
I need to get rows with EffDate field less than current date.
#Entity
#Table(name="party")
#Where(clause="EffDate < " + currentdate())
public class Party implements Serializable {
.......
}
I understand #Where takes constant expression which needs to be provided on compile time. But I need to compare with current date from Java and not from Database.
Any Idea how can I do that.

You can use the SQL function/constant for the current timestamp (depending on the type of database it might be called e.g. current_timestamp) (note however, that since this will be plain SQL, it will use the timestamp from the database server, not the server where the Java application runs on:
#Where(clause="EffDate < current_timestamp")

Write a Hibernate filter and enable it by default for all Sessions in the application. Depending on which framework you use for transaction management, you can add an interceptor/hook that enables the filter when transactions are started.

#Where annotation will not work, since it accepts constant expressions. Instead, I use findOnByEffectiveDateLessThanEquals from JPARepository.
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation

Related

Spring-data-couchbase - running non ad-hoc parametrized query

Is any possibility to execute parametrized N1QL query with turned off adhoc flag using annotation #Query?
I.e. for given query:
#Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} and author = $author")
List<Comment> getCommentsByAuthor(#Param("author") String author);
If not, is any other way to force couchbase to use secondary index (in this example index on author field) in annotation queries?
You seem to mix a few things:
Parametrized Query will work with the #Query annotation as in your snippet. The documentation mentions it (below the 1st warning block in this section)
N1QL secondary indexes should be automatically picked up by N1QL, depending on the statement. The subtlety here is that n1ql.filter. SDC needs that to limit such queries to the correct set of documents in an heterogeneous bucket.
adhoc is something else: it is about prepared statements. SDC doesn't use that feature, and it will only produce N1QL query with the default value for adhoc (which is still true as far as I know).
If you've logged the query produced by this annotation and run an EXPLAIN on it to effectively see that the index is not picked up, maybe try inverting the two expressions in the WHERE clause?

Hibernate entity only one column, no name

I want to map one column, without using a column name.
I am using a count entity, and the want to use mulple different queries with the same entity :
#Entity
public class CountDTO extends Number {
#Id
// below causes an error in my test, hsql not same syntax
#Column(name = 'COUNT')
private Long count;
In my prod (oracle) database I can just do select count() as COUNT from ... however, the same syntax doesn't work using hypersql in-memory db ?
Is their an oracle/hsql compatible way to map a single column alias in HQL ?
Your issue is that COUNT is a reserved keyword for HSQL, but not for Oracle.
According to the HSQL documentation, it might still be possible to use COUNT as identifier, if you either
Mask it as described in the Hibernate documentation or in the JPA spec (cf. chapter 2.13 from the JPA 2 spec; you'll need to accept their license agreement). Note that the JPA spec speaks of double quotes whereas the Hibernate documentation mentions backticks (which will be converted to the appropriate character according to the database dialect in use).
From the hibernate documentation:
You can force Hibernate to quote an identifier in the generated SQL by
enclosing the table or column name in backticks in the mapping
document. Hibernate will use the correct quotation style for the SQL
Dialect. This is usually double quotes, but the SQL Server uses
brackets and MySQL uses backticks.
From the JPA 2 spec:
Using annotations, a name is specified as a delimited identifier by
enclosing the name within double quotes, whereby the inner quotes are
escaped, e.g., #Table(name="\"customer\"").
Configure HSQL to allow it by executing SET DATABASE SQL NAMES FALSE (however, this should already be the default setting and it will only allow "the use of most keywords", not all - edit: COUNT will still be disallowed as per documentation)
My recommendation would be to avoid using identifiers if possible as you never know what problems may arise elsewhere (e.g. one might think Hibernate would be able to mask keywords itself) and use something like COUNT1 instead as column name.
The above part of the JPA spec also explains why Hibernate does not mask the name itself:
By default, the names of database objects must be treated as
undelimited identifiers and passed to the database as such.
The JPA spec also mentions a <delimited-identifiers/> option "to specify that all database identifiers in use for a persistence unit be treated as
delimited identifiers", but this seems to be only usable with an XML mapping file.

How to get selection with ManyToMany relationship in the Play framework [duplicate]

I have a simple jpa entity 'ApplicationForm' with a one to many list in it:
#OneToMany(cascade=CascadeType.REMOVE, mappedBy="textQuestion")
private List<Dictionary> questions;
The variable Dictionary contained in ApplicationForm is just another plain entity with just the text of the question.
The corresponding database table mapped by Dictionary is:
'locale' 'text' 'formId'
en my question 123
it mia domanda 123
I was wondering if it's possible with jpa or hibernate, to build a query for retrieving an ApplicationForm entity with a Dictionary for a specific locale, for example 'it' only.
That would be easy enough to do with standard sql, but I cannot translate in hql.
If not possible, could you suggest an alternative way ? I have tried to manually iterate the Dictionary questions list and remove the not required locale, but is not really elegant, and also I got a jpa/hibernate error.
I hope I made myself clear, and code supplied is enough.
thanks
I was wondering if it's possible with jpa or hibernate, to build a query for retrieving an ApplicationForm entity with a Dictionary for a specific locale, for example 'it' only.
Not with standard JPA. But Hibernate allows to apply arbitrary filters to a collection load during a given session. From the Hibernate Annotations Reference Guide:
2.4.8. Filters
Hibernate has the ability to apply
arbitrary filters on top of your data.
Those filters are applied at runtime
on a given session. First, you need to
define them.
#org.hibernate.annotations.FilterDef
or #FilterDefs define filter
definition(s) used by filter(s) using
the same name. A filter definition has
a name() and an array of
parameters(). A parameter will allow
you to adjust the behavior of the
filter at runtime. Each parameter is
defined by a #ParamDef which has a
name and a type. You can also define a
defaultCondition() parameter for a
given #FilterDef to set the default
condition to use when none are defined
in each individual #Filter. A
#FilterDef(s) can be defined at the
class or package level.
We now need to define the SQL filter
clause applied to either the entity
load or the collection load. #Filter
is used and placed either on the
entity or the collection element
#Entity
#FilterDef(name="minLength", parameters=#ParamDef( name="minLength", type="integer" ) )
#Filters( {
#Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length"),
#Filter(name="minLength", condition=":minLength <= length")
} )
public class Forest { ... }
When the collection use an association
table as a relational representation,
you might want to apply the filter
condition to the association table
itself or to the target entity table.
To apply the constraint on the target
entity, use the regular #Filter
annotation. However, if you wan to
target the association table, use the
#FilterJoinTable annotation.
#OneToMany
#JoinTable
//filter on the target entity table
#Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length")
//filter on the association table
#FilterJoinTable(name="security", condition=":userlevel >= requredLevel")
public Set<Forest> getForests() { ... }
See also
Chapter 17. Filtering data In the Hibernate Core Reference Documentation.
Hibernate3 Filters

Inject attribute into JPQL SELECT clause

Let's depict the following use case: I have a JPQL Query which on the fly creates data objects using the new keyword. In the SELECT clause I would like to inject an attribute which is not known to the database but to the layer which queries it.
This could look like
EntityManager em; // Got it from somewhere
boolean editable = false; // Value might change, e.g. depending on current date
Query q = em.createQuery("SELECT new foo.bar.MyDTO(o, :editable) FROM MyObject o")
.setParameter("editable", editable);
List<MyDTO> results = (List<MyDTO>) q.getResultList();
Any ideas how this kind of attribute or parameter injection into the SELECT clause might work in JPQL? Both JPA and JPA 2.0 solutions are applicable.
Edit: Performance does not play a key role, but clarity and cleanness of code.
Have you measured a performance problem when simply iterating over the list of results and call a setter on each of the elements. I would guess that compared to
the time it takes to execute the query over the database (inter-process call, network communication)
the time it takes to transform each row into a MyObject instance using reflection
the time it takes to transform each MyObject instance into a MyDTO using reflection
your loop will be very fast.
If you're so concerned about performance, you should construct your MyDTO instances manually from the returned MyObject instances instead of relying on Hibernate and reflection to do it.
Keep is simple, safe, readable and maintainable first. Then, if you have a performance problem, measure to detect where it comes from. Then and only then, optimize.
It will not work without possible vendor extensions, because according specification:
4.6.4 Input Parameters
...
Input parameters can only be used in the
WHERE clause or HAVING clause of a query.

How to get over limitations of the Hibernate Criteria and Example APIs?

I'm in a position where our company has a database search service that is highly configurable, for which it's very useful to configure queries in a programmatic fashion. The Criteria API is powerful but when one of our developers refactors one of the data objects, the criteria restrictions won't signal that they're broken until we run our unit tests, or worse, are live and on our production environment. Recently, we had a refactoring project essentially double in working time unexpectedly due to this problem, a gap in project planning that, had we known how long it would really take, we probably would have taken an alternative approach.
I'd like to use the Example API to solve this problem. The Java compiler can loudly indicate that our queries are borked if we are specifying 'where' conditions on real POJO properties. However, there's only so much functionality in the Example API and it's limiting in many ways. Take the following example
Product product = new Product();
product.setName("P%");
Example prdExample = Example.create(product);
prdExample.excludeProperty("price");
prdExample.enableLike();
prdExample.ignoreCase();
Here, the property "name" is being queried against (where name like 'P%'), and if I were to remove or rename the field "name", we would know instantly. But what about the property "price"? It's being excluded because the Product object has some default value for it, so we're passing the "price" property name to an exclusion filter. Now if "price" got removed, this query would be syntactically invalid and you wouldn't know until runtime. LAME.
Another problem - what if we added a second where clause:
product.setPromo("Discounts up to 10%");
Because of the call to enableLike(), this example will match on the promo text "Discounts up to 10%", but also "Discounts up to 10,000,000 dollars" or anything else that matches. In general, the Example object's query-wide modifications, such as enableLike() or ignoreCase() aren't always going to be applicable to every property being checked against.
Here's a third, and major, issue - what about other special criteria? There's no way to get every product with a price greater than $10 using the standard example framework. There's no way to order results by promo, descending. If the Product object joined on some Manufacturer, there's no way to add a criterion on the related Manufacturer object either. There's no way to safely specify the FetchMode on the criteria for the Manufacturer either (although this is a problem with the Criteria API in general - invalid fetched relationships fail silently, even more of a time bomb)
For all of the above examples, you would need to go back to the Criteria API and use string representations of properties to make the query - again, eliminating the biggest benefit of Example queries.
What alternatives exist to the Example API that can get the kind of compile-time advice we need?
My company gives developers days when we can experiment and work on pet projects (a la Google) and I spent some time working on a framework to use Example queries while geting around the limitations described above. I've come up with something that could be useful to other people interested in Example queries too. Here is a sample of the framework using the Product example.
Criteria criteriaQuery = session.createCriteria(Product.class);
Restrictions<Product> restrictions = Restrictions.create(Product.class);
Product example = restrictions.getQueryObject();
example.setName(restrictions.like("N%"));
example.setPromo("Discounts up to 10%");
restrictions.addRestrictions(criteriaQuery);
Here's an attempt to fix the issues in the code example from the question - the problem of the default value for the "price" field no longer exists, because this framework requires that criteria be explicitly set. The second problem of having a query-wide enableLike() is gone - the matcher is only on the "name" field.
The other problems mentioned in the question are also gone in this framework. Here are example implementations.
product.setPrice(restrictions.gt(10)); // price > 10
product.setPromo(restrictions.order(false)); // order by promo desc
Restrictions<Manufacturer> manufacturerRestrictions
= Restrictions.create(Manufacturer.class);
//configure manuf restrictions in the same manner...
product.setManufacturer(restrictions.join(manufacturerRestrictions));
/* there are also joinSet() and joinList() methods
for one-to-many relationships as well */
Even more sophisticated restrictions are available.
product.setPrice(restrictions.between(45,55));
product.setManufacturer(restrictions.fetch(FetchMode.JOIN));
product.setName(restrictions.or("Foo", "Bar"));
After showing the framework to a coworker, he mentioned that many data mapped objects have private setters, making this kind of criteria setting difficult as well (a different problem with the Example API!). So, I've accounted for that too. Instead of using setters, getters are also queryable.
restrictions.is(product.getName()).eq("Foo");
restrictions.is(product.getPrice()).gt(10);
restrictions.is(product.getPromo()).order(false);
I've also added some extra checking on the objects to ensure better type safety - for example, the relative criteria (gt, ge, le, lt) all require a value ? extends Comparable for the parameter. Also, if you use a getter in the style specified above, and there's a #Transient annotation present on the getter, it will throw a runtime error.
But wait, there's more!
If you like that Hibernate's built-in Restrictions utility can be statically imported, so that you can do things like criteria.addRestriction(eq("name", "foo")) without making your code really verbose, there's an option for that too.
Restrictions<Product> restrictions = new Restrictions<Product>(){
public void query(Product queryObject){
queryObject.setPrice(gt(10));
queryObject.setPromo(order(false));
//gt() and order() inherited from Restrictions
}
}
That's it for now - thank you very much in advance for any feedback! We've posted the code on Sourceforge for those that are interested. http://sourceforge.net/projects/hqbe2/
The API looks great!
Restrictions.order(boolean) smells like control coupling. It's a little unclear what the values of the boolean argument represent.
I suggest replacing or supplementing with orderAscending() and orderDescending().
Have a look at Querydsl. Their JPA/Hibernate module requires code generation. Their Java collections module uses proxies but cannot be used with JPA/Hibernate at the moment.

Categories

Resources