The given id must not be null with Spring Data - spring

I'm developing an application written in Spring Framework 5, I'm also using Spring Data to access the PostgreSQL database, and I have a strange problem which occurs sometimes when I'm querying the database.
In short, I have a service which contains myMainFunction. That function is called from the controller, and that function calls myFunction which calculates and sets the ID to the object. myFunction then also queries the database to find if the object with calculated ID already exists in the database.
Then myMainFunction again quires the database to get the object with the same ID, if it exists. This is the line where java.lang.IllegalArgumentException: The given id must not be null! is sometimes thrown.
I can't understand why is this happening, and why is that happening sometimes, not all the time?
This is the "pseudo-code" which causes the problem:
publi class MyService {
...
public myMainFunction(MyObject object) {
...
myFunction(object);
Optional<MyObject> oldObject = objectRepository.findById(object.getId());
...
}
private void myFunction(MyObject object) {
...
object.setId(calculateId(...));
Optional<MyObject> existingObject = objectRepository.findById(object.getId());
...
}
...
}

Related

How to decide a type of GetMapping method in Spring boot?

I'm spring boot learner and trying to clone-code a website. Below is a code to get a data of the specific content.
#GetMapping("/api/articles/{id}")
public List<Article> takeArticle() { return articleRepository.findAllByOrderByModifiedAtDesc();}
Then the ARC shows whole data of contents which I've already posted, but I want a specific data according to the id value. I think the problem is the type of takeArticle() method. So which type should be used for the method above to fulfill my purpose?
#GetMapping("/api/articles/{id}")
public Article takeArticle(#PathVariable Integer id) {
return articleRepository.findById(id).orElseThrow(() -> {
// throw Not found exception if article doesn't exists with given id
});
}
By the way you shouldn't use repository interfaces directly in your controller layer. Use service layer between repository and controllers.

Data not updated in oracle-db within the running funtion

From a method callAndUpdateInB(), Suppose I am calling update() method of class B(#Component), in which I am calling an myRepository.save() method to update some data in db, and in same funtion I am performing some other calls ... and then return the response back to class A.
So the problem is data gets updated in db when class B method update() return the response back to class A method callAndUpdateInB().
But it should have updated it when I have called myRepository.save() in update method of class B().
Why so ?
For Reference, just see this dummy example
class A{
#Autowired
B b;
public void callAndUpdateInB(String arg){
String data = b.update(arg);
// check Updates in Db (True)
// Now data is updated in db
}
}
#Component
class B{
#Transactional(
propagation = Propagation.REQUIRED
)
public String update(String arg){
MyRepository myRepository; // This is abstract class having
// imlementation for the following
// data. (MyRepositoryImpl)
String updatedData = myRepository.save(arg);
// check Updates in Db (False)
// Making some other calls, which need that updated data
// But data is not still updated in db.
// Though here the updated data field is showing that the data is updated, but it
// is not actually updated in the db.
return updatedData;
}
}
The transaction will be commited to the database if the method update finishes successfully.
Therefore you can't see the data before the method returns.
Additionally save does not execute the insert/update statement. This will also happen before transaction commit.
If you want to execute the statements before you have to call saveAndFlush(). BUT will also not commit the transaction and from another transaction you will not see this data as well.
This is the usual and expected transactional behavior in a Spring application using transactions.
Propagation REQUIRED
Support a current transaction, create a new one if none exists. Analogous to EJB transaction attribute of the same name.
and keeps the transaction uncommitted and alive at the end of the annotated method.
If you call your update() twice at the very beginning of the request processing, the first starts a transaction and the second reuses it. If you call your update() twice, one successfully, the other unsuccessfully (on unique constraints or something), both of the changes will be rolled back.
Developers usually expect a transaction to start and end like that. But in some cases, a change needs to be committed/rolled back independently from other changes. If it is your case, you can use Propagation.REQUIRES_NEW: See
https://stackoverflow.com/a/24341843/12656244

