I am using Spring,JPA using hibernate for service -> dao layer.
Transactions are spring managed.
I have validation testcase wherein , I need to validate for duplicate data insertion and throw an exception.
In my testcase ,which is extension to AbstractTransactionalJUnit4SpringContextTests
I have configured #TransactionConfiguration with defaultRollback as true and bean name for transaction manager (in my case its a bean of JpaTransactionManager)
I execute this testcase as below steps
Create a record with call to dao.create(entity); (this will succeed)
Create a same record (with all the attributes same as set in step 1) and call dao.create(entity) (this must fail, but its not failing)
In my create(entity) method I make call to validate() method, which fires scalar object query (JPQL) to validate.
I expect validation to be failed, but this works without exception and duplicate data gets inserted in DB.
I tried debugging (enabled hibernate logs),I found that the select query (scalar query) fails to get the proper data (ideally it should fetch at least 1 record, as I inserted data for it in step 1 listed above.)
I see Insert query for step1 in logs ,before select query for validation.
Is there any other way to write test case for such scenario which involves spring / jpa with hibernate?
Please post your views
Thanks in advance!!
Can you please paste your test here, I believe each test is running in its own transaction, and you have defaultRollback as true. That should be the issue.
Related
I am facing a weird problem with Spring Boot(2.3.7) + PostgreSQL v12 (row level security) + Hibernate (5.x).
Here are the steps that I am executing
A procedure accepts an input variable and creates temporary table. The variable is then inserted in temporary table.
Spring Advice which executes for all #Service annotation and invokes a procedure with a variable (call it custom_id).
#Transactional attribute is specified on all #Service classes.
PostgreSQL row level security has been enabled on the tables being queried and updated.
Row level security applies filter based on the variable stored (custom_id value) in temporary table.
All update, select, insert operations are executed using custom implementation of JpaRepository (interface based)
This works fine as long as there are only select operation performed on the database. But starts to fail with code having a combination of select and updates. The code simply fails with a message as it is not able to locate the temporary table.
I enabled trace for Spring transaction and found that there are few statements like
No need to create transaction for XXX
While code that performs update operation has statements like
Getting transaction for XXX
After searching for a while, I realised that SimpleJpaRepository has #Transaction with readonly flag set to true. This results in SELECT operation getting executing in transaction less mode.
Procedure
create or replace procedure proc_context(dummy_id uuid) AS $context_var$
declare
begin
create temp table if not exists context_metadata
(
dummy_id uuid
)
on commit drop;
insert into context_metadata values(dummy_id);
end;
$context_var$ LANGUAGE plpgsql;
ERROR
Following error is logged in console
ERROR: relation "context_metadata" does not exist
What I tried
Tried implementing custom transaction manager and explicitly invoking the procedure to set the temporary variable value (Didn't work). Refer below
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
super.prepareSynchronization(status, definition);
if (status.isNewTransaction() || status.isReadOnly() || status.isNewSynchronization()) {
UUID someID = ....;
Query query = entityManager.createNativeQuery("CALL proc_context(?);");
query.setParameter(1, someID);
query.executeUpdate();
}
}
Tried setting #Transactional notation with readonly set to false on all repositories.
What I am looking for?
Unfortunately due to this behaviour, the row-level security implementation is not working in my code. Is there any way to disable read-only transactions using a global property OR provide me with any hint to overcome this problem?
Finally, I could figure out after 2 days of battle. The problem was multi-faceted.
I noticed hibernate.transaction.flush_before_completion property set to true in application.properties file. I had to remove that property.
Developer had written a very messy code to update the entity attributes (Was performing select, then creating new instance, populating attributes and then calling save method). All this ruckus to update one single attribute.
Tested the code and everything worked fine.
I'm doing some junit tests to improve my application. One of these, tests the deletion of a single raw (indicated by an id as primary key) if present, and this works fine. Now I'm testing how my application behave if I want to delete an id Not present in my database.
What I expect is that my test passes with 0 rows affected, but he doesn't pass giving me this error:
No class com.package1.package2.package3.entities.className entity with id 326L exists!
Some advice?
deleteById() from CrudRepository firstly tries to find entity by Id.
In case no entity is found it throws exception; You can have your own repository and declare deleteAllByIdIn() method which takes collection of ids as argument and ORM will create its implementation for you.
This way you should not get any exceptions even if entities with such ids were not present. Or you can always make a native SQL query that deletes the row in DB by id.
SOLUTION:
The method should be :
#Query (your query, nativeQuery=true)
#Modifying
#Transactional
void DeleteById(#Param(id) Long id)
I am using below code to insert data into DB2 tables and it's part of a grails cron job. Job ran successfully but i don't see data inserted in Database tables. I don't see any errors also in application log. I reran the same job after some time and i see the data in database tables. I am not sure why i see this behaviour.
Code Snippet :
def conn = new Sql( dataSource )
For loop which is running from 1 to 11813
conn.execut("SQL Query")
Few things:
1. I have not explicitly called conn.close() since when we are using SQL with datasource we don't have to call this
2. Method in which i am using this conn.execute is Transactional.
3. This method contains hibernate object save also and it is like below:
if (!Object.save(flush:true, failOnError:true)) {
//throw exception
}
Can you please suggest. Thanks!
Try putting this code into a grails service and add the #Transactional annotation above the class name.
I've added database functionality using hibernate to a system which was in memory up to this point. When all the data was it the memory I was able to use JUnit which restored the original data after each test.
Is there a way to achieve the same result with the new hibernate addition?
By "the same result" I mean start with the database at its original state, do the test which can alter the database, and restore the database to its original state.
Up until now, my ideas are:
In memory database (which is a Hibernate feature) but that won't allow me to use my actual data.
Add "testing flag" to me DOA won't commit the changes if set.
I am sure there is a better solution, but I haven't found anything better yet.
You could start the database transaction before each test:
#PersistenceContext
private EntityManager em;
#Before
public void init() {
em.getTransaction().begin();
}
#After
public void destroy() {
em.getTransaction().rollback();
}
This way, each test has a transaction running before the test starts and this transaction is rolled back after the test finishes, so you always discard all changes the current test underwent.
I think we should be clear with the definition of Unit Test. Unit Test must only test a small unit (a public method) in the application.
Assuming you have a DAO layer which uses Hibernate to interact with Database. Now the Hibernate uses a SessionFactory that requires a dataSource. The data source of the Unit Test should not be same as the one for your production application.
The idea is to define a test datasource and use a in memory DB (hsqldb or any other). For each of the test case you can execute some queries on the in memory DB, using the test dataSource and clear that after the execution of the Unit Test. For each Unit Test you should execute the query so that the test data setup is done for that particular test.
For e.g.: If you want to test the following:
1) Create Account
2) Update Account
3) Delete Account
Then there are three test scenarios and there can bee multiple Unit Tests possible for each of the scenario.
Now before executing the Create Account Test, it is important that the DB doesn't have this account. and then you call the createAccount method in the DAO to test the same. No need to verify if the result is in DB or not. Just check the return of your method and if it is same as expected on a successful account creation then your test case should pass.
For Update Account, your setup method should insert one account through query and then you must call the updateAccount in DAO for this account id and so on.
Please stick to the definition of Unit Tests and do not use it for testing more than one functionality at a time.
Hope this helps.
Is it possible to stop hibernate from auto updating a persistent object?
#Transactional
public ResultTO updateRecord(RequestTO requestTO) {
Entity entity = dao.getEntityById(requestTO.getId());
// now update the entity based on the data in the requestTO
ValidationResult validationResult = runValidation(entity);
if(validationResult.hasErrors()) {
// return ResultTO with validation errors
} else {
dao.persist(entity);
}
}
Here is what happens in the code, I retrieve the entity which would be considered by hibernate to be in persistent state, then I update some of the fields in the entity, then pass the entity to validation. if validation fails, then don't udpate, if validation succeeds then persist the entity.
Here is the main issue with this flow: because I updated the entity for it to be used in the validation, it does not matter whether I call persist() method (on the DAO) or not, the record will always be updated because hibernate detects that the entity has been changed and flags it for update.
Keep im mind I can change the way i do validation and work around the issue, so I'm not interested in workarounds. I'm interested in knowing how i would be able to disable the hibernate feature where it automatically updates persistent objects.
Please keep in mind I'm using hibernates' implementation of JPA. so Hibernate specific answers dealing with hibernate specific API will not work for me.
I tried to look for hibernate configuration and see if I can set any configuration to stop this behavior but no luck.
Thanks
--EDIT ---
I couldn't find a solution to this, so I opted to rolling back the transaction without throwing any RuntimeException even though I'm in a declarative transaction using:
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
which works like a charm.
Configure FlushMode for your session.
http://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/FlushMode.html
You can use EntityManager.clear() method after getting object from database.
http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#clear()
You can call the following code:
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
Throw an exception if validation fails and have the caller handle that.