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.
Related
I have one table which has all the api audit information - Table name : api_audit
I have one table which has extra information about every api call - Table name : api_audit_info
Inside api_audit I have primary key as "transaction_id".
I want all the data from api_audit table and some data from api_audit_info table.
I have written a custom query like -
#Query(select c from ApiAudit c INNER JOIN ApiAudiInfo t ON c.transactionId = t.msgId)
But the issue is that the result type that I am getting this way contains only ApiAudit type data.
What shall I do to get data from both the tables. Please help.
Note: I am using JpaRepository as I need paginated data.
I am fairly new to Spring boot and JPA so not sure exactly which direction to look to.
Whenever I need to join data from more than 1 table, I am using Jdbi.
Here you have the official documentation:
Remember to include the required dependencies and configure a bean for Jdbi in your project.
Then I create a repository class, POJO and query with all of the information which I need. For example:
select c.transaction_id as transactionId, t.name as name from ApiAudit c INNER JOIN ApiAudiInfo t ON c.transactionId = t.msgId
Here you have some code samples from official documentation
After you map your data to POJO, you can use
public PageImpl(List<T> content, Pageable pageable, long total)
to return paginated data.
There is a big chance that there is a better solution, but this works for me every time.
I'm building REST API connected to ORACLE 11G DB. API sends data to Android client using JSON. To get data I'm using JpaRepository, and #Query annotations.
I want to provide data for charts: number of contracts in years.
I have native SQL query:
select aa.ROK, count(aa.NUMER_UMOWY)
from (select distinct NUMER_UMOWY, ROK from AGR_EFEKTY) aa
group by aa.ROK order by aa.ROK
Result of query using SQL Developer look like this:
I tried to get result using native query:
But result is always like this:
or error depending what I try.
Is it possible to obtain list of count() results using #Query?
If not, what should I use?
Thanks in advance :-)
I think What you are trying to use here is spring data projection.
As mentioned in the reference doc:
Spring Data query methods usually return one or multiple instances of
the aggregate root managed by the repository. However, it might
sometimes be desirable to create projections based on certain
attributes of those types. Spring Data allows modeling dedicated
return types, to more selectively retrieve partial views of the
managed aggregates.
and particularly closed projection where all accessor methods match the target attributes. In your case the count is not an attribute of your aggregate.
To perform what you want you can use constructor as follow :
class ContractsDto{
private String rok;
private int count;
public ContractsDto(String rok, int count) {
this.rok=rok;
this.count =count;
}
// getters
}
The query will be:
#Query(value = "select new ContractsDto(aa.rok , /*count */) from fromClause")
List<ContractsDto> getContractsPerYear();
I have a field in my SQL table which needs to be updated by one and return a unique ID. But looks like it is not being updated on the latest data, especially when I give a lot of requests.
#Transactional
public interface CompanyRepository extends CrudRepository<Company, Integer> {
#Modifying(clearAutomatically = true, flushAutomatically = true)
#Query(value = "update Company c set c.accessId = :accessId WHERE c.id = :companyId AND c.accessId = :oldAccessId", nativeQuery = true)
int updateAccessId(#Param("companyId") Integer companyId, #Param("accessId") Integer accessId, #Param("oldAccessId") Integer oldAccessId);
}
Even with both clearAutomatically and flushAutomatically set to true, it is not working on the latest data.
I could see two update query being successful both with oldAccessId as the same.
Should the table design be changed?
PS : I have tried without nativeQuery = true as well.
What you have here is a classical race condition.
Two threads read the same entity, with identical accessId, increment it by one and then writing the result using the method you show in your question. Resulting in effectively only one update.
There are various ways how to fix this.
Use JPA and optimistic locking.
Assuming you have an attribute with #Version annotated you can do the
following in a single transactional method:
Load the entity.
increment the accessId.
persist the entity.
If another transaction tries to do the same on the same entity one of the two will get an exception. In that case retry until the update goes through.
Use the database.
Make reading and updating atomic in the database. Instead of passing the new value as parameter use a query like this one:
update Company c
set c.accessId = c.accessId + 1
WHERE c.id = :companyId
Make it a version attribute.
As mentioned above JPA already has #Version attributes which get updated on every change. Depending on the exact requirements you might be able to make accessId simply that attribute and get it updated automatically.
Check if any rows got updated.
Based on your comment your intention was to basically reimplement what JPA does with version attributes. If you do this you are missing a crucial part: checking that the update actually updated anything, by comparing the return value against 1.
I have a question regarding querying in hibernate. If there is a way to search without specifying an where cause explicitly?
So what I mean is: let's say I have a search form with 10 columns that are bound to my dto fields. So the user can fill some of them and left the rest as nulls. And now I would like to search only by fields that are specified (filled) and left the nulls behind (they doesn't matter).
So the query would be like this:
select e
from entity e
where e.entity = e.searchedCriteriaEntityGivenInDTO
Or a better example via jpg: I'd like to have all sample entities without specifying "where number, where name, where firstanme", but over my dto by "where dtoFields". As mentioned the nulls should be ignored.
Thanks a lot in advance.
[EDIT]: Thanks to Dragon I have a great solution how to do it, but I have one more question: What about I have 2 row's, I can search? My query should look like:
select e
from example e,
where (e.entity = example) OR (e.entity = example2);
I tried to put the
session.createCriteria(MyEntity.class).add(Example.create(myEntityExample))
into an OR-Predicate but it seems it does not work.
Any suggestions?
No, you can't do it with DTOs, but you can use a prototype (example) entity instance for it:
MyEntity myEntityExample = new MyEntity();
myEntityExample.setNumber(12);
myEntityExample.setName("AA");
myEntityExample.setFirstName("BB");
List<MyEntity> results = session.createCriteria(MyEntity.class)
.add(Example.create(myEntityExample))
.list();
I have a database with this structure.
I am using JSP + Servlet + Entity Classes from database + Session Beans for entity classes. As you can see, my tables are normalized which in return makes it necessary to join tables to obtain the whole details of a patient/staff. As i studied the https://netbeans.org/kb/docs/javaee/ecommerce/intro.html i saw that they access the database by using the facade.find etc and etc. Considering my case, I have also tried using the same thing.
For example. I have a session bean (Profile Manager) which accesses the entities and puts it in the map.
public Map getPatientDetails(int patientID)
{
Map patientMap = new HashMap();
Patient patient = patientFacade.find(patientID);
User user = userFacade.find(patient.getUserId().getId());
UserContact userContact = user.getUserContact();
Family family = familyFacade.find(patient.getFamilyId().getId());
String patientDOB = new SimpleDateFormat("MMMMM dd, yyyy").format(user.getDateOfBirth());
patientMap.put("familyRecord", family);
patientMap.put("patientRecord", patient);
patientMap.put("patientDOB", patientDOB);
patientMap.put("userRecord", user);
patientMap.put("userContactRecord", userContact);
return patientMap;
}
As I give myself time to think about it, I thought that I can join the entities by using and setting a namedquery instead making it a single access. Which is the right way to do this? Do you think using facades to access my database is better than constructing an inner join query to acheive getting all the information at once? What would you guys suggest? Thanks!
I would suggest you to avoid joins in your SQL as, in my experience, it is one of the main root cause of performance issues associated to data access layer.
I would suggest to fetch entity one by one (like hibernate). In this method, there will be round trips to the database. But the SQLs will be simple and thus faster.