pburggra
pburggra

Reputation: 11

Grails - Domain Record Not Updating on .Save()

Long time listener, first time caller. I've been practicing coding for ~1 year now and I'm currently trying to build a small CRUD app in Grails. For the past 3-4 weeks I have been unable to update records in the h2 database that Grails comes packaged with. I'm read many articles online, sifted through Stack Overflow, and even purchased the book 'Grails in Action' as a reference. Each resource makes me believe this should be relatively easy. I'm not sure if my issue is in my view (maybe how I'm posting the data), in my controller, or possibly in my application.yml. Below is my HTML, javascript, controller, domain, and application.yml code. Any help is GREATLY appreciated.

A few items to note: 1) When an update is executed, Grails validates with zero errors. 2) The update action prints the object's new attributes to the console, but the database isn't updated.

HTML - note, javascript function is used to change readOnly attributes from true to false

 <form method="POST" action="../update">
        <!--hidden field for contractor id-->
        <input name="id" value="${params.id}" hidden>

        <div class="row">
            <div class="col-15">
                <label for="name">Name:</label>
            </div>

            <div class="col-40">
                <input class="readOnly" type="text" id="name" name="name" value="${params.name}" readonly>
            </div>

            <div class="editIcon">
                <i id="editIcon" class="far fa-edit" title="Edit Contractor"></i>
            </div>
        </div>

        <div class="row">
            <div class="col-15">
                <label for="street">Street:</label>
            </div>

            <div class="col-40">
                <input class="readOnly" type="text" id="street" name="street" value="${params.street}" readonly>
            </div>
        </div>

        <div class="row">
            <div class="col-15">
                <label for="city">City:</label>
            </div>

            <div class="col-40">
                <input class="readOnly" type="text" id="city" name="city" value="${params.city}" readonly>
            </div>
        </div>

        <div class="row">
            <div class="col-15">
                <label for="state">State:</label>
            </div>

            <div class="col-15 removeLeftPadding">
                <select id="state" name="state" disabled>
                    <option value="AL">Alabama</option>
                    <option value="AK">Alaska</option>
                </select>
            </div>

            <div class="col-05 zip">
                <label for="zip">Zip:</label>
            </div>
            <div class="col-13">
                <input class="readOnly" type="text" id="zip" name="zip" value="${params.zip}" readonly>
            </div>

        </div>

        <div class="row">
            <div class="col-15">
                <label for="paymentType">Payment Type:</label>
            </div>

            <div>
                <select id="paymentType" name="paymentType" disabled>
                    <option value="1">ACH</option>
                    <option value="2">Credit Card</option>
                </select>
            </div>
        </div>

        <div class="row">
            <input type="submit" value="Update">
        </div>
    </form>

JavaScript - I doubt the issue is here as the parameters are passed when viewing the network tab in Google dev tools (see screenshot below).

    /*When icon edit icon is clicked, set readOnly/disabled values to false and input/icon colors to appropriate colors*/
$( "#editIcon" ).click(function() {
    console.log("Edit called.")

    if(!clicked){
        $( "#name" ).prop('readonly', false)
        $( "#street" ).prop('readonly', false)
        $( "#city" ).prop('readonly', false)
        $( "#state" ).prop('disabled', false)
        $( "#zip" ).prop('readonly', false)
        $( "#paymentType" ).prop('disabled', false)

        //Set input color to black
        $( "#name" ).css("color", "black")
        $( "#street" ).css("color", "black")
        $( "#city" ).css("color", "black")
        $( "#state" ).css("color", "black")
        $( "#zip" ).css("color", "black")
        $( "#paymentType" ).css("color", "black")

        //Set icon to red
        $("#editIcon").css("color", "red")

        clicked = true;
    } else{
        $( "#name" ).prop('readonly', true)
        $( "#street" ).prop('readonly', true)
        $( "#city" ).prop('readonly', true)
        $( "#state" ).prop('disabled', true)
        $( "#zip" ).prop('readonly', true)
        $( "#paymentType" ).prop('disabled', true)

        //Set input color to black
        $( "#name" ).css("color", "gray")
        $( "#street" ).css("color", "gray")
        $( "#city" ).css("color", "gray")
        $( "#state" ).css("color", "gray")
        $( "#zip" ).css("color", "gray")
        $( "#paymentType" ).css("color", "gray")

        //Set icon to red
        $("#editIcon").css("color", "#7543b7")

        clicked = false;
    }

});

Update Action in Contractor Controller

  def update(Long id){
    println("I made it to update for Contractors")
    def contractorInstance = Contractor.get(id)
    contractorInstance.properties = params

    println contractorInstance.validate()
    println contractorInstance.errors

    if(!contractorInstance.save()){
        println 'Contractor did not update.'
    } else{
        println 'Contractor updated.'
    }

    println contractorInstance.id  //prints id to console
    println contractorInstance.name   //prints updated name to console

    redirect(action: "index")
}

Development Data Source in Application.yml

environments:
development:
    dataSource:
        dbCreate: create-drop
        url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
        logsql: true    #added by PCB on 5/14

Dev Tools Screenshot - Form Data Post

Contractor Domain

class Contractor {
String name
String street
String city
String state
String zip
PaymentType paymentType 

static hasMany = [masteragreements: MasterAgreement, contracts: Contract, termsAndConditions: TermsAndConditions]

static constraints = {

}

}

EDIT: I'm using Grails Version 4.0.3

Upvotes: 1

Views: 1306

Answers (2)

Orubel
Orubel

Reputation: 324

One of the ways you can force the flush (and view your errors) is to do it something like this:

if(!instance.save(flush: true)){
    instance.errors.allErrors.each { println it }
}

Upvotes: 1

erichelgeson
erichelgeson

Reputation: 2340

A Transaction is required to write to the database in Grails 4/Hibernate 5.2+. In your case you are writing to a session but never instructing that session to be flushed(written) to the database. That happens at the end of a transaction or when flush is explicitly called.

The most common solution would be to move the logic to a Service and annotate the method with @Transactional or using GORM Data Services as they manage transactions for you.

Upvotes: 2

Related Questions