Primary key violation issue using hibernate? - java

I am using Hibernate. A Service is exposed as a webservice. This service wil be called by 2 applications. The service method saves record into database.
ServiceClass.java:
------------------
//Here Transaction will start
public void saveRecord(SampleEntity entity){
someDAO.saveData(entity);
}
SomeDao.java
-----------------
public void saveData(SampleEntity entity){
//record is saved using saveOrUpdate method
}
If saveRecord method is called by two applications at a time with same ID, PK violation exception is thrown.
Both the applications are sending the records with same ID.
As we are using saveOrUpdate it should update the record if it already exists.

If this is in a transaction, the row will be locked (and the index not updated) until the transaction commits. So it may look like two inserts with the same PK if the update happens before the commit.

Related

Spring JdbcTemplate rollback using annotations

I am new to Java and Spring. I am learning spring jdbc connectivity using JdbcTemplate. I wrote the below code.
Controller
service.importInsuranceEstimates(insuranceEstimates);
Service class
public void importInsuranceEstimates(List<String[]> insuranceEstimates) {
for (String[] insuranceEstimate: insuranceEstimates) {
repository.insertInsuranceEstimate(insuranceEstimate);
}
}
Repository class
public void insertInsuranceEstimate(String[] insuranceEstimate) {
jdbcTemplate.update("insert into some_table values (?, ?, ?)", insuranceEstimate[0], insuranceEstimate[1], insuranceEstimate[2]);
}
Assume that after inserting few records, the next insert statement failed. In this case, I would like the previously inserted records to be rolled back.
So I decorated the repository method with #Transactional(propagation = Propagation.REQUIRED). But still I don't see the previous records being rolled back if the insert failed.
Then I understood that the rollback is not done because each insert is done in its own transaction and committed before the repository is returned.
So then I decorated the service method also with the same annotation #Transactional(propagation = Propagation.REQUIRED). But no success. The records are still not being rolled back.
Then, I understood that I have to insert all the records under the same transaction. So I changed my repository signature to
public void importInsuranceEstimates(List<String[]> insuranceEstimates)
then service class
repository.importInsuranceEstimates(insuranceEstimates);
In the repository class I am using batchUpdate instead of using the regular update.
What I understood is
1. queries related to a single transaction must be run/executed under a single transaction.
2. annotation based rollback is not possible using JdbcTemplate. We have to get the connection and play with setAutoCommit(boolean) method.
Are my observations right?
Also, in some cases one would like to make multiple insert/update/delete db calls for different tables from service layer. How to make multiple db calls from service layer under the same transaction. Is it even possible?
For example I want to write a code to transfer money from an account to another. So I have to make two db calls, one to debit the send and one to credit the receiver. In this case I would write something like below
Service class
repository.debitSender(id, amount);
repository.creditReceiver(id, amount);
Since I cannot run these two method calls under the same transaction, I have to modify my service class to
repository.transferMoney(senderId, receiverId, amount)
and do the two updates under the same transaction in the repository like below
public void transferMoney(String senderId, String receiverId, double amount) {
jdbcTemplate.getConnection().setAutoCommit(false);
// update query to debit the sender
// update query to credit the receiver
jdbcTemplate.getConnection().setAutoCommit(true);
}
What if I do not want to use transferMoney method and instead split the method into two - debitSender and creditReceiver and call these two methods from the service class under the same transaction with JdbcTemplate?

How many hits to database does entityManager.find produce? [duplicate]

We are using Toplink implementation of JPA + Spring + EJB. In one of our EJBs we have something like this:
public void updateUser(long userId, String newName){
User u = em.get(User.class, userId);
u.setName(newName);
// no persist is invoked here
}
So, basically this updateUser() method is supposed to update the name of a user with the given userId.
But the author of this method forgot to invoke em.persist(u).
And the strangest thing is that it works fine. How can it be? I was 100% sure that
without invoking em.persist() or em.merge() there is no way that changes could have been saved into database. Could they? Is there any scenario when this could happen?
You're working with a managed entity. If the entity does not become detached because its entity manager is closed, all changes done to the entity are reflected to the database when the session is flushed/closed and the transaction commited.
From the Java EE tutorial:
The state of persistent entities is
synchronized to the database when the
transaction with which the entity is
associated commits.
Edit for clarity and explanation: So there are three distinct modes that an entity could be in during its lifecycle:
Unsaved: The entity has been instantiated, but persist() has not been called yet.
Managed: The entity has been persisted using persist(), or loaded from the database, and is associated with an entity manager session. All changes to the entity are reflected to the database when the entity manager session is flushed.
Detached: The entity's entity manager session was closed. Changes to the entity will not be reflected to the database automatically, but can be merged explicitly using the merge() command.

Hibernate : Lock a row while update, so user's don't retrieve a counter from it

