I am using grails-2.1.1. When I load the edit page, I am assigning some value in the edit action in controller. But it is updating my table! although I am not saving. How can I stop it?
Here is my code below. My edit action in controller:
def edit() {
def accTxnMstInstance = AccTxnMst.get(params.id)
if (!accTxnMstInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'accTxnMst.label', default: 'AccTxnMst'), params.id])
redirect(action: "list")
return
}
accTxnMstInstance?.accTxnDtls?.each {
if (it?.debitCoa != null && it?.debitCoa != "") {
String debitCoaVal = ""
List<String> items = Arrays.asList(it?.debitCoa?.split("\\s*~\\s*"))
items.each {
List itemList = new ArrayList()
List<String> subItems = Arrays.asList(it.split("\\^"))
subItems.each {
itemList.add(it)
}
itemList.add("false")
itemList.add("0")
itemList.each {
debitCoaVal += it.toString() + "^"
}
debitCoaVal += "~"
}
it?.debitCoa = debitCoaVal
debitCoaVal = ""
}
if (it?.creditCoa != null && it?.creditCoa != "") {
String creditCoaVal = ""
List<String> items = Arrays.asList(it?.creditCoa?.split("\\s*~\\s*"))
items.each {
List itemList = new ArrayList()
List<String> subItems = Arrays.asList(it.split("\\^"))
subItems.each {
itemList.add(it)
}
itemList.add("false")
itemList.add("0")
itemList.each {
creditCoaVal += it.toString() + "^"
}
creditCoaVal += "~"
}
it?.creditCoa = creditCoaVal
creditCoaVal = ""
}
}
[accTxnMstInstance: accTxnMstInstance]
}
You can see that I am not saving after assigning the value just passing to view.
Grails uses the Open Session In View (OSIV) pattern, where at the beginning of the web request a Hibernate session is opened (and stored in a thread-local to make it easily accessible) and at the end of the request as long as there wasn't an exception, the Hibernate session is flushed and closed. During any flush, Hibernate looks at all "active" object instances and loops through each persistent property to see if it is "dirty". If so, even though you didn't explicitly call save(), your changes will be pushed to the database for you. This is possible because when Hibernate creates an instance from a database row it caches the original data to compare later to the potentially-changed instance properties.
A lot of the time this is helpful behavior, but in cases like this it gets in the way. There are lots of fixes though. One drastic one is to disable OSIV, but this is generally a bad idea unless you know what you're doing. In this case there are two things you can try that should work.
One is to change AccTxnMst.get(params.id) to AccTxnMst.read(params.id). This will not cause the instance to be strictly "read-only" because you can still explicitly call save() and if something was modified, all of the instance changes will be persisted. But the caching of the original data isn't done for instances retrieved using read(), and there's no dirty checking during flush for these instances (which isn't possible anyway since there's no cached data to compare with).
Using read() is a good idea in general when retrieving instances that are not going to be updated (whether you make property changes or not), and makes the code more self-documenting.
Another option is to call discard() on the instance before the controller action finishes. This "detaches" the instance from the Hibernate session, so when the OSIV filter runs at the end of the request and flushes the Hibernate session, your instance won't be considered dirty since Hibernate won't have access to it.
read() only makes sense for individual instances retrieved by id, whereas discard() is useful for any instance, e.g. if they're in a mapped collection or were retrieved by a non-id query (e.g. a dynamic finder, criteria query, etc.)
Related
I am using Grails 2.4.5 version. I am checking a condition while saving. If my condition matches I am trying to return with a message instead of saving. But it is showing me the message that I want but at the same time it saves the object properties to database.
Here is my code:
#Transactional
def save(AccountHead accountHeadInstance) {
if (!request.method.equals('POST')) {
redirect(action: 'index')
return
}
LinkedHashMap result = new LinkedHashMap()
String outPut
if (accountHeadInstance.id) {
if (accountHeadInstance.id == accountHeadInstance.subParentId) {
result.put(CommonUtils.IS_ERROR, Boolean.TRUE)
result.put(CommonUtils.MESSAGE, "Sorry, Same account can not be parent of it's own")
outPut = result as JSON
render outPut
return
// here should it return, is shows the message ok but saving the object. if I print something after this block it does not execute
}
}
if (accountHeadInstance.hasErrors()) {
def errorList = accountHeadInstance?.errors?.allErrors?.collect{messageSource.getMessage(it,null)}
result.put(CommonUtils.IS_ERROR, Boolean.TRUE)
result.put(CommonUtils.MESSAGE, errorList?.join('\n'))
outPut = result as JSON
render outPut
return
}
accountHeadInstance.hasChild = params.position == CommonUtils.POSITION_FIRST
accountHeadInstance.save flush:true
result.put(CommonUtils.MESSAGE, "Account head saved successfully.")
outPut = result as JSON
render outPut
return
}
Well you are using #Transactional, so any changes to domain object will set that object as dirty, and when the transaction is ended, they will be saved implicitly. To prevent this you can use accountHeadInstance.discard().
However I feel compelled to tell you that your code as several Grails anti-patterns.
Any business logic especially that that uses #Transactional should be moved to service(s), and not done in controllers. Controllers should just be for binding incoming params, routing to servies, and rending results.
While you can use domains as command object binding parameters it is generally not a good idea, from a security standpoint.
Optionally you can do 'Groovy' things like this:
LinkedHashMap result = [:]
result[CommonUtils.IS_ERROR] = Boolean.TRUE)
render result as JSON
My domain object booking has multiple attributes that are allowed to be null, because they will be set later after the object has been saved to the db.
Part of myService.action():
booking.properties = params
if (booking.contactFirstname?.length() <= 1) { booking.errors.rejectValue("contactFirstname", "empty") }
if (booking.contactLastname?.length() <= 1) { booking.errors.rejectValue("contactLastname", "empty") }
if (booking.contactPhone?.length() <= 1) { booking.errors.rejectValue("contactPhone", "empty") }
if (booking.contactMobile?.length() <= 1) { booking.errors.rejectValue("contactMobile", "empty") }
if (booking.contactEmail?.length() <= 1) { booking.errors.rejectValue("contactEmail", "empty") }
if (booking.hasErrors() || ! booking.validate()) {
return [success: false, model: booking]
} else {
booking.save(failOnError: true)
return [success: true, model: booking]
}
My controller does:
def result = myService.action(params)
if (result.success) {
flash.success = message(code: "msg.successfullySaved")
redirect(action: "registerEventConfirmation", id: result.model.uid, params: [lang: params.lang], mapping: "paginated")
} else {
flash.error = message(code: "msg.errorSavingCheckFields")
render(view: "registerEventStep3", params: [lang: params.lang], model: [booking: result.model])
I'm using
hasErrors(bean: booking,field:'contactFirstname', 'has-error')}
to mark error fields.
If I now submit the form without any values in textfields, all fields are red, booking.errors has >0 errors.
If I submit the form after with a firstname, booking.errors is NULL and no other field is marked.
Is this a Bug? I'm with Grails 2.3.6
additional information
I visit the form, submit it empty completely
I see all form fields in red, object.errors has >0 errors (VALID)
I enter a value in the first field, firstname and submit
I see none of the form fields in red, object.errors =0 errors (INVALID)
I re-submit the form with none changes
I see all empty form fields in red, object.errors has >0 errors (VALID)
Now that I fully understand the situation and since I was having trouble sleeping I thought I give you a very concise answer so that you can hopefully make full sense and use things properly.
Firstly I know creating a validation bean sounds like it will be a lot of work so let me teach you how to do it all relatively simply and why it is my preferred method.
It is my preferred method simply because when you do
class MyController {
def myAction(Mybean bean) {
// 1. the object allowed into this save action
// are all that is available objects withing MyBean.
// If it has user define but not telephone. Then
// if telephone is passed to myAction it will fail and not recognise
// field
// When declaring Date someField or User user then the params now
// received as bean this way is now actually properly bound
// to the data / domainType declared.
// Meaning user will now be actual user or someField actually Date
}
So now to explain how to best solve this issue. When creating beans simply copy over the actual domain class from your domain folder into src/groovy/same/package in grails 2 or src/main/groovy/same/package in grails 3
Change name / class or copy as from Booking to BookingBean so it has a different name.
Add #Validateable above actual BookingBean in grails 2 or add implements to main class like Class BookingBean implements Validateable { in grails 3
Now since it is copied all the objects are identical and at this point a save from the controller would be
class MyController {
def myAction(BookingBean bean) {
Booking booking = new Booking()
// this will save all properties
booking.properties = bean
booking.save()
}
}
But you have a special circumstance and you wanted to declare a transient field in the main domain class what I would do instead is
class BookingBean {
def id
String contactFirstname
String contactLastname
boolean secondSave=false
static constraints = {
id(nullable: true, bindable: true)
contactFirstname(nullable:true) //,validator:checkHasValue)
contactLastname(nullable:true) //,validator:checkHasValue)
secondSave(nullable:true,validator:checkHasValue))
}
//use the same validator since it is doing identical check
static checkHasValue={value,obj,errors->
// So if secondSave has a value but contactFirstName
// is null then complain about contactFirstName
// you can see how secondSave gets initialise below
//typical set this to true when you are about to save on 2nd attempt
//then when set run validate() which will hit this block below
// Check all the things you think should have a
// value and reject each field that don't
if (val) {
if ( !obj.contactFirstname) {
errors.rejectValue('contactFirstname',"invalid.contactFirstname")
}
if ( !obj.contactSecondname) {
errors.rejectValue('contactSecondname',"invalid.contactSecondname")
}
//and so on
}
}
So now in your controller:
class MyController {
def save1(BookingBean bean) {
Booking booking = new Booking()
// this will save all properties
booking.whatEver = bean.whatEver
booking.save()
// you can choose to validate or not here
// since at this point the secondSave has
// not been set therefore validation not called as yet in the bean
}
//you probably have id and it should bind with actual domain class
def save2(BookingBean bean) {
booking.secondSave=true
if (!bean.validate()) {
//this is your errors
//bean.errors.allErrors
return
}
//otherwise out of that loop since it hasn't returned
//manually set each object
booking.contactFirstname=bean.contactFirstName
booking.contactSecondname=bean.contactSecondname
booking.save()
}
}
e2a side note - above should answer
well don't validate it until you have created it. Only validate it after you created the object then added a value. Alternative create a function possibly in a validation bean that you run as part of your 2nd check. This Example bean is not validated until formatRequest is called as seen here
I don't grasp the specifics of your question, so I will give some general guidance since I have just dug into this.
Don't call hasErrors() before validate(). If you do, Grails won't hand you errors from domain constraints and you will only end up with the errors you set yourself using rejectValue().
Be careful with using rejectValue(). Try to set all your errors using domain constraints. If you have sophisticated constraints use the validator syntax and obj.getPersistentValue() might be your friend once in a while.
If you still have to use rejectValue(), understand that any later calls to validate() will start from scratch and erase your prior errors. I have written a workaround for this (to be placed in your domain object) although I can't assure you it is 100% ok:
def validateWithErrors(def fields = null) {
def existingErrors = this.errors
def ret = (fields ? this.validate(fields) : this.validate())
existingErrors?.allErrors?.each { error ->
this.errors.rejectValue(error.field, error.code)
}
return (existingErrors?.allErrors ? false : ret)
}
I have a custom validation in controller because I need to validate with database.
def update(Object instance) {
if (instance == null) {
notFound()
return
}
if (instance.hasErrors()) {
//redirect code here
return
}
def obj = objService.someMethod()
//some validation code here
if(someCheck){
// if i discard it wont work
instance.discard()
flash.error = message(code: 'message.code')
//render code here
return
}
In the above code even the instance.discard() does not work after the database access is performed. The changed data is automatically saved even there is no save method call. The same instance.discard() will work if there is no database access is performed. How do I discard the changed value to be persisted to the database when there is a validation failure.
Add the annotation #Transactional(readonly = true) above your update action. That should do the trick.
This is the in built feature of Grails that automatically persist the instance to the database.
Please read my answer here for more detail
https://stackoverflow.com/a/32659803/2405040
Is there an equivalent of Rails ActiveRecord::Callbacks in ASP MVC?
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
I'm in a situation where we are not using identities for our primary key. We do this for reasons specific to our DB sharding design. Because of this we have a lookup table to find the next ID for a specific table. I'd like to automatically get this value and set it in an abstract class whenever a model is created/updated and before it is saved. I also need to update the lookup table with an incremented 'nextID' after the save is successful.
I'm open to other solutions on how to do this without callbacks as well.
So you need the callback just to increment ID in the lookup table? AFAIK there is no equivalent in ASP.NET, may be you could try with Async Controllers (http://msdn.microsoft.com/en-us/library/ee728598%28v=vs.100%29.aspx) and wait for a state change from the successful save, but I would prefer use a service specifically for this like Snowflake (https://github.com/twitter/snowflake/).
I found a solution using overrides as opposed to callbacks. It's my hope that ASP mvc adds support for callbacks as the framework continues to mature because callbacks allow for cleaner code by allowing the OnSave event to exist in the model[s] that the event is concerned with rather than the centralized DbContext class (separation of concerns).
Solution:
The SaveChanges method can be overridden in the Context Class (Entity Framework Power Tools creates the Context class is the 'Models' directory).
public override int SaveChanges()
{
// create a cache for id values in case their are multiple added entries in the dbcontext for the same entitytype
Dictionary<string, UniqueID> idCache = new Dictionary<string, UniqueID>();
IEnumerable<DbEntityEntry> changes = this.ChangeTracker.Entries();
foreach (var entry in changes)
{
//check if this is a new row (do nothing if its only a row update because there is no id change)
if (entry.State == System.Data.EntityState.Added)
{
//determine the table name and ID field (by convention)
string tableName = entry.Entity.GetType().Name;
string idField = entry.Entity.GetType().Name + "ID";
UniqueID id = null;
//if we've already looked this up, then use the cache
if (idCache.ContainsKey(tableName))
{
id = idCache[tableName];
}
//if we havn't looked this up before get it and add it to the cache
else
{
id = this.UniqueIDs.Find(tableName, idField);
//if it doesn't already exist in the lookup table create a new row
if (id == null)
{
id = new UniqueID(tableName, idField, 1);
// since this is a new entry add it
this.UniqueIDs.Add(id);
}
else
{
// set the state to modified
this.Entry(id).State = System.Data.EntityState.Modified;
}
}
entry.CurrentValues[tableName + "ID"] = id.NextID;
id.NextID = id.NextID + 1;
}
}
return base.SaveChanges();
}
I am having a problem with transaction in Grails. I want to save a list of object to DB by a checking condition at each object. All these process I want to put to one transaction, it means if the k-th object does not satisfied the checking condition, all previous objects (from the first object to the (k-1)th one) will be rolled back from DB. Here is my example:
static transactional = true
public void saveManyPeople() {
// ...
List<People> peoples = new ArraysList();
for(i = 0, i < n, i++) {
People newPeople = createPeopleFromRawData(); // return a people object in memory
if(<checking-condition>) {
newPeople.save(flush : false)
} else {
throw new MyCustomizedException() // MyCustomizedException has extended from RuntimException
}
}
// ...
}
As you may see, I set transactional variable to true and I've tried to use flush : true and flush : false, but it didn't work as I want. I've read this article Rolling back a transaction in a Grails Service
And the author recommended that the service method should throw a RuntimeException then the process will be rollbacked. But if I want to throw another exception, so what I have to do?
Could you please give me some suggestions on this problem?
Thank you so much!
You can throw any exception that extends from RuntimeException to rollback the transaction. Or you can use Programmatic Transactions, using withTransation, to have more control over the transaction.
Could you verify that saveManyPeople() is within a Service and not a Controller?
The static transactional = true isn't respected in a Controller. I am suspecting that this is the issue.
If you need to have transactional support with the controller, you could always use DomainClass.withTransaction. Reference Documentation
Example:
Account.withTransaction { status ->
def source = Account.get(params.from)
def dest = Account.get(params.to)
def amount = params.amount.toInteger()
if(source.active) {
source.balance -= amount
if(dest.active) {
dest.amount += amount
}
else {
status.setRollbackOnly()
}
}
}