Some limitations about EJB 3.0 and Toplink framework - java

I have project use EJB 3.0 and implement Toplink framework for model layer.
When using EJBQL to process data, I see it seems have some limitation:
It cannot process datatime such as find a part of date such as day, month or year
It cannot find datetime among from...to
It cannot comparison datetime field
It cannot map a class not entity to a customize native select query because I want to get List data from SELECT statement but when I query in case join 2 or more table and map the object output into a class but impossible
#PersistenceContext private
EntityManager em;
em.createNativeQuery("SELECT
a.usertype , b.username, b.userpass
FROM tablea a, tableb b WHERE a.id =
b.id,MyClass.class).getResultList
.....
class MyClass(){
String usertype;
String username;
String userpass;
}
Could you help me any ideas?
Thank in advance!

It can not, do it in your code. Otherwise, you need to use something database specific on one side of your condition.
It can, why not. You can use between :fromDate and :toDate in the query, or use > :fromDate and < :toDate, in the NamedQuery. Where is the problem.
It can. Similar to the last one, use = sign instead
It can using #SqlResultSetMapping. Refer to this.

Related

Can #Query annotation in Spring Data JPA take in a list of enums?

I've been trying to figure out how to take an input of a list of enums for my sql query. This is what it looks like:
#Query(value = "Select * from employees where city in :cities", nativeQuery = true)
List<Employee> findByCities(#Param("cities") List<City> cities);
I understand that for simple queries we can rely on the JPA Criteria API but I want to know if I can actually do it this way instead. Because if so, i can create more complicated queries (such as joins with another table) if I could have this flexibility of specifying the list.
Yes spring-data-jpa's #Query can take a list of enums.
This is my repository method
#Query("Select u from User u where u.userType in :types")
List<User> findByType(#Param("types") List<UserType> types);
This is my repository call
userRepository.findByType(Arrays.asList(AppConstant.UserType.PRINCIPLE)))
And this is the query logs
SELECT user0_.id AS id1_12_,
user0_.date_created AS date_created2_12_,
...
...
FROM users user0_
WHERE user0_.user_type IN ( ? )
Hope this helps.
PS: I tested this in my local machine with positive results.
Update 1
The same doesn't work with nativeQuery=true. I tested it on my local system and it doesn't seem to be working with native queries. However with JPQL it works fine as mentioned in the above answer.
May be this answer will help.

JPA perfomance with case insensitive in select

I have a JPA select where you receibe a parameter then we can search using some attributes (username, email, identifier) The user only have a text field to write the criteria text.
The problem is perfomance, in the database We have about 9Millions of users registereds, and the search is too slow, using JPA,
Form:
- Value (Input text)
User sends the value in the form (He doesn't say if he is using username, email or identifier)
User (Table) Fields:
- Identifier
- Name
- Email
JPA Query:
select u from UserEntity u where u.alias LIKE lower(:query) OR u.email LIKE lower(:query) OR lower(u.identifier) LIKE lower(:query) ORDER BY u.alias
I don't know what the best method to improve the speed of the search, (We have some indexes in the table in these fields), if we remove the lower in the u.identifier field the speed improves a lot (almost instant). But we can have the identifier in a lot of ways (migrations, registers, manual client inserts..)
We have about 9 Millions of users registered, and the search is too slow, using JPA
Please note the search will be too slow regardless the technology involved in executing the query just because the SQL query itself is too slow. That being said the problem is not JPA but how to improve the SQL query.
Combining LOWER() function with LIKE operator adds a lot of overhead because the RDBMS must apply a text function to 3 fields before analyse the LIKE match.
IMHO a good approach is using a VIEW to leverage the LOWER() part on the 3 fields and then execute the query over this view (BTW mapping a View with JPA is just as simple as mapping a Table):
CRAETE VIEW user_view AS SELECT id, lower(identifier) AS identifier, lower(alias) AS alias, lower(email) AS email FROM user;
Then create an entity for searching:
#Entity
#Table("user_view")
public class UserView {
#Basic private Long id;
#Basic private String identifier;
#Basic private String alias;
#Basic private String email;
// getters and setters as required
}
And finally the JPQL query:
String jqpl = "SELECT u FROM UserView u WHERE u.alias LIKE :query OR u.email LIKE :query OR u.identifier LIKE :query";
Note that you can pass query parameter directly in lower case as a Query parameter and you can order the result list after the query execution. Both will result in the RDBMS making less effort and consequently improving the overall response time.
You can solve it using different techniques.
One is using a collate that ignores case. For example in MySQL https://dev.mysql.com/doc/refman/5.7/en/case-sensitivity.html
Another strategy is to have a derived field with an index. In DB2, for example, you can create a field 'lower_identifier generated always as lower(identifier)' and an index in the field and it will use it automatically when doing a lower search. You don't have to map the field in JPA.

JPA - Setting entity class property from calculated column?

I'm just getting to grips with JPA in a simple Java web app running on Glassfish 3 (Persistence provider is EclipseLink). So far, I'm really liking it (bugs in netbeans/glassfish interaction aside) but there's a thing that I want to be able to do that I'm not sure how to do.
I've got an entity class (Article) that's mapped to a database table (article). I'm trying to do a query on the database that returns a calculated column, but I can't figure out how to set up a property of the Article class so that the property gets filled by the column value when I call the query.
If I do a regular "select id,title,body from article" query, I get a list of Article objects fine, with the id, title and body properties filled. This works fine.
However, if I do the below:
Query q = em.createNativeQuery("select id,title,shorttitle,datestamp,body,true as published, ts_headline(body,q,'ShortWord=0') as headline, type from articles,to_tsquery('english',?) as q where idxfti ## q order by ts_rank(idxfti,q) desc",Article.class);
(this is a fulltext search using tsearch2 on Postgres - it's a db-specific function, so I'm using a NativeQuery)
You can see I'm fetching a calculated column, called headline. How do I add a headline property to my Article class so that it gets populated by this query?
So far, I've tried setting it to be #Transient, but that just ends up with it being null all the time.
There are probably no good ways to do it, only manually:
Object[] r = (Object[]) em.createNativeQuery(
"select id,title,shorttitle,datestamp,body,true as published, ts_headline(body,q,'ShortWord=0') as headline, type from articles,to_tsquery('english',?) as q where idxfti ## q order by ts_rank(idxfti,q) desc","ArticleWithHeadline")
.setParameter(...).getSingleResult();
Article a = (Article) r[0];
a.setHeadline((String) r[1]);
-
#Entity
#SqlResultSetMapping(
name = "ArticleWithHeadline",
entities = #EntityResult(entityClass = Article.class),
columns = #ColumnResult(name = "HEADLINE"))
public class Article {
#Transient
private String headline;
...
}
AFAIK, JPA doesn't offer standardized support for calculated attributes. With Hibernate, one would use a Formula but EclipseLink doesn't have a direct equivalent. James Sutherland made some suggestions in Re: Virtual columns (#Formula of Hibernate) though:
There is no direct equivalent (please
log an enhancement), but depending on
what you want to do, there are ways to
accomplish the same thing.
EclipseLink defines a
TransformationMapping which can map a
computed value from multiple field
values, or access the database.
You can override the SQL for any CRUD
operation for a class using its
descriptor's DescriptorQueryManager.
You could define a VIEW on your
database that performs the function
and map your Entity to the view
instead of the table.
You can also perform minor
translations using Converters or
property get/set methods.
Also have a look at the enhancement request that has a solution using a DescriptorEventListener in the comments.
All this is non standard JPA of course.

Java-Hibernate-Newbie: How do I acces the values from this list?

I have this class mapped
#Entity
#Table(name = "USERS")
public class User {
private long id;
private String userName;
}
and I make a query:
Query query = session.createQuery("select id, userName, count(userName) from User order by count(userName) desc");
return query.list();
How can I access the values returned by the query?
I mean, how should I treat the query.list()? As a User or what?
To strictly answer your question, queries that specify a property of a class in the select clause (and optionally call aggregate functions) return "scalar" results i.e. a Object[] (or a List<Object[]>). See 10.4.1.3. Scalar results.
But your current query doesn't work. You'll need something like this:
select u.userName, count(u.userName)
from User2633514 u
group by u.userName
order by count(u.userName) desc
I'm not sure how Hibernate handles aggregates and counts, but I'm not sure if your query is going to work at all. You're trying to select a aggregate (i.e. the "count(userName)"), but you don't have a "group by" clause for userName.
If the query does in fact work, and Hibernate can figure out what to do with it, the results you get back will most likely be a raw Object[], because Hibernate will not be able to map your "count(userName)" data into any field on your mapped objects.
Overall, when you get into using aggregates in queries, Hibernate can get a little more tricky, since you're no longer mapping tables/columns directly into classes/fields. It might be a good idea to read up more on how to do aggregates in Hibernate, from their documentation.

Hibernate Subquery Question

This should be a simple one I hope.
I have an invoice and that invoice has a list of payments.
Using the Criteria API I am trying to return a list of invoices and their payment total. So, in SQL I want something like this:
SELECT i.*, (SELECT SUM(PMT_AMOUNT) FROM INVOICE_PAYMENTS p WHERE p.INVOICE = i.INVOICE) FROM INVOICES i
I can't for the life of me figure out how to achieve this with the Criteria API. Doing something like:
Criteria crit = session.createCriteria(Invoice.class)
criteria.setProjection(Projections.projectionList()
.add(Projections.sum("payements.paymentAmount").as("paymentTotal"))
Simply returns 1 row with the projected payment total for all invoices, which is actually what you'd expect, but this is as close as I can get.
Any help is greatly appreciated.
There is a way with Criteria to return a list of Invoices along with the total payments for that invoice.
In theory, the answer is that you can use a grouping property on a projection query to group the result into total payment by invoice. The second part is that you could use a transient "totalPayment" value on the Invoice and select the projection into the Invoice structure using a transformer. This would be easier than dealing with an ArrayList of different properties but would depend on what you needed to use the results for.
To demonstrate this, here is the important part of a small Invoice class:
public class Invoice{
private String name;
#Transient private int totalPayments;
#OneToMany Set<Payment> payments = new HashSet<Payment>();
// getters and setters
...
}
Then this is the criteria that you could use
Criteria criteria = session.createCriteria(Invoice.class)
.createAlias("payments", "pay")
.setProjection(Projections.projectionList()
.add(Projections.groupProperty("id"))
.add(Projections.property("id"), "id")
.add(Projections.property("name"), "name")
.add(Projections.sum("pay.total").as("totalPayments")))
.setResultTransformer(Transformers.aliasToBean(Invoice.class));
List<Invoice> projected = criteria.list();
And this is the sql that is generated
Hibernate:
select this_.id as y0_,
this_.id as y1_,
this_.name as y2_,
sum(pay1_.total) as y3_
from invoice this_
inner join invoice_payment payments3_ on this_.id=payments3_.invoice_id
inner join payment pay1_ on payments3_.payments_id=pay1_.id
group by this_.id
I'm pretty sure you can't return entities in a Projection.
There are two possibles:
Run two criteria queries, one for the actual invoices and one for there totals
Use HQL to perform the query
I haven't tested this but it should go something like:
select i, (select sum(p.amount) from InvoicePayments p where p.invoice = i.invoice) from Invoice i
Will have to wait until tomorrow, I have a very similar data structure at work I should be able to test this then.
You can also use #Formula for the totalPayments field. Disadvantage is, that the "sum" is computed every time you load the entity. So, you may use LAZY #Formula - do build time enhancement or Pawel Kepka's trick: http://justonjava.blogspot.com/2010/09/lazy-one-to-one-and-one-to-many.html Disadvantage is, that is you have more LAZY #Fromula and you hit just one of them, all of them are loaded. Another solution may be to use #MappedSuperclass and more subclasses. Each subclass may have different #Formula fields. And one more solution beside DB view: Hibernate #Subselect.

Categories

Resources