I am working on a Spring-MVC project in which I am using Hibernate as the ORM, PostgreSQL as our DB and in one of our Objects(GroupCanvas), we have a number which is incremented everytime when user takes some action, and then the GroupCanvas object is updated in DB, and it should be unique.
THe problem we have currently is, if multiple users take action in front-end, some of them are getting duplicate numbers. We are working on fixing this now, so later we can implement a sequence and are assured that the numbers are unique.
How can I ensure that when I am updating the row, other users are waiting till the row is updated. I tried LockMode.Pessimistic_write, and a few others, none helped.
Code :
#Override
public void incrementNoteCounterForGroupCanvas(int canvasId) {
Session session = this.sessionFactory.getCurrentSession();
session.flush();
Query query = session.createQuery("update GroupCanvas as gc set gc.noteCount=gc.noteCount+1 where gc.mcanvasid=:canvasId");
query.setParameter("canvasId",canvasId);
query.executeUpdate();
session.flush();
}
#Override
public GroupCanvas getCanvasById(int mcanvasid) {
Session session = this.sessionFactory.getCurrentSession();
session.flush();
return (GroupCanvas) session.get(GroupCanvas.class, mcanvasid,LockMode.PESSIMISTIC_WRITE);
}
Both methods are in DAO, which has #Transactional annotation, and annotation present in service layer as well.
Thank you.
Looking at the method you have posted the usage if the 'LOCKING' technique is not quite correct. In order for a lock to end up with the result you are looking for the sequence of actions should be similar to the ones below (in the nutshell it is similar to the Double-Checked Locking but implemented using DB locks - https://en.wikipedia.org/wiki/Double-checked_locking).
Start the transaction (eg #Transactional annotation on your service method)
Retrieve entity from database with the PESSIMISTIC_WRITE lock mode (make sure to indicate hibernate that fresh copy should be read instead of the one stored in session cache)
If required check the current value of the target field if it meets your invariants
Perform the change/update on the field (eg, increment the value of a field )
Save the entity (and make sure to flush the value to the DB if you do not want to wait for the auto-flush)
Commit the transaction (done automatically when using #Transactional)
The essential difference of this sequence when compared with the posted method is that the update of the property value is performed while your transaction holds a lock on the target entity/db row, hence preventing other transactions from reading it while your update is in progress.
Hope this helps .
UPDATE:
I believe something like the code snippet bellow should work as expected :
#Transactional
#Override
public void incrementNoteCounterForGroupCanvas(int canvasId) {
final Session session = this.sessionFactory.getCurrentSession();
final GroupCanvas groupCanvas = session.get(GroupCanvas.class, canvasId,LockMode.PESSIMISTIC_WRITE);
session.refresh(groupCanvas);
groupCanvas.setNoteCount(groupCanvas.getNoteCount()+1);
session.saveOrUpdate(groupCanvas);
session.flush();
}

Record not inserted after interceptor onSave()

my app consists of spring #transaction with hibernate. I am trying to use hibernate interceptor. i registered the interceptor as
getHibernateTemplate().getSessionFactory().withOptions().interceptor(interceptor).openSession();
And the actual interceptor extends EmptyInterceptor and i am overriding onSave() method.
the problem is that the interceptor onSave() method is called but after that , the actual entity does not get inserted in the database.
public boolean onSave(Object entity, Serializable id, Object[] state,
String[] propertyNames, Type[] types) {
System.out.println("inside interceptor - on save");
{
// my changes here . setting a field of the entity.
return true;
}
return false;
}
According to Interceptor.onSave():
Called before an object is saved. The interceptor may modify the state, which will be used for the SQL INSERT and propagated to the persistent object.
So the changes are commited to the persistent object and the INSERT statement that Hibernate will issue to the database.
You might want to make sure you are searching for the record once the #Transactional method has finished, if you're querying the database with a different session than the one that opened the transaction. That is when Spring will commit the transaction to the database.
You could also try using Interceptor.afterTransactionCompletion() to check if the record is effectvely inserted.
Called after a transaction is committed or rolled back.

How do you update a foreign key value directly via Hibernate?

I have a couple of objects that are mapped to tables in a database using Hibernate, BatchTransaction and Transaction. BatchTransaction's table (batch_transactions) has a foreign key reference to transactions, named transaction_id.
In the past I have used a batch runner that used internal calls to run the batch transactions and complete the reference from BatchTransaction to Transaction once the transaction is complete. After a Transaction has been inserted, I just call batchTransaction.setTransaction(txn), so I have a #ManyToOne mapping from BatchTransaction to Transaction.
I am changing the batch runner so that it executes its transactions through a Web service. The ID of the newly inserted Transaction will be returned by the service and I'll want to update transaction_id in BatchTransaction directly (rather than using the setter for the Transaction field on BatchTransaction, which would require me to load the newly inserted item unnecessarily).
It seems like the most logical way to do it is to use SQL rather than Hibernate, but I was wondering if there's a more elegant approach. Any ideas?
Here's the basic mapping.
BatchQuery.java
#Entity
#Table(name = "batch_queries")
public class BatchQuery
{
#ManyToOne
#JoinColumn(name = "query_id")
public Query getQuery()
{
return mQuery;
}
}
Query.java
#Entity
#Table(name = "queries")
public class Query
{
}
The idea is to update the query_id column in batch_queries without setting the "query" property on a BatchQuery object.
Using a direct SQL update, or an HQL update, is certainly feasible.
Not seeing the full problem, it looks to me like you might be making a modification to your domain that's worth documenting in your domain. You may be moving to having a BatchTransaction that has as a member just the TransactionId and not the full transaction.
If in other activities, the BatchTransaction will still be needing to hydrate that Transaction, I'd consider adding a separate mapping for the TransactionId, and having that be the managing mapping (make the Transaction association update and insert false).
If BatchTransaction will no longer be concerned with the full Transaction, just remove that association after adding a the TransactionId field.
As you have writeen, we can use SQL to achieve solution for above problem. But i will suggest not to update the primary keys via SQL.
Now, as you are changing the key, which means you are creating alltogether a new object, for this, you can first delete the existing object, with the previous key, and then try to insert a new object with the updated key(in your case transaction_id)

Categories

Resources