I'm using hibernate jpa. I'm trying to execute an update query and want to tell if the update actually happen or not based on the return value because I may have multiple processes doing the same updates and I only want to continue with the process that successfully update the record. (Relying on mysql return 1 iff the update happen successfully, 0 if nothing changed.)
int update = entityManager.createQuery("update Employee set name = \'xxxx\' where id=1").executeUpdate();
My question is that with default flush mode (AUTO), will this query being executed on the db? (if not, does the return value still make sense?) What about flush mode = COMMIT? Do I need to do entityManager.flush()? (I feel like the query execution will bypass the entityManager so flush doesn't do anything?)
What if I use JPA repository instead of the native query?
#Modifying
#Query("UPDATE Employee SET name = :name WHERE id = :id)
int update(long id, String name)
Will the return value reflects the rows changed in DB or just the persistent context?
Related
I'm currently working in a legacy application that uses an Stored Procedure in a Sybase DB to generate some IDs incrementally. The procedure is defined as:
CREATE PROC getId
(#val int = -1 output)
AS
BEGIN
UPDATE ID_TABLE SET LAST_VALUE = LAST_VALUE + 1
SELECT #val = LAST_VALUE FROM ID_TABLE
RETURN #val
END
This procedure is being called from a Java application using Spring's TransactionTemplate to handle the transaction in a declarative manner.
public Integer getId() {
TransactionTemplate txTemplate = new TransactionTemplate(txManager); // txManager is an autowired instance of PlatformTransactionManager.
txTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
txTemplate.setTimeout(-1);
return (Integer) txTemplate.execute((TransactionCallback) status -> idDao.generateId());
}
Internally, idDao uses a JdbcTemplate to call the Stored Procedure using a CallableStatementCreatorFactory. Nothing too out of the ordinary there.
The Stored Procedure is called ~10k times/day. From time to time, we see some ID collisions. My understanding was that the isolation level being set to SERIALIZABLE should prevent this from happening, and I can't seem to reproduce this even calling getId simultaneously with several threads. Does anybody have a hint on what might be happening here?
I can't speak to the java/spring coding but on a purely SQL basis it looks and sounds like the update/select is not being performed within a transaction.
Without a transaction wrapper (eg, explicit begin/commit tran, autocommit=false, set chained on) the update/select is a prime target for a race condition and duplicate key errors.
NOTE: the isolation level has no effect if the update/select is performed outside of a transaction wrapper
Tracking down a 'missing transaction wrapper' issue is a good idea as there could be other pieces of SQL code suffering from poor/missing transaction management. Having said that ...
For this particular case one easy fix would be to add a transaction wrapper in the proc, eg:
CREATE PROC getId
(#val int = -1 output)
AS
BEGIN
begin tran
UPDATE ID_TABLE SET LAST_VALUE = LAST_VALUE + 1
SELECT #val = LAST_VALUE FROM ID_TABLE
commit tran
RETURN #val
END
Even better would be a rewrite of the update that eliminates the need for the select, eg:
CREATE PROC getId
(#val int = -1 output)
AS
BEGIN
UPDATE ID_TABLE SET #val=LAST_VALUE+1, LAST_VALUE = LAST_VALUE + 1
RETURN #val
END
NOTE: as a standalone operation this eliminates the need for a transaction wrapper and should eliminate the generation of duplicate keys (assuming this proc is the root cause of the duplicate key issue)
Assuming this is Sybase ASE I'd want to look at making sure the table is configured to use datarows locking; with allpages locking, and to a limited extent datapages locking, there's a chance of a race condition on index updates (which in turn could lead to a deadlock scenario).
jpql delete query is active for last 4 hours. When I tried to execute the same query directly on the database console it took around 30seconds to execute.The total data to delete is maximum 100000. I have index on id. I am unable to understand. Any suggestions would be appreciated.
Thanks
#Modifying
#Query("DELETE FROM IoEntity WHERE Id = :id")
void deleteAllById(#Param("id") UUID id);
Whenever you run a DML in your DB using SQL clients like Toad, SQL Developer etc, make sure you commit it unless auto commit is turned on in the client.
If you are doing the delete via JPA, commit will be taken care by Spring when you define #Transactional in your service method.
I need to return one item id from table which has not been processed yet (At given time there can be multiple available but I need to return unique id per request).
I am fetching first record from DB using JPA native query
#Lock(LockModeType.PESSIMISTIC_READ)
#QueryHints({#QueryHint(name = "javax.persistence.lock.timeout", value ="5000")})
#Query(value = "SELECT id FROM item WHERE name->> 'name'=:name AND city->> 'city'=:city LIMIT 1",nativeQuery = true)
public String find(#Param("name") String name, #Param("city") String city);
Post that I am changing the status to processed using update query
Returning the ID
The above scenario throws an exception stating "Invalid use of Lock" (As it doesn't support native query and only supports crud operations).
Need help to use PESSIMISTIC_READ using native query for SELECT for UPDATE and SKIP locked row during race condition.
By Adding below line in query solved the issue.
FOR UPDATE SKIP LOCKED
#Query(value = "SELECT id FROM item WHERE name->> 'name'=:name AND city->> 'city'=:city LIMIT 1 FOR UPDATE SKIP LOCKED",nativeQuery = true)
public String find(#Param("name") String name, #Param("city") String city);
In my project I use hibernate hbm and spring,i run an sql query to update a single column,
Query sql = getSession().createSQLQuery("update HISTORIQUE_DETAIL_APPELS set token_otp = '"+historiqueDetailAppelsVO.getCodeOtp()+"' where id = '"+historiqueDetailAppelsVO.getId()+"'");
try {
sql.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
I found that another query is executed and update the table in data base,
Hibernate: update HISTORIQUE_DETAIL_APPELS set token_otp = '14d3fc' where id = '150017'
Hibernate: update HISTORIQUE_DETAIL_APPELS set cod_cent=?, adresse_ip=?, id_conseiller=?, type_piece=?, num_piece_ident=?, msisdn=?, mois1_detail=?, mois2_detail=?, mois3_detail=?, date_demande=?, no_ticket_caisse=?, date_ticket=?, cod_user=?, dat_maj=?, flag_imp_data=?, date_imp_data=?, token_otp=?, send_mail=?, client_mail=?, date_debut=?, date_fin=? where id=?
where does the origin of the second update ?
I faced the same issue before, and after some research i found :
When user update any record by using direct query based operation, it is directly updated to database.
But, if the same record(previous copy) is already present in current session(that is previously read by user in current session) then, there is a difference occurs between database record(that is updated by query based operation) and current session record, due to this, hibernate again runs update query to update session record during either flushing of session or on transaction completion.
To avoid the second execution executed by hibernate either during flushing of session or on transaction completion.
I wish it will help you.
Thanks
I solve this probleme by adding : dynamic-update="true" in hbm.xml file
I found the solution here:
"The dynamic-update attribute tells Hibernate whether to include
unmodified properties in the SQL UPDATE statement."
So in my database, I have 3 rows, two rows have defaultFlag as 0 and one is set to 1, now in my processing am updating defaultProperty of one object to 1 from 0 but am not saving this object yet.
Before saving I need to query database and find if any row has defaultFlag set or not, there would be only 1 default set.
So before doing update am running query to find if default is set and i get 2 values out, note here if i go and check in db then there is only 1 row with default set but query gives me two result because this.object default property has changed from 0 to 1 but note that this object is not yet saved in database.
I am really confused here as to why hibernate query is returning 2 when there is one row with default set in database and other object whose default property has changed but it is not saved.
Any thoughts would be helpful. I can provide query if need be.
Update
Following suggestions, I added session.clear() to before running the query.
session.clear();
String sql = "SELECT * FROM BANKACCOUNTS WHERE PARTYID = :partyId AND CURRENCYID = :currencySymbol AND ISDEFAULTBANKACCOUNT= :defaultbankAccount";
SQLQuery q = session.createSQLQuery(sql);
q.addEntity(BankAccount.class);
q.setParameter("partyId", partyId);
q.setParameter("currencySymbol", currencySymbol);
q.setParameter("defaultbankAccount", 1);
return q.uniqueResult();
and it returns 1 row in result as expected but now am getting
nested exception is org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session exception
Either query which row has the "default flag" set before you start changing it, or query for a list of rows with default flag set & clear all except the one you're trying to set.
Very easy, stop mucking about with your "brittle" current approach which will break in the face of concurrency or if data is ever in an inconsistent state. Use a reliable approach instead, which will always set the data to a valid state.
protected void makeAccountDefault (BankAccount acc) {
// find & clear any existing 'Default Accounts', other than specified.
//
String sql = "SELECT * FROM BANKACCOUNTS WHERE PARTYID = :partyId AND CURRENCYID = :currencySymbol AND ISDEFAULTBANKACCOUNT= :defaultbankAccount";
SQLQuery q = session.createSQLQuery(sql);
q.addEntity(BankAccount.class);
q.setParameter("partyId", partyId);
q.setParameter("currencySymbol", currencySymbol);
q.setParameter("defaultbankAccount", 1);
//
List<BackAccount> existingDefaults = q.list();
for (BankAccount existing : existingDefaults) {
if (! existing.equals( acc))
existing.setDefaultBankAccount( false);
}
// set the specified Account as Default.
acc.setDefaultBankAccount( true);
// done.
}
This is how you write proper code, do it simple & reliable. Never make or depend on weak assumptions about the reliability of data or internal state, always read & process "beforehand state" before you do the operation, just implement your code clean & right and it will serve you well.
I think that your second query won't be executed at all because the entity is already in the first level cache.
As your transaction is not yet commited, you don't see the changes in the underlying database.
(this is only a guess)
That's only a guess because you're not giving many details, but I suppose that you perform your myObject.setMyDefaultProperty(1) while your session is open.
In this case, be careful that you don't need to actually perform a session.update(myObject) to save the change. It is the nominal case when database update is transparently done by hibernate.
So, in fact, I think that your change is saved... (but not commited, of course, thus not seen when you check in db)
To verify this, you should enable the hibernate.show_sql option. You will see if an Update statement is triggered (I advise to always enable this option in development phase anyway)