How to not allow lazy loading from outside the transnational method?

I'm using JPA with Hibernate and Spring. I have an entity (Say Employee) with an attribute (Say of type Position) and this attribute is lazy-loaded.
I believe that when you try to access the position attribute, it will be lazy loaded from the DB and this is done inside the transnational method.
Let's say I didn't access the attribute in that transnational method. So if I tried to access it later, I would get "org.hibernate.LazyInitializationException: could not initialize proxy - no Session" which is normal because the session was closed by that transnational method.
At this point, I need it null (or not initialized) wherever I access it later in different method but this is not the case! The question is how can we make it null after committing and closing the session because it is not accessed while the session is open?
Below is a simple code to illustrate the issue.
// In some Service class
#Override
#Transactional(readOnly = true)
public Employee getEmployeeById(Integer id) throws Exception {
Employee emp = employeeDAO.getEmployeeById(id);
// I didn't access the position attribute here because I don't need it for now
return emp;
}
Later I call the above method (Say from some controller):
Employee emp = employeeService.getEmployeeById(904);
System.out.println(emp.getPosition()); // Here, the LazyInitializationException
//would occur, but I need this to be null or at least to prevent the lazy loading,
//thus, avoiding the exception. How?
I think this might be the answer that you're looking for
Hibernate - Avoiding LazyInitializationException - Detach Object From Proxy and Session
Basically
Use Hibernate to check if that field is initialised with Hibernate. isInitialized(fieldName)in the getter and return null if not initialised.
Inside employeeDAO.getEmployeeByIdmethod, create a new Employee object and set the parameters from the one that return from query, which is more work but prevent you to couple your domain to Hibernate.

javax.persistence.EntityNotFoundException: deleted entity passed to persist

