My case: Postgresql database, a 'spendings' table, a 'user' table with a 'total_spendings' column. I have a trigger on the 'spendings' table which updates the 'total_spendings' column.
I use Spring JPA and my test is annotated with
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
#Rollback
After insertion on the 'spendings' table, I do not know how to retrieve the new value of the 'total_spendings' column. I reloaded the User entity and it shows the old value.
If I remove #Transactional and #Rollback, it works as expected, but I have to clean up the data after testing.
How to do this in a #Rollback unit test transaction?
Thank you
As Davidxxx said you MUST execute flush() to force JPA to execute the SQL statements and this will execute the trigger.
// Inject the EnitityManager in your test class
#PersistenceContext
private EntityManager em;
// Force JPA to execute the SQL statements
em.flush();
Related
I am new to jhipster. I am trying to implement a test method to test values which are retrieved from mysql db. When I am trying to execute "gradlew test" command it will fail the relevant test case by saying "java.lang.AssertionError: No value at JSON path "$.[0].id". And I have added H2 Console db table values manually. My test method as follows.
#Test
#Transactional
public void getAllPlayersNS() throws Exception {
// Get all the playersNList
restPlayersNMockMvc.perform(get("/api/players-ns?sort=id,asce"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("$.[0].id").value(1));
}
Where I went wrong?
As far as I know the integration tests in JHipster use H2 in memory so the changes you made using H2 console are probably not used by these tests and so our table is empty (this is what your failed assertion means) because they were stored in H2 on disk in target/h2db folder (if you chose this option at project generation).
So either, your test should create players using PlayerNSRepository or you should add a Liquibase migration that loads them from CSV (look at users.csv) and restrict it to H2 db and maybe using test Liquibase context.
since upgrading to EclipseLink 2.5 from EL 2.1 we get a PersistenceException on a previously running code.
The scenary is really simple: we have two identical tables, the only difference is that one is a "history" version of the other. Basically, when we are sure that a row won't change anymore, we move the row to the history table. This means that while the id is generated on the first table, it is "inherited" on the history table. Here are the entities:
#Entity
#Table(name = "DOCUMENT")
public class Document implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
// Some other fields
}
#Entity
#Table(name = "DOCUMENT_HISTORY")
public class DocumentHistory implements Serializable {
#Id
private long id;
// Some other fields
}
In order to move a row to the history table we use a native query (because some columns are not mapped as fields in the entity classes):
String query = "INSERT INTO DOCUMENT_HISTORY SELECT * FROM DOCUMENT t WHERE t.id=?1";
Query updateQuery = entityManager.createNativeQuery(query);
updateQuery.setParameter(1, document.getId());
updateQuery.executeUpdate();
When executing this query, sometimes this exception is thrown:
javax.persistence.PersistenceException: Exception [EclipseLink-7251] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5):
org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [id] of class [org.myc.entities.jpa.company.DocumentHistory] is mapped to a primary key column in the database. Updates are not allowed.
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:868)
at org.eclipse.persistence.internal.jpa.QueryImpl.performPreQueryFlush(QueryImpl.java:963)
at org.eclipse.persistence.internal.jpa.QueryImpl.executeUpdate(QueryImpl.java:296)
at org.myc.utility.jpa.user.DocumentManager.toDocumentHistory(DocumentManager.java:141)
at java.lang.Thread.run(Thread.java:662)
Caused by: Exception [EclipseLink-7251] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [id] of class [org.myc.entities.jpa.company.DocumentHistory] is mapped to a primary key column in the database. Updates are not allowed.
at org.eclipse.persistence.exceptions.ValidationException.primaryKeyUpdateDisallowed(ValidationException.java:2548)
at org.eclipse.persistence.mappings.foundation.AbstractDirectMapping.writeFromObjectIntoRowWithChangeRecord(AbstractDirectMapping.java:1257)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildRowForUpdateWithChangeSet(ObjectBuilder.java:1768)
at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.updateObjectForWriteWithChangeSet(DatabaseQueryMechanism.java:1030)
at org.eclipse.persistence.queries.UpdateObjectQuery.executeCommitWithChangeSet(UpdateObjectQuery.java:84)
at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:301)
at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:899)
at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:798)
at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)
at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1793)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1775)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1726)
at org.eclipse.persistence.internal.sessions.CommitManager.commitChangedObjectsForClassWithChangeSet(CommitManager.java:267)
at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsForClassWithChangeSet(CommitManager.java:192)
at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:138)
at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4196)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1441)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithPreBuiltChangeSet(UnitOfWorkImpl.java:1587)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.writeChanges(RepeatableWriteUnitOfWork.java:452)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:863)
... 5 more
Did anyone have the same problem? Why do you think this doesn't happen on all rows but only some of them (with no apparent specific characteristics)?
Thanks in advance,
Luc
P.S.: Is it correct for EL to perform mapping/query validation on a native query?
As stated in JPA 2.0 specification, executeUpdate() executes an update or delete statement thus insert is not supported.
You can insert an entity into the underlying database by persisting and flushing it with use of EntityManager (within a transaction). An dummy example in your particular case might look as follows:
// transaction starts
Document doc = em.find(Document.class, id);
DocumentHistory docHistory = new DocumentHistory(doc);
em.persist(docHistory);
// transaction ends
NOTE: instead of a proprietary solution (which is fine) you may consider of using EclipseLink's auditing feature:
EclipseLink also support full history support, which allows a complete history of all changes made to the database to be tracked in a mirror history table.
Ok, I found the problem and it wasn't related at all with that query, so I still don't know why the exception is thrown there.
The problem was that instead of using entityManager.find() to get the newly inserted history row, we were creating a DocumentHistory instance (with same ID) which was not attached to a persistence context, then using that instance to perform other operations.
Hope it helps someone.
I have a rather complex query here, where I also need to return the total of available result sets in addition to a limited result set.
There is the SQL_CALC_FOUND_ROWS option in MySQL, which allows to return that number in a subsequent query by using an interceptor. I have already implemented this in a similar project, by using a hibernate with a native query and an interceptor and it worked fine.
However in this case, there already is this complex JPA Criteria query and I would be more than happy if I could add that mysql option to the criteria query, but couldn't find a way to do this so far.
Below are some details about how the entityManager and Criteria is set up
#Stateless
class Dao{
#PersistenceContext(unitName = "persistenceName")
private EntityManager entityManager;
#TransactionAttribute(TransactionAttributeType.SUPPORTS)
public List<Entity> find(String someParam) {
CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Entity> cq = cb.createQuery(Entity.class);
Root<Entity> root = cq.from(Entity.class);
// some dummy predicate here
cq.where(predicates);
TypedQuery<Entity> query = this.entityManager.createQuery(cq);
// limit query
query.setFirstResult(100);
query.setMaxResults(10);
return query.getResultList();
}
}
Any ideas how to add the SQL_CALC_FOUND_ROWS option?
This thing runs in Glassfish 3 with Hibernate 3.5
I had the same issue when using eclipselink, and this fixed it for me:
query.setHint("eclipselink.sql.hint", "SQL_CALC_FOUND_ROWS");
The eclipselink.sql.hint flag instructs the string to be added immediately after the first word in the query, in this case SELECT. There may be an equivalent for Hibernate.
I am using hibernate to connect to a mysql database.
My saveOrUpdate method is :
public void saveOrUpdate(T t){
Session session = HibernateUtil.getSession();
Transaction transaction = session.beginTransaction();
session.saveOrUpdate(t);
transaction.commit();
}
I used this method actually for about half year, it worked well.
But now when I do junit test using this method, it seems suck.
I do saveOrUpdate first and then use Criteria to query, it gets the result seems to be right, but in database nothing actually changed.
Could anyone please give me any hint about what's happening here?
Thank you very much.
What is your JUnit configuration set to for transactions? I believe it is set to rollback all the transactions in the test. It should rightly be so, as after a test the database should be put back into a consistent/known state. The correctness of your code should rely on how you write the test and not the records being physically present in the database.
There are a lot of examples over the net which describe how to call a stored procedure using Hibernate, however, when using Spring, the picture changes a bit.
I have a stored procedure in MySQL which I want to call:
in SQL I need to write the following:
CALL inrange(32.342324,32.234234);
It returns a row with the following: `{INT},{INT},{FLOAT}`
With Spring, I use the HibernateTemplate way of executing hibernate operations, I know that some of you won't like it, but this is the how the project was when I started, and I'm not so eager changing it, maybe in the future...
Currently, I have the following code in Java, which tries to call the procedure:
List<Object[]> resultset = hibernateTemplate
.findByNamedQuery("inrange",
person.getAddress().getLatitude(),
person.getAddress().getLongitude());
When I run it, I get the following Hibernate exception:
org.springframework.orm.hibernate3.HibernateSystemException:
Named query not known: inrange;
I figured that this is happening duo the fact that I didn't declare the stored procedure in hibernate.
My question is:
how do I declare it ?
Is there a special way of declaring it in the Spring's application context file ?
You can call native sql queries within hibernate.
Look at this link:
http://www.mkyong.com/hibernate/how-to-call-store-procedure-in-hibernate/
Btw if you want to call stored procedures you could simply use a Spring JdbcTemplate.
Notice that an hibernate extension can fit to your needs:
http://www.hibernatespatial.org/
You're confusing Hibernate's named queries with MySQL's stored procedures.
If you want to call the MySQL stored proc, there is no benefit to doing so through Hibernate's API. I recommend you use Spring's JdbcTemplate to perform the query.
If you absolutely must use Hibernate, something like this should work:
SQLQuery query = hibernateTemplate.getCurrentSession()
.createSQLQuery("SELECT inrange(:latitude, :longitude)";
query.setDouble("latitude", ...);
query.setDouble("longitude", ...);
List<Object[]> result = query.list(); // requires casting for generics
You need to add the named query to your hibernate mapping file.
Can you share your hibernate mapping file? You can find some samples here.
Along with the previous link you can go through this also.
It will be easier if you can share the POJO, hibernate mapping and the procedure you are using.
This blog will be of help for you. I hope you will not have any problem with using the getHibernateTemplate().execute(HibernateCallback) method.
You can use JPA as Spring supports it either in Core or Spring Data.
Calling the stored procedure can be done using the StoredProcedureQuery as follows:
StoredProcedureQuery query = entityManager
.createStoredProcedureQuery("count_comments")
.registerStoredProcedureParameter(
"postId", Long.class, ParameterMode.IN)
.registerStoredProcedureParameter(
"commentCount", Long.class, ParameterMode.OUT)
.setParameter("postId", 1L);
query.execute();
Long commentCount = (Long) query
.getOutputParameterValue("commentCount");