Reputation: 3774
I am playing with grails 3 app. I created this simple book app with crud actions. It is not updating the record.
here is book domain
package bookapp
class Book {
String name
static constraints = {
}
}
here is book controller
package bookapp
import grails.plugin.springsecurity.annotation.Secured
@Secured('ROLE_USER')
class BookController {
def index() {
[books: Book.findAll()]
}
def create(){
}
def save(){
def book = new Book(params)
book.save()
redirect(action:'show', id:book.id)
}
def edit(Long id){
[book: Book.get(id)]
}
def update(Long id){
def book = Book.get(id)
bindData(book, params)
book.save()
redirect(action:'show', id:book.id)
}
def show(Long id){
[book: Book.get(id)]
}
def destroy(){
}
}
Here is the edit view page
<h1> Edit Book Page</h1>
<g:form action="update" id="${book.id}">
<g:textField name="name" value="${book.name}"></g:textField>
<g:submitButton name="submit"></g:submitButton>
</g:form>
When i am in edit page and change the name field and then click submit button. It will not update the book. If i change update action to
def update(Long id){
def book = Book.get(id)
bindData(book, params)
book.save(flush:true)
redirect(action:'show', id:book.id)
}
This works.So my question is why do we need to explicitly flush? Why is save() not saving the record? Am i missing anything? Thanks for help!
Full Code:
First create a domain Book as follows
class Book {
String name
static constraints = {
}
}
Create Book controller
class BookController {
def index() {
[books: Book.findAll()]
}
def create(){
}
def save(){
def book = new Book(params)
book.save()
redirect(action:'show', id:book.id)
}
def edit(Long id){
[book: Book.get(id)]
}
def update(Long id){
def book = Book.get(id)
book.properties = params
book.save()
redirect(action:'show', id:book.id)
}
def show(Long id){
[book: Book.get(id)]
}
def destroy(Long id){
Book.get(id).delete()
redirect(action:'index')
}
}
Views
create.gsp
<h1> Create Book Page</h1>
<g:form action="save">
<g:textField name="name"></g:textField>
<g:submitButton name="submit"></g:submitButton>
</g:form>
edit.gsp
<h1> Edit Book Page</h1>
<g:form action="update" id="${book.id}">
<g:textField name="name" value="${book.name}"></g:textField>
<g:submitButton name="submit"></g:submitButton>
</g:form>
index.gsp
<g:each in="${books}" var="book">
<h2><g:link action="show" id="${book.id}"> ${book.name} </g:link></h2>
</g:each>
<g:link controller="book" action="create">Create Book</g:link>
show.gsp
<h1> ${book.name} </h1>
<g:link action="edit" id="${book.id}">Edit</g:link>
<g:link action="destroy" id="${book.id}">Delete</g:link>
<g:link action="index">Back</g:link>
Delete and Update doesn't work with just save(). Need to add save(flush:true). Thanks!
UPDATE: This is strange. The same code works with grails 2. I tried with grails 2.2. It only is not working with grails 3. The version of grails 3 i am using to test this is grails 3.3.6. Is there any change in grails 3 that is breaking this?
UPDATE 2:
I have now moved the delete action to service as follows.
@Transactional
class BookService {
def destroy(Long id) {
def b = Book.get(id)
b.delete()
}
}
In the controller
class BookController {
def bookService
def destroy(Long id){
bookService.destroy(id)
redirect(action:'index')
}
}
And view code to delete is
<h1> ${book.name} </h1>
<g:link action="edit" id="${book.id}">Edit</g:link>
<g:link action="destroy" id="${book.id}">Delete</g:link>
<g:link action="index">Back</g:link>
When i click Delete then it redirects to index page but i can still see the record. Am i missing anything? Thanks!
Upvotes: 1
Views: 344
Reputation: 481
I see two classic mistakes that I see in every Grails app I've ever worked on. So don't be discouraged.
First while you can do persistantance in Controllers, you shouldn't that is what Services are for, to decouple your business logic from your routing, and rendering.
Second you when you do db modifications you should be using a transaction. In Grails you can do that by using @Transactional, which you can add to a method or to a class. In general you shouldn't have to do flush:true, unless you need the id of the saved record. @Transactional can take many arguments to alter how the transaction works: https://docs.grails.org/latest/guide/services.html#declarativeTransactions
A while ago I created these architecture diagrams for Grails 3: https://github.com/virtualdogbert/Grails_Architecture
I also created this page as a place for Groovy/Grails resources: https://github.com/virtualdogbert/Groovy_Links
Hope that helps.
For the delete is your list setup in the service like this:
class BookService {
@Transactional(readOnly = true)
def listBooks() {
Book.list()
}
Upvotes: 5