Grails Grom + mongoDb get during save OptimisticLockingException - mongodb

I try in Grails service save an object to mongodb:
Cover saveCover = new Cover()
saveCover.id = url
saveCover.url = url
saveCover.name = name
saveCover.sku = sku
saveCover.price = price
saveCover.save()
Cover domain looks like this:
class Cover {
String id
String name
String url
String sku
String price
}
So I want to have custom id based on url, but during save process I get error:
Could not commit Datastore transaction; nested exception is
org.grails.datastore.mapping.core.OptimisticLockingException: The
instance was updated by another user while you were editing
But if I didn`t use setters and just pass all values in constructor, the exception is gone. Why?

As reported in the documentation here:
Note that if you manually assign an identifier, then you will need to use the insert method instead of the save method, otherwise GORM can't work out whether you are trying to achieve an insert or an update
so you need to use insert method instead of save when id generator is assigned
cover.insert(failOnError: true)
if you do not define the mapping like this:
static mapping = {
id generator: 'assigned'
}
and will use insert method you'll get an auto-generated objectId:
"_id" : "5496e904e4b03b155725ebdb"

This exception occurs when you assign an id to a new model and try to save it because GORM thinks it should be doing an update.
Why this exception occurs
When I ran into this issue I was using 1.3.0 of the grails-mongo plugin. That uses 1.1.9 of the grails datastore core code. I noticed that the exception gets generated on line 847(ish) of NativeEntryEntityPersister. This code updates an existing domain object in the db.
Above that on line 790 is where isUpdate is created which is used to see if it's an update or not. isInsert is false as it is only true when an insert is forced and readObjectIdentifier will return the id that has been assigned to the object so isUpdate will end up evaluating as true.
Fixing the exception
Thanks to && !isInsert on line 791 if you force an insert the insert code will get called and sure enough the exception will go away. However when I did this the assigned id wasn't saved and instead a generated object id was used. I saw that the fix for this was on line 803 where it checks to see if the generator is set to "assigned".
To fix that you can add the following mapping.
class Cover {
String id
String name
String url
String sku
String price
static mapping = {
id generator: 'assigned'
}
}
A side effect of this is that you will always need to assign an id for new Cover domain objects.

Related

Entity Framework error during update: field is required

I'm using EF 6.2.0, Code First from Database, in a .net 4.5.2 environment.
I have this DbSet:
<Table("Firebird.PLANT4")>
Partial Public Class PLANT4
<Key>
<DatabaseGenerated(DatabaseGeneratedOption.None)>
Public Property ID_PLANT4 As Integer
Public Property STATUS As Integer
<Required>
<StringLength(20)>
Public Property DESCRIPTION1 As String
Public Property COUNTER As Long
Public Property RESET_COUNTER As Integer
End Class
When I execute this code:
Using dbContext As New DbModel
dbContext.Database.Connection.ConnectionString = GetFbConnectionString()
dbContext.Database.Connection.Open()
Dim plant As PLANT4 = dbContext.PLANT4.Find(1)
plant.RESET_COUNTER = 1
dbContext.SaveChanges()
End Using
I get the error: "DESCRIPTION1 field is required".
The exception is throwing during SaveChanges.
I can't understand where the problem is, as if I watch "plant" in debug, all fields are there (ID_PLANT4 = 1 is an existing row), and DESCRIPTION1 in particular is not Nothing.
I can simply remove the "Required" attribute and it works, but the attribute is a consequence of the db column not allowing nulls, so I don't think this is the right way to go.
I can even add this line of code, just after the "Using" statement:
dbContext.Configuration.ValidateOnSaveEnabled = False
and it works, but again I don't think this is the right way to go.
What is the reason of this behavior?
Eventually I find that the problem is: field DESCRIPTION1 is populate by default with 20 spaces. It is not null, but it is a string formed only by spaces. For an unknown reason, during validation this string is treated by EF like null, and an exception is thrown because it's a required field.
"Required" attribute is not needed, but I generate my POCO classes by "Code First from Database", so if a VARCHAR field is declared as "not null" it is automatically generated with the "Required" attribute. Now I think it's better allowing nulls for VARCHAR columns.

Delete by Object Id angular 2 and mongodb

I am trying to delete Posts from a comments section in my web app. Mongodb passes an objectId but I am unable to get it from my angular 2 front end. By default ObjectId is passed as _id so on my front end I call Post._id in my delete function and it passes through all this info
what I actually want is the unique ObjectId given by the database itself that look like this
How can I get this value on the front end of my application? I have all the code to my project on github located here with both my UI and API backend. Thanks for any help!
relative files from repo
UI/src/app/components - PostData.Service.ts
UI/src/app/components -postRand.component.ts
UI/src/app/components/models - Post.ts
API/src/controllers - PostAPIController.cs
API/src/models - Post.cs
API/src/models - DataAccess.cs
API Running
I believe I need to make this fix in the api layer because it is passing my _id as an object and not a string... This is just a guess of mine and I am not sure how to do this.
I found out how to solve my issue....
I had to go back into my model and convert my ObjectId to a string by parsing it out
[BsonRepresentation(BsonType.ObjectId)]
public string Id
{
get { return Convert.ToString(_id); }
set { _id = MongoDB.Bson.ObjectId.Parse(value); }
}
I then added my new Id field to all of my http calls changing out where I was calling by ObjectId, to now call upon my new string value Id
Now when I run my Api get the actual Id of my object
Lastly I added my newly generated Id field to my front end, replacing the old objectId value in my delete functions with the new Id.
All code has been updated in my git project, see file referenced in question for relevant documents.
After review it looks like you are trying to delete the object by an object id string instead of an object id object.
In PostAPIController.cs
line 74
objds.Remove(post._id);
This is looking for an object with a string value to match on.
Pretty sure it should be like this
objds.Remove(ObjectId.Parse(post._id))
This will construct a object id to match on for the deletion

Concurrency check not happening

I have a code first EF model with concurrency tokens on some of the entities.
These tokens are defined as byte[] properties and are decorated with [Timestamp] attributes.
[Timestamp]
public byte[] ConcurrencyStamp { get; set; }
(I've also tried any combination of Timestamp and ConcurrencyCheck)
The properties are also marked as concurrency tokens in OnModelCreating of my context:
modelBuilder.Entity<Room>().Property(x => x.ConcurrencyStamp).IsConcurrencyToken();
So, here's the scenario:
Some of the entities are serialized as JSON and passed on to external clients. When a client updates an object, that changed object is received again (as Json) and the changes are applied to a freshly fetched object. In this process, I also update the concurrencytoken value received from the client to the object just fetched from the db. Then, when saving changes, no concurrency error is thrown even if the values don't match.
So, to summarize:
1. fetch object from DB
2. serialize object to JSON (including concurrencytoken)
3. client messes with object
4. server receives updated object as json
5. fetch object (by id) from DB
6. apply json values to fetched object (including concurrencytoken)
7. context.savechanges
--> no error if token was changed
Checking the log, it seems that EF is doing the update statement with the "fetched" concurrencytoken when saving changes, not the token set manually from the external object.
UPDATE [dbo].[Rooms]
SET [RoomName] = #0, [ConcurrencyStamp] = #1
WHERE (([RoomId] = #2) AND ([ConcurrencyStamp] = #3))
-- #0: 'new room name' (Type = String, Size = -1)
-- #1: '1500' (Type = Int64)
-- #2: '1' (Type = Int32)
-- #3: '1999' (Type = Int64)
(I've used longs here, but the same applies to byte[] stamps, which I tried initially).
1999 is the current concurrencytoken value in the DB. 1500 is the token coming from the JSON object, which was set manually by setting the property.
Even though you can see EF updating the token in the statement (because I set the property) it is still using the original token value to do the check.
Changing the properties through the change tracker doesn't help, the behaviour stays the same.
Any clues? Is this scenario not supported? Am I doing something wrong?
Update
The check does work. When creating a new context in a separate thread and doing a change between the fetch and savechanges (thus between step 5 and step 7), the savechanges in step 7 barfs with a ConcurrencyException.
So it appears it works as described, but there's no way to "force" the token to be updated externally (which might make sense in a way, I guess).
You actually can force it.
You just need to set timestamp like this:
customerRequest.RowVersion = detachedRequest.RowVersion;
Context.Entry(customerRequest).Property(p => p.RowVersion).OriginalValue = customerRequest.RowVersion;
Context.Entry(customerRequest).Property(p => p.RowVersion).IsModified = false;
After that ef will think that its not updated and will throw concurrency exception on update.
tested on ef 6 code first.
EF always uses the OriginalValue of the timestamp fetched in step 5 in its UPDATE statement in step 7.
Setting entity.ConcurrencyStamp = viewModel.ConcurrencyStamp in step 6 only updates the CurrentValue.
To set the OriginalValue, do this in step 6 instead:
dbContext.Entry(entity).Property(e => e.ConcurrencyStamp).OriginalValue =
viewModel.ConcurrencyStamp;

How Do I Write A "Count By" Method Name Query In Spring Data

So I know that I can write an interface like the one below and Spring Data will automatically generate the necessary database access stuff for me. Now what I'd like to do is add a new method name that will count the number of entities that match a set of criteria.
public interface EventRegistrationRepository extends JpaRepository<EventRegistration, String>
{
List<EventRegistration> findByUser_EmailAddress(String email);
int countByEvent_Code(String eventCode);
}
As of now the countBy method causes this error:
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property count found for type com.brazencareerist.brazenconnect.model.relational.EventRegistration
What's the proper syntax for what I'm trying to do here?
This works as expected as of the just released Spring Data JPA 1.4.0.M1.

How do I tell Play Framework 2 and Ebean to save null fields?

I'm using Play Framework 2 and Ebean. When a user submits a form to edit an existing object in the database, it doesn't save null values. I guess this is to prevent overwriting fields that aren't in the form with null. But how can I let them set fields in the form to null if they need to?
For example, the user edits an Event object. Event.date is 1/1/13. The user sets the Event.date field in the form to empty and submits the form. Inspecting Event.date in the debugger shows its value is null. I save the Event. If I look at the Event in the database, its value is still 1/1/13.
Edit: It seems there is a method for this. The only problem is it doesn't work on nested entities. Any solutions for this?
update(Object bean,Set<String> properties)
Create an ebean.properties file right next to the application.conf file and add this line to it:
ebean.defaultUpdateNullProperties=true
Null properties in Ebean are considered as unloaded, so to prevent accidental nulling properties that shouldn't be nulled, they are just excluded.
Because of this reverting Date (and other fields) to null in Ebean is... hard :). Last time when I had to do the same thing (revert Date) I used second query to do just... nulling the Date (after event.update(Object o)):
public static Result updateEvent(){
Form<Event> eventForm = form(Event.class).bindFromRequest();
// do some validation if required...
Event event = eventForm.get();
event.update(event.id);
if (eventForm.get().date == null){
Ebean
.createUpdate(Event.class, "UPDATE event SET date=null where id=:id")
.setParameter("id", page.id).execute();
}
}
On the other hand, if you are using comparison, for filtering events (always selecting newer than X), you can just set the date to very 'old' value, which also should do the trick. In this case you'll update the object only once.
private static final Date VERY_OLD_DATE = new GregorianCalendar(1, 0, 1).getTime();
public static Result updateEvent(){
Form<Event> eventForm = form(Event.class).bindFromRequest();
Event event = eventForm.get();
if (eventForm.get().date == null){
event.date = VERY_OLD_DATE;
}
event.update(event.id);
}
In this case in your HTML form you will need to clear the value of the form's field (or just send every time date like 0001-01-01), however it can be done easily even with JavaScript.