I have a Play 2.1.3 Java app using Ebean. I am getting the OptimisticLockException below.
[OptimisticLockException: Data has changed. updated [0] rows sql[update person
set name=? where id=? and email=? and name=? and password is null and created=?
and deleted is null] bind[null]]
I understand that it is trying to tell me the record has changed between when I read it and when I tried to write it. But the only change is happening in this method.
public void updateFromForm(Map<String, String[]> form) throws Exception {
this.name = form.get("name")[0];
String password = form.get("password")[0];
if (password != null && password.length() != 0) {
String hash = Password.getSaltedHash(password);
this.password = hash;
}
this.update();
}
Am I doing this wrong? I saw similar logic in zentasks. Also, should I be able to see the the values for the bind variables?
UPDATE: I am calling updateFromForm() from inside a controller:
#RequiresAuthentication(clientName = "FormClient")
public static Result updateProfile() throws Exception {
final CommonProfile profile = getUserProfile();
String email = getEmail(profile);
Person p = Person.find.where().eq("email", email).findList().get(0);
Map<String, String[]> form = request().body().asFormUrlEncoded();
if (p == null) {
Person.createFromForm(form);
} else {
p.updateFromForm(form);
}
return ok("HI");
}
I have an alternative approach to this, where I add the annotation
#EntityConcurrencyMode(ConcurrencyMode.NONE)
to the Entity class.
This disables the optimistic locking concurrent modification check meaning the SQL becomes
update person set name=? where id=?
This is even more optimistic since it simply overwrites any intermediate changes.
Little bit late, but for your case #Version annotation should be the solution. We're using it mostly with java.util.Date, so it can be also used also for determining the date of last record update, in Play model that's just:
#Version
public java.util.Date version;
In such case update statement will be done with id and version fields only - useful especially when using with large models:
update person set name='Bob'
where id=1 and version='2014-03-03 22:07:35';
Note: you don't need/should update this field manually at each save, Ebean does it itself. version value changes ONLY when there was updated data (so using obj.update() where nothing changes doesn't update version field)
Mystery solved.
First- this public service announcement. "OptimisticLockException" is a big bucket. If you are trying to track one of these down be open to the idea that it could really be anything.
I figured out my problem by dumping SQL to the log and finding this:
update person set name='Bob'
where id=1 and email='jj#test.com'
and name='Robert' and password is null
and created=2013-12-01 and deleted is null
So I guess what happens when you do an update is that it builds a WHERE clause with all the known entities and their values as they were originally ready.
That means, if any other part of your code or another process changes something behind your back, this query will fail. I wrongly assumed that the problem was that somehow .setName('Bob') had changed the name in the DB or some object cache.
Really what was happening is that the WHERE clause includes a date while my database includes an entire timestamp with date, time, and timezone.
For now, I fixed it by just commenting out the timestamp in the model until I can figure out if/how Ebean can handle this data type.
I had the same problem,
after hours of search i found the reason..
It was of inconsistency of the parameters type in the data base (in my case string) and the object i created and tried to save -java.util.Date.
after changing the database to hold datetime object the problem was solved
Related
When I run our Java program, I get a weird error that none of my team mates get, because a JPA-Query gives a different result on my machine, even if I and a fellow developer check out the exact same git commit, and the same DB content, build it and then debug it together.
What this JPA-Query does is, it basically checks if a certain entity already exists in the database. It explicitly excludes entries that have the same Id, so the entity doesn't get compared to itself. It appears that this doesn't work on my machine. Here's a dummy version of the code:
import com.mysema.query.BooleanBuilder;
import com.mysema.query.jpa.impl.JPAQuery;
public class ExampleClass {
#Autowired
protected ClassThatExtendsJpaRepository customerDAO;
public void checkUniqueness(Customer inputCustomer) throws NotUniqueException {
//Or-condition
BooleanBuilder condition = new BooleanBuilder();
Condition.or(QCustomer.customer.registerNr.eq(inputCustomer.getRegisterNr());
Condition.or(QCustomer.customer.registerNr.eq(inputCustomer.getRegisterNr());
JPAQuery query = new JPAQuery().from(Qcustomer.customer).where(condition);
//This should exclude customers of the same id, but it has no effect
query.where(Qcustomer.customer.id.ne(inputCustomer.getId());
Customer existingCustomer = customerDAO.findOne(query);
//Result: finds customer with same id! Should find nothing!
if(existingCustomer != null) {
throw new NotUniqueException();
}
}
}
As you can see, aside from the check that the existing entity doesn't have the same ID as the one we're trying to compare it to, there are also other conditions connected by an OR. For simplicity, I've used the same condition for both in this example. The way the code is written now, it will find the entity with the same ID in the database and throw the NotUniqueException even though it shouldn't. But remove one of the Condition.or, and it works. This leads me to suspect that the query is putting the brackets wrong.
According to the debugger, the query is
Select customer
from Customer customer
where (customer.registerNr = ?1 or customer.registerNr = ?1) and customer.id <> ?2
(And for the record: when I run this directly on my DB it works correctly, finding nothing.)
But I suspect it's actually running it as if the brackets were different:
Select customer
from Customer customer
where customer.registerNr = ?1 or (customer.registerNr = ?1 and customer.id <> ?2)
Either way, this still doesn't explain why this occurs, and why it's only on my machine.
We don't want to change the code, since this bug is only on my machine and doesn't stop me from working. So hopefully if I find the cause I can fix it some other way.
Versions:
Spring-data-jpa: 1.9.6.RELEASE
com.mysema.querydsl: 3.7.4
Hibernate: 4.2.21.Final
OJDBC: 19.17.0.0
Database: Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production
Java: 8
The solution was: I downgraded Oracle to version 18, like all the other developers.
Yes, that's right, apparently the queries can give different results in Oracle 21.
I have an issue related to SQLInput not reading data with readString()
Same code works on two different Oracle databases.
But on this one I have following issue:
This code:
#Override
public void readSQL(SQLInput stream, String typeName) throws SQLException {
userId = stream.readBigDecimal().longValue();
name = stream.readString();
modified = stream.readTimestamp();
}
returns userId and modified, but for name it returns "???" even though it has data in database for that name.
I have no idea what the issue is.
The data from query is return into oracle type similar to:
create or replace TYPE MY_ROW AS OBJECT (
USER_ID NUMBER,
name VARCHAR2(50),
MODIFIED TIMESTAMP
)
which is used in table type of that row type:
create or replace TYPE MY_TABLE as TABLE of MY_ROW
So I again, I have no idea why this doesn't work for that specific oracle database, when it works with two other ones.
It's like varchar2 fields with getString are not getting returned and I'm just getting ???
Anyone has any idea on what to do?
EDIT:
Even after including orai18n.jar in the classpath, the issue is still there
I finally managed to resolved it by combining orai18n.jar and ojdbc7.jar into same jar file.
Not sure why adding orai18n.jar separately in the class path didn't do the trick.
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)
Using the GeoTools WFS-T plugin, I have created a new row, and after a commit, I have a FeatureId whos .getId() returns an ugly string that looks something like this:
newmy_database:my_table.9223372036854775807
Aside from the fact that the word "new" at the beginning of "my_database" is a surprise, the number in no way reflects the primary key of the new row (which in this case is "23"). Fair enough, I thought this may be some internal numbering system. However, now I want a foreign key in another table to get the primary key of the new row in this one, and I'm not sure how to get the value from this FID. Some places suggest that you can use an FID in a query like this:
Filter filter = filterFactory.id(Collections.singleton(fid));
Query query = new Query(tableName, filter);
SimpleFeatureCollection features = simpleFeatureSource.getFeatures(query);
But this fails at parsing the FID, at the underscore of all places! That underscore was there when the row was created (I had to pass "my_database:my_table" as the table to add the row to).
I'm sure that either there is something wrong with the id, or I'm using it incorrectly somehow. Can anyone shed any light?
It appears as if a couple things are going wrong - and perhaps a bug report is needed.
The FeatureId with "new" at the beginning is a temporary id; that should be replaced with the real result once commit has been called.
There are a number of way to be aware of this:
1) You can listen for a BatchFeatureEvent; this offers the information on "temp id" -> "wfs id"
2) Internally this information is parsed from the Transaction Result returned from your WFS. The result is saved in the WFSTransactionState for you to access. This was before BatchFeatureEvent was invented.
Transaction transaction = new transaction("insert");
try {
SimpleFeatureStore featureStore =
(SimpleFeatureStore) wfs.getFeatureSource( typeName );
featureStore.setTransaction( transaction );
featureStore.addFeatures( DataUtilities.collection( feature ) );
transaction.commit();
// get the final feature id
WFSTransactionState wfsts = (WFSTransactionState) transaction.getState(wfs);
// In this example there is only one fid. Get it.
String result = wfsts.getFids( typeName )[0];
}
finally {
transaction.close();
}
I have updated the documentation with the above example:
http://docs.geotools.org/latest/userguide/library/data/wfs.html
I am using Hibernate and getting
Exception in thread "main" org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [#271]
What is pretty weird about this error is, that the object with the given id exists in the database. I inserted the problematic record in another run of the application. If I access it in the same run (i.e. same hibernate session) there seem to be no problems retrieving the data.
Just because it could be a fault of the mapping:
public class ProblemClass implements Persistent {
#ManyToOne(optional = false)
private MyDbObject myDbObject;
}
public class MyDbObject implements Persistent {
#OneToMany(mappedBy = "myDbObject")
private List<ProblemClass> problemClasses;
#ManyToOne(optional = false)
private ThirdClass thirdClass;
}
I have absolutely no clue even where to look at. Any hints highly appreciated!
Just to clarify:
The data was inserted in another RUN of the application. It is definitely in the database, as I can see it via an SQL-Query after the application terminated. And after THAT, i.e. when starting the application again, I get the error in the FIRST query of the database -- no deletion, no rollback involved.
Addition:
Because it was asked, here is the code to fetch the data:
public List<ProblemClass> getProblemClasses() {
Query query = session.createQuery("from ProblemClass");
return query.list();
}
And just to make it complete, here is the generic code to insert it (before fetching in another RUN of the application):
public void save(Persistent persistent) {
session.saveOrUpdate(persistent);
}
Eureka, I found it!
The problem was the following:
The data in the table ThirdClass was not persisted correctly. Since this data was referenced from MyDbObject via
optional = false
Hibernate made an inner join, thus returning an empty result for the join. Because the data was there if executed in one session (in the cache I guess), that made no problems.
MySQL does not enforce foreign key integrity, thus not complaining upon insertion of corrupt data.
Solution: optional = true or correct insertion of the data.
Possible reasons:
The row was inserted by the first session, but transaction was not committed when second session tried to access it.
First session is roll-backed due to some reason.
Sounds like your transaction inserting is rollbacked
Main reason behind this issue is data mismatch, for example i have entity mapping class called "X" and it has column "column1" and it has reference to the table "Y" column "column1" as below
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "column1", referencedColumnName = "column1")
public Y getColumn1() {
return Y;
}
In this if X table column1 has value but Y table column1 is not having the value. Here link will be failed.
This is the reason we will get Hibernate objectNotFound exception
This issue can also be resolved by creating proper data model like creating proper indexing and constraints (primary key/foreign key) ..
This might be your case, kindly check my answer on another post.
https://stackoverflow.com/a/40513787/6234057
I had the same Hibernate exception.
After debugging for sometime, i realized that the issue is caused by the Orphan child records.
As many are complaining, when they search the record it exists.
What i realized is that the issue is not because of the existence of the record but hibernate not finding it in the table, rather it is due to the Orphan child records.
The records which have reference to the non-existing parents!
What i did is, find the Foreign Key references corresponding to the Table linked to the Bean.
To find foreign key references in SQL developer
1.Save the below XML code into a file (fk_reference.xml)
<items>
<item type="editor" node="TableNode" vertical="true">
<title><![CDATA[FK References]]></title>
<query>
<sql>
<![CDATA[select a.owner,
a.table_name,
a.constraint_name,
a.status
from all_constraints a
where a.constraint_type = 'R'
and exists(
select 1
from all_constraints
where constraint_name=a.r_constraint_name
and constraint_type in ('P', 'U')
and table_name = :OBJECT_NAME
and owner = :OBJECT_OWNER)
order by table_name, constraint_name]]>
</sql>
</query>
</item>
2.Add the USER DEFINED extension to SQL Developer
Tools > Preferences
Database > User Defined Extensions
Click "Add Row" button
In Type choose "EDITOR", Location - where you saved the xml file above
Click "Ok" then restart SQL Developer
3.Navigate to any table and you will be able to see an additional tab next to SQL, labelled FK References, displaying FK information.
4.Reference
http://www.oracle.com/technetwork/issue-archive/2007/07-jul/o47sql-086233.html
How can I find which tables reference a given table in Oracle SQL Developer?
To find the Orphan records in all referred tables
select * from CHILD_TABLE
where FOREIGNKEY not in (select PRIMARYKEY from PARENT_TABLE);
Delete these Orphan records, Commit the changes and restart the server if required.
This solved my exception. You may try the same.
Please update your hibernate configuration file as given below:
property start tag name="hbm2ddl.auto" create/update property close tag
I have found that in Oracle this problem can also be caused by a permissions issue. The ProblemClass instance referred to by the MyDbObject instance may exist but have permissions that do not allow the current user to see it, even though the user can see the current MyDbObject.