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)
Related
Does anyone know why getById and GetReferenceById work differently?
I'm using JpaRepository and method GetReferenceById doesn't throw EntityNotFoundException but getById throwing this exception when object doesn't exists
getReferenceById returns a proxy and doesn't do a database call therefore no exception is called as JPA doesn't know if the entity with this ID exists.
getReferenceById executes EntityManager.getReference and the docs says:
T getReference(java.lang.Class entityClass, java.lang.Object
primaryKey)
Get an instance, whose state may be lazily fetched. If the
requested instance does not exist in the database, the
EntityNotFoundException is thrown when the instance state is first
accessed. (The persistence provider runtime is permitted to throw the
EntityNotFoundException when getReference is called.) The application
should not expect that the instance state will be available upon
detachment, unless it was accessed by the application while the entity
manager was open.
Source: https://jakarta.ee/specifications/persistence/2.2/apidocs/javax/persistence/entitymanager
Sometimes when using only getById as a way to fetch the entity makes some major issues. Usually we prefer to use getReferenceById method instead to save some extra calls, as if we were doing such using getById there is an extra call to database.
Let’s consider we have a Category child entity that is associated with the parent Product entity via the Product reference in the Category entity. Means each product has multiple categories. The code block that adds a new category to our product using productId as reference is below:
public Category addNewCategory(String categoryName, Long productId) {
Category category = new Category()
.setName(categoryName)
.setProduct(productRepository.findById(productId)
.orElseThrow(
()-> new EntityNotFoundException(
String.format(
"Product with id [%d] was not found!",
productId
)
)
)
);
categoryRepository.save(category);
return category;
}
If you want to add a category to a product, you first need to call findProductById each time you insert a new Category. When you got the Product then you insert it as a reference to the Category entity.
In this case Spring Data JPA would generate the following sql:
SELECT
product0_.id AS id1_0_0_,
product0_.slug AS name2_0_0_,
product0_.title AS title3_0_0_
FROM
product product0_
WHERE
product0_.id = 1
SELECT nextval ('hibernate_sequence')
INSERT INTO category (
product_id,
name,
id
)
VALUES (
1,
'book',
1
)
This query was generated by the findById method call, which is meant to load the entity in the current Persistence Context. However, in our case, we don’t need that. We just want to save a new Category entity and set the product_id Foreign Key column to a value that we already know.
But, since the only way to set the underlying product_id column value is to provide a Product entity reference, that’s why many developers end up calling the findById method.
In our case, running this SQL query is unnecessary because we don’t need to fetch the parent Product entity. But how can we get rid of this extra SQL query?
We can use getReferenceById method instead.
public PostComment addNewCategory(String name, Long productId) {
Category category = new Category()
.setName(name)
.setProduct(productRepository.getReferenceById(productId));
categoryRepository.save(category);
return category;
}
When calling the same addNewCategory method now, we see that the Product entity is no longer fetched as it will be fetched from a cached location where JPA holds entity Proxy. This way we can optimize our Spring applications that uses database intensively.
What you are saying seems a bit odd, as the implementation of the deprecated org.springframework.data.jpa.repository.JpaRepository#getById just delegates to it's replacement, org.springframework.data.jpa.repository.JpaRepository#getReferenceById.
As you can see in the implementation of that method (org.springframework.data.jpa.repository.support.SimpleJpaRepository#getReferenceById) it is directly using the EntityManager#getReference method.
When using Hibernate this normally only creates a Proxy, and just when you access one of the fields the Proxy fetches the real values from the DB - or throwing an EntityNotFoundException in case it does not exist.
Could it be that you changed something in your code, or you are being tricked by your debugger trying to display the toString of your method?
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 am using spring boot reactive and Couchbase
Flux<Item> findByLocation_LocationIdOrderByCreatedAtDesc(String locationId);
Just to point here I am accessing Location.locationId. Location object has locationId field in main document
Also below properties is not printing the generated Couchbase query, if anyone can help with that too,
logging:
level:
org.springframework.data: DEBUG
The response is fine when database has only one entry, but when the db has more than one entry matching criteria it throws "Source emitted more than one item" .
My controller is returning
Flux<Item>
Following two annotations are mandatory or it keep returning the exception
#N1qlPrimaryIndexed
#ViewIndexed
#Ghrissology I already had ReactiveCouchbaseRepository
Normally, the spring Data change the query based on the return type.
if you want one result it use getSingleResult() automatically :
Item findByLocation_LocationIdOrderByCreatedAtDesc(String locationId);
in you case, i think he didn't understand the return type because you didn't implement a ReactiveCrudRepository:
public interface ReactiveItemRepository
extends ReactiveCrudRepository<item, String> {}
In my controller I invoke a method from a service that will invoke save() on my database, and return me the object that was saved. That class that I am saving has a auto_generated id, so when i save it to the database, i expect to be returned with the id set (and that is working fine). In the same controller i store a result of that save() in a variable, and I found out that it's id is not set. That is because save() will actually save i to the database once that transaction is completed (when i exit the controller method. My problem is that i want to use that result before i exit my controller in a different service. How can I force the service (and consenquetly the repositroy) to save it immidiatelly and return me the result.
The reason for using id of classA in classB , is because classB is a "conncetion" table between two tables in database, and I should update it only when certain conditions are met, but this is of the point. I have already tried saveAndFlush() method in repository, that this service is calling, but it doesn't help. My service is only doing calling a save(), or saveAndFlush() method and nothing else (so it can't be a problem in the service).
I have already tried #Transactional annotation with REQUIRES_NEW, but it isn't working.
#PostMapping("")
public ClassA createClassA(#RequestBody ClassA classA){
ClassA a = classAService.saveClassA (classA);
System.out.println("Id = " + a.getIdClassA());
classBService.saveClassB(new classB(a.getId()); //it will cause an exception if a.getId() returns 0
return classA;
}
System.out.println will print out Id=0, but should print out Id=(some number that database makes, and cannot be zero because database has AUTO INCREMENT)
I have already tested all other services, repositories, connections etc. I am just interested how to force a response to come immidatelly so it can be stored in a variable and used later in the method.
Well, thank you for the comments, #JBNizet and #Lebecca you were both right :). Indeed saveAndFlush() would reslove my problem if I had told my class that id will be generated in the database. Thats why the soultion is to put something like #GeneratedValue(strategy = GenerationType.IDENTITY), and to add saveAndFlush(). It worked after these two steps.
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.