I have an entity Venue with its Events:
Event { ID, Name, DateTime }
Venue { ID, Name, #OneToMany List<Event> events}
What I would like to achieve is to be able to call these functions in view (with OpenEntityManagerInView):
customVenue.getId(); // no problem
customVenue.getName(); // no problem
customVenue.getEvents(); // no problem
customVenue.getCurrentEvents(); // hm?
customVenue.getPastEvents(); // hm?
There might be thousands of events in the database, so iterating over the "events" to get the current ones might not be a good idead.
Is this a correct approach? Is this feasible? How can I split events in current and past and to have them ordered?
Thanks!
Of course it's possible. But not using a method of customVenue (unless the entity has a reference to the entity manager, which is a bad idea).
Execute a query:
String jpql = "select event from Venue venue"
+ " inner join venue.events event"
+ " where event.date < :now"
+ " order by event.date asc";
List<Event> pastEvents = em.createQuery(jpql, Event.class)
.setParameter("now", new Date())
.getResultList();
Related
I've been working in legacy project that has several repository methods like this:
#Query(value = "SELECT "
+ " i.uf, "
+ " i.cidade "
+ "FROM Imovel AS i "
+ "WHERE i.ativo = 'Sim' AND "
+ " EXISTS (SELECT 1 "
+ " FROM ImovelFoto AS f "
+ " WHERE f.codigoImovel = i.codigo)"
+ "GROUP BY i.uf, i.cidade", nativeQuery = true)
List<Object[]> findUFCidade();
I would like to change it to an object like this:
public class LocalizacaoAgrupadaDTO {
private String uf;
private String cidade;
// Getters e Setters omitidos
}
This answer suggest changing nativeQuery for JPQL.
However, this change add an extra complexity due to the different syntax and the entities mapping, since this select clause is a groupping with no direct relation to entities.
What is the best way to refactoring this use of Spring Data JPA?
Should I always try to use JPQL or can I use nativeQuery??
There isn't really a "Best" way to refactor this using Spring Data JPA. There is just a spectrum of options that you can choose from.
I think the most "ideal" / modern option, though likely the most time consuming, would be to switch to using properly mapped objects and JPQL.
Your result wouldn't need to be an #Entity object itself though, you could use syntax like SELECT new LocalizacaoAgrupadaDTO(e.uf, e.cidade) FROM EntityObject e ...<rest of JPQL query to return the result of your query as a specific DTO type.
http://www.objectdb.com/java/jpa/query/jpql/select
Create a custom repository and than use the ResultTransformer.
Another example.
And you gone need get the object Session from hibernate from the EntityManager,here a example.
I have 2 methods that exhibit a different behavior in regard to flushing in Hibernate.
The first one is:
#Transactional
public void firstMthod(int id, int status) {
Person entity = session.get(Person.class, id);
entity .setStatus(personStatus.registered);
session.merge(entity);
updatePersonAge(id,18);
}
The updatePersonAge method is located in another class, and the SQL output of this method looks like this:
select personel0_.ID as ID1_119_0_,
personel0_.status as status2_119_0_,
personel0_.age as age3_119_0_,
personel0_.CreatedBy as CreatedBy4_119_0_,
personel0_.UpdatedBy as UpdatedBy5_119_0_,
personel0_.CreatedDate as CreatedDate6_119_0_,
personel0_.UpdatedDate as UpdatedDate7_119_0_,
personel0_.Ip as Ip8_119_0_
from tbl_personel personel0_
where personel0_.ID = ?
update tbl_person set status = ? where ID = ?
update tbl_person set age = ? where ID = ?
and for the second use case, we have the following method:
#Override
#Transactional
public void secondMethod(int id,int courseId, int status) {
Course courseEntity=session.get(Course .class, courseId);
courseEntity.setCreatedDate(new Date());
session.merge(courseEntity);
updatePersonAge(id,18);
}
For which the updatePersonAge method generates the following SQL output:
select course0_.ID as ID1_120_0_,
course0_.CreatedBy as CreatedBy7_120_0_,
course0_.UpdatedBy as UpdatedBy8_120_0_,
course0_.CreatedDate as CreatedDate9_120_0_,
course0_.UpdatedDate as UpdatedDate10_120_0_,
course0_.Ip as Ip11_120_0_
from tbl_course course0_
where course0_.ID = ?
update tbl_course set created_date = ? where ID = ?
update tbl_person set age = ? where ID = ?
The updatePersoneAge method is :
public int updatePersonAge(int id,int age){
Query query = session.createQuery("update " + domainClass.getName() + " e set e.age= :age ");
query.setParameter("age ", age);
return query.executeUpdate();
}
According to my expectations, the output of the second method should be the same with the output of the first method. So why the difference? It is really confusing.
First of all, it makes no sense to call merge on an entity that is already attached to the currently running Session. Merge is meant to be used when you want to attach a detached entity.
Second, Hibernate FetchMode.AUTO flush only triggers the flush if the query about to be run overlaps with entities in the ActionQueue.
In the first example, because you modified the Person, and the query is run against a Person, it makes sense to trigger a flush as otherwise, the SQL query might return stale results.
In the second case, you modify a Course entity, yet you want to select from Person. So, there is no need to trigger the flush.
You can control this behavior using Query.addSyncronizedEntityName.
Apart from updating the age explicitly, inside the updatePersonAge method, in each of those transactions you are also updating implicitly (by getting an entity and changing one of the fields) other field of a managed entity.
As you are merging those changes, the PErsistence Provider has the obligation to flush those changes at the end of the transaction.
Thats why when you change the status:
Person entity = session.get(Person.class, id);
entity .setStatus(personStatus.registered);
session.merge(entity);
Hibernate persists that change along with the explicit age upate:
update tbl_person set status = ? where ID = ?
update tbl_person set age = ? where ID = ?
In the second method when you change a field of the Course entity:
Course courseEntity=session.get(Course .class, courseId);
courseEntity.setCreatedDate(new Date());
session.merge(courseEntity);
That change is persisted along with the explicit age update:
update tbl_course set created_date = ? where ID = ?
update tbl_person set age = ? where ID = ?
I'm quite the rookie with JPA/JPQL, so please excuse me if this question is not crystal clear.
I am trying to find an efficient JQPL query in order to get all records of a complex object.
(ie. represented by multiple tables, with several one-to-many relationships - see simplified example below):
class ComplexObject {
private Set< SubOject1> so1 ...
.....
#OneToMany(fetch = FetchType.LAZY)
public Set< SubOject1>...
}
class SubObject1 {
private Set< SubOject2> so2 ...
.....
#OneToMany(fetch = FetchType.LAZY)
public Set< SubOject2>...
}
I am using the following JPQL query :
select distinct CO
from ComplexObject CO
left join fetch CO.so1 SO1
left join fetch SO1.so2
The query is run on a stateless session, in order to get a de facto snapshot of the current data in the DB, which is detached from the entity manager (hence the usage of left join fetch).
Unfortunately, I've encountered 2 problems :
Since the complex object contains multiple instances of so1, and each so1 instance contains multiple instances of so2, the underlying translation to SQL queries generates a specific select query per row of the product of all the table joins - a very wasteful solution. Is there a way to reduce the number of internal select queries? (This seems like the dreaded N+1 queries problem).
The JPQL query returns a ComplexObject instance per internal SQL query on the product of all the table joins - which means multiple references to the ComplexObject instances. Why does this happen on a 'select distinct' query?
The JPA framework I am using is hibernate, and the DB is HyperSQL.
The (1) issue turned out to be related to using the p6spy logging framework, which printed out all the results from a large DB table. The logging format led to an incorrect assumption that many queries where being executed.
While trying to fine tune performance, using native queries did not appear to have better performance then using JPQL queries.
Using a Native Query also resulted in Object typed results, which required post processing.
You can use View Objects to receive only the columns what you want:
StringBuilder sb = new StringBuilder();
sb.append(" SELECT new ").append(ObjectVO.class.getName()).append("(co.someInfo1, co.someInfo2, so1.someInfo )");
sb.append(" FROM ComplexObject co ");
sb.append(" JOIN co.subOject1s so1 ");
sb.append(" LEFT JOIN so1.so2 so2 ");
sb.append(" WHERE so1.id = :idSo1 AND so2 = :someThing");
Query q = em.createQuery(sb.toString());
q.setParameter("idSo1", idSo1);
q.setParameter("someThing", someThing);
List<ObjectVO> listResult = q.getResultList();
The ObjectVO class:
public class ObjectVO {
private String info1;
private Long info2;
private String info3;
public PedidoModel(String info1, Long info2, String info3){
this.info1 = info1;
this.info2 = info2;
this.info3 = info3;
}
}
I have related objects consisting of parent entities such as Organisation.java which has object-typed child attributes as #OneToMany lists like activities (i.e. List activitiyList) (Activiy.java has its own object-typed attributes.
It is very easy to use JPA persistence to do CRUD operations of these objects on a database, but my current requirement forbids me to use JPA, and implement the same functionality using only-JDBC - which I'm not sure how to implement.
How could the same functionality be implemented via JDBC when both parent and child objects are created for the first time (i.e. with all of the objects having null IDs)?
Assuming you have a foreign key relationship between Organisation and Activity, you must create the parent first, then the child rows with the parent id.
You can do this with spring, here's an old post, but the principals remain the same.
To implement manually, your database must provide a mechanism by which to generate primary keys for a given table without having to create a row first. Oracle supports sequence.nextVal, so your database should support something similar.
I'm pseudo-coding this, you can fill in the blanks:
try{
connection.setAutoCommit(false)
//get organisation id first
String nextOrgIdSql = "select orgSeq.nextval from someVirtualTable" //depends on database
ResultSet orgIdRs = statement.executeQuery( nextOrgIdSql)
int orgId = -1
if( orgIdRs.next())
orgId = orgIdRs.getInt(1)
//create organisation first
String orgSql =
"Insert into ORGANISATION (ORGID, ...) values ("+ orgId + ",...)"
//create activities
for( Activity activity : organisation.getActivityList()){
String nextActvIdSql = "select activitySeq.nextval from someVirtualTable"
ResultSet actvIdRs = statement.executeQuery( nextActvIdSql)
int actvId = -1
if( actIdRs.next())
actvId = actvIdRs.getInt(1)
statement.execute(
"Insert INTO ACTIVITY (ACTVID, ORGID) values ("+actvId+","+orgId+")"
}
connection.commit()
}catch(SQLException e){
connection.rollback()
}
I have 3 ways things get written to the DB
public void create(T object) {
entityManager.persist(object);
}
public void update(T object) {
object = entityManager.merge(object);
}
public int updateStatus(String id, String status) {
final int changes =
entityManager.createQuery("update item set state = :newState," +
" last_modified = current_timestamp" +
" where id = : id ")
.setParameter("newState", status)
.setParameter("id", id)
.executeUpdate();
return changes;
}
The problem I have, is in order to get the Hibernate Envers to actually write the audit records to the corrsponsing x_aud and revinfo DB tables. It only works successfully for '.persist()' or '.merge()'. I cannot get it to work for 'createQuery(...).executeUpdate()'
Am I missing something or does it just not work for this. The problem is, a lot of my code has been written using .executeUpdate and not merge, so really I need this to work with the existing code.
Can anyone help please?
No, Envers won't work if you are using executeUpdate. That is because the update doesn't pass through Hibernate's event mechanism, so Envers has no chances of intercepting the change, and writing the audit.
It looks like Avinash T. is right - if you want to create native SQL query, use createNativeQuery(String sqlString) method of EntityManager. Using createQuery(String ejbqlString) is only possible if you're using EJB QL. Hope it would help.
Try this -
public int updateStatus(String id, String status) {
final int changes =
entityManager.createQuery("Update Item set state = :newState," +
" lastModified = CURRENT_TIMESTAMP" +
" where id = : id ")
.setParameter("newState", status)
.setParameter("id", id)
.executeUpdate();
return changes;
}
Following link wiil help you to learn more about JPQL -
http://docs.oracle.com/javaee/6/tutorial/doc/bnbtg.html