I am using spring + JPA as orm framework. My project layer structure is like web --> Service --> Domain DAO --> genericDAO.
In genericDAO I am injecting EntityManager using #PersistenceContext.
genericDAO.delete(Object o) {
o = entityManager.merge(o);
entityManager.remove(o);
}
genericDAO.saveOrUpdate(Object o) {
entityManager.merge(o);
entityManager.flush();
}
In one method in service layer, I have following operations.
// delete order item if already exists.
Order order = getOrderFromSession();
if (CollectionUtils.isNotEmpty(orderItems)) {
Iterator<OrderItem> iterator = orderItems.iterator();
while (iterator.hasNext()) {
OrderItem orderItem = iterator.next();
iterator.remove();
orderDAO.deleteOrderItem(orderItem); // Which internall calls genericDAO.delete()
}
}
//orderDAO.saveOrder(order) // line Y
//Now create fresh order items submitted by jsp form.
for (ProductVO productVO : productList) {
if (productVO.getQuantity() > 0) {
orderItem = new OrderItem();
Product product = productDAO.getProductByCode(productVO.getCode()); // line X
orderItem.populateOrderItemByProduct(product, productVO.getQuantity(), order);
order.addOrderItem(orderItem);
}
}
Line X retrieve product entity using hql. But when line X is executed, I get below error.
javax.persistence.EntityNotFoundException: deleted entity passed to persist: [core.entity.OrderItem#].
I do not understand if order item is already marked as deleted in entity manager, why it tries to persist.
When I uncomment line Y, which internally flush the entity manager, it works fine. I do not understand why it requires entity manager to be flushed before executing line X
Here is a quote from hibernate documentation
Transactional persistent instances (i.e. objects loaded, saved,
created or queried by the Session) can be manipulated by the
application, and any changes to persistent state will be persisted
when the Session is flushed. There is no need to call a particular method (like update(), which has
a different purpose) to make your modifications persistent. The most
straightforward way to update the state of an object is to load() it
and then manipulate it directly while the Session is open.
Sometimes this programming model is
inefficient, as it requires in the same session both an SQL SELECT to
load an object and an SQL UPDATE to persist its updated state.
Hibernate offers an alternate approach by using detached instances.
But I'll try to explain simplier. Your method getOrderFromSession() is transactional and hibernate objects have session open inside it, but when object order is returned to you, it has been detached from session and hibernate doesn't know what you are doing with it, until you persist him again. So for deleted items hibernate will find out when you save that object, until then object in hibernate have same state as it was in a moment when getOrderFromSession() has return it.
Here you have detailed explanation
UPDATE:
When you delete object in hibernate, object in java becomes transient. It still exist in java and after delete you can use it.
Session.delete() will remove an object's state from the database. Your
application, however, can still hold a reference to a deleted object.
It is best to think of delete() as making a persistent instance,
transient.

How to use a Database Generated Identity Key in Web Api OData

I've managed to create number of readonly Web Api OData services following the tutorials here: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api. I'm therefore employing the ODataConventionModel builder to create the model from a set of entities (incidentally coming from a Telerik ORM). This all seems to work fine and I can happily issue queries, view the metadata and so forth on the service.
I've now tried to turn my attention to the other CRUD operations - firstly Create and have stumbled into a problem! Namely, the Post method fires correctly (CreateEntity) but the entity parameter is null - by doing a check against the ModelState.IsValid, it shows that the problem is a null ID (key) value. This is unsurprising because the database uses a Database Generated Identity for the ID column and therefore the ID would be created when the entity is saved into the database context.
I've therefore tried all sorts of ways of marking the ID column as database generated, but haven't managed to find anything. Strangely, I can't seem to find even one post of someone asking for this - surely I can't be the only one?!
I noted that when looking at the EF modelbuilder (for example here: http://forums.asp.net/t/1848984.aspx/1) there appears to be a means of affecting the model builder with a .HasDatabaseGeneratedOption property, but no similar option exists in the System.Web.Http.OData equivalent.
So the questions therefore are:
Is there a means of altering the model builder (or something else) so that the controller will accept the object and deserialize the entity even with a null key value?
If so, how can I do this?
If not, any suggestions as to other options?
I realise that I could potentially just populate the object with an (in this case) integer value from the client request, but this seems a) semantically wrong and b) won't necessarilly always be possible as a result of the client toolkit that might be used.
All help gratefully received!
Many thanks,
J.
You need to create a viewmodel for insert which does not contain the ID parameter. Use Automapper to map the properties of the incoming insert-model to your data entities.
The problem that you're having is that ID is a required attribute in your data model because it is your PK, except during insert, where it shouldn't be specified.
In my case, my database-generated key is a Guid.
As a work-around, in my TypeScript client code, I submit (via http POST) the object with an empty Guid like this: Note: ErrorId is the key column.
let elmahEntry: ELMAH_Error = {
Application: 'PTUnconvCost',
Host: this.serviceConfig.url,
Message: message,
User: that.userService.currentUserEmail,
AllXml: `<info><![CDATA[\r\n\r\n${JSON.stringify(info || {})}\r\n\r\n]]></info>`,
Sequence: 1,
Source: source,
StatusCode: 0,
TimeUtc: new Date(Date.now()),
Type: '',
ErrorId: '00000000-0000-0000-0000-000000000000'
};
Then, in my WebApi OData controller, I check to see if the key is the empty guid, and if so, I replace it with a new Guid, like this:
// POST: odata/ELMAH_Error
public IHttpActionResult Post(ELMAH_Error eLMAH_Error)
{
if (eLMAH_Error.ErrorId == Guid.Empty)
{
eLMAH_Error.ErrorId = Guid.NewGuid();
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.ELMAH_Error.Add(eLMAH_Error);
try
{
db.SaveChanges();
}
catch (DbUpdateException)
{
if (ELMAH_ErrorExists(eLMAH_Error.ErrorId))
{
return Conflict();
}
else
{
throw;
}
}
return Created(eLMAH_Error);
}

Resources