Grails will create an id and a version columns from a domain class automatically. I want to use my own column for the primary key. So, I follow the doc to change the mapping.
class book {
String isbn
static mapping = {
id generator: 'assigned', name: 'isbn'
}
}
So far so good. The isbn column is now the primary key.
I use generate-all to create the view and controller. However, the data binding won't work anymore.
Create and Save work no problem. It binds a book to the view. I can add a new book to the database no problem.
def create() {
respond new Book(params)
}
def save(Book book) {
if (book == null) {
notFound()
return
}
...
}
But the Update action does not bind. book is null after I click the Update button from the Edit view.
def update(Book book) {
if (book == null) {
notFound()
return
}
...
}
The codes generated by generate-all in the Save and Update actions are the same. I don't understand why it will bind the book to the Save action but not to Update action.
Would you show me the problem please?
Many Thanks!
I think I figure it out. When I bind an object to a view, Grails is hardcoded to look for the id property. It has to be spelled "id". If there is no "id" property in the domain class, Grails will not bind.
The way I figure this out is to look at the actual HTML generated by the server.
If there is an id property bind to the view, I see the HTML has the ../controller/action/id link.
If the id property is missing, the HTML link is just ../controller/index
I am new to Grails. So, I guess in order for the binding to work, I need to have an id property for Grails to put in the link.
I think this is a REST call. I don't know what REST is though.
So, I will try to add an dummy id property to my Book domain class to see if Grails will take the bait. I will set up the domain so Grails won't generate the id column in the database table. The id property is used locally only. No need to save it to the database.
class book {
String isbn
String id
static mapping = {
id generator: 'assigned', name: 'isbn'
}
}
I will copy the isbn value to the id property. I am not sure if this will work or not. I hope Grails will generate the link in the view with the isbn string in the id property instead of the default integer id value.
../controller/action/978-3-16-148410-0
Related
I am creating a project using ASP.NET MVC 5.
I used Identity to get user information.
Using migrations I created fields like FirstName, LastName
But how do I use that? I have this:
#using MTC.Models
#helper Render(MTC.Post post, System.Web.Mvc.HtmlHelper html, bool showComments)
{
{
var user = new ApplicationUser();
user.FirstName.ToString();
}
}
And it returns this:
System.NullReferenceException: Object reference not set to an instance of an object.
All I want to do is get it to display the name from the database.
I am trying to learn the backend. Sorry If this is completely wrong.
you are creating new object of ApplicationUser without initializing its values. So every property of this object will be null. You need to either first assign value to it or fetch user from database.
You can do this by creating UserID column in your table along with Names of user and get User Name from the table by:
string Name = User.Identity.GetUserId();
return View(Database.Table.Where(r =>r.UserID == Name).ToList());
You can do this in your controller and also in your view.
I have the following Groovy Domain class and generated scaffold controller for it.
When I create a Book row in the table, the ID column is invisible on the list view.
Is there anyway to make ID column visible on the view? I tried visible:true but it seems not making any difference.
class Book {
String bookAuthor
static constraints = {
bookAuthor blank: false, maxSize:30
}
static mapping = {
version false
id generator: 'sequence',
params: [sequence:'s_book_seq']
}
}
Edit the generated scaffolding views and add an id field manually. If you need to do this for a large number of domain classes, modify the scaffolding templates instead; you can install the templates using grails install-templates, they will be copied into src/templates
I am developing a Grails 2.3.7 application and I'm having trouble changing a domain property with a select box. Every time I try to change the property and save, I get a HibernateException: identifier of an instance of Ethnicity was altered from X to Y. I don't want to change the ID of the ethnicity, I simply want to change the ApplicationPersons ethnicity from one to another.
A few things to note:
I am using the same controller action to create AND update the person.
Setting personInstance.ethnicity to null right before personInstance.properties = params will make the save work, but I
don't know why, and I don't want to do this for every association
that I want to change.
I realize the domain model seems odd. It is a legacy DB that I cannot change.
Here are my domain classes:
class ApplicationPerson implements Serializable {
Integer appId
Integer applicationSequenceNumber
String firstName
String lastName
Ethnicity ethnicity
static mapping = {
id composite: ['appId', 'applicationSequenceNumber'],
generator: 'assigned'
}
}
class Ethnicity {
String code
String description
static mapping = {
id name: 'code', generator: 'assigned'
}
}
Here is my _form.gsp to update the Ethnicity (I removed all the other properties that are saving just fine):
<div class="fieldcontain ${hasErrors(bean: personInstance,
field: 'ethnicity', 'error')} ">
<label for="ethnicity">Ethnicity</label>
<g:select id="ethnicity"
name="ethnicity.code"
from="${Ethnicity.list()}"
optionKey="code"
value="${personInstance?.ethnicity?.code}" />
</div>
And lastly, my controller action that the form POSTs to:
def save() {
Application app = applicationService.getCurrentApplication()
// Find/Create and save Person
ApplicationPerson personInstance = app.person
if (!personInstance) {
personInstance =
new ApplicationPerson(appId: app.id,
applicationSequenceNumber: app.sequenceNumber)
}
personInstance.properties = params
if (!personInstance.validate()) {
respond personInstance.errors, view:'edit'
return
}
personInstance.save flush:true
redirect action: 'list'
}
Modify the name in the select element from ethnicity.code to personInstance.etnicity.code as shown below:
<g:select id="ethnicity"
name="personInstance.ethnicity.code"
from="${Ethnicity.list()}"
optionKey="code"
value="${personInstance?.ethnicity?.code}" />
The name of selected option gets bound to params as key and the selected value as the
value against the key. Using ethnicity.code would try to modify the primary key of an existing ethnicity instead of modifying the ethnicity of an application person.
UPDATE
Above change in name is optional (can be used in case you don't need params to be assigned as properties of domain class). Previous name ethnicity.code should work as well but below changes are also required in the controller action in order to set ethnicity:
//or use params.ethnicity.code
//if name is ethnicity.code
person.ethnicity = Ethnicity.load(params.personInstance.ethnicity.code)
//or use params if name is ethnicity.code
person.properties = params.personInstance
Load an existing Ethnicity based on the passed in code and then set it in person before setting properties.
The issue lies with code being the primary key of Ethnicity. If Ethnicity had a separate identity column (for example, the default Long id) then your exact implementation in question would work with the help of data binding. But since it is mentioned that you are working with legacy database, I suppose you won't be able to modify the tables to add another column for id. So your best bet will be to load(cheap compared to get, as row is loaded from hibernate cache) ethnicity from the passed in code and then set it to person.
You can also see try caching the Ethnicity domain if possible because that will be master data and a good candidate for caching.
I'm beginner in Grails and I have a problem when I try to save a POGO
I have created 1 domain class
class Book {
String title
}
Then, I have generated the controller and view automatically.
Now, I want to be able to create a book with the code by clicking "create" (I know it is possible directly with the code generated but for my example I want to do it by the code). To do this, I have modified the method 'save(Book bookInstance)' in the controller like this
#Transactional
def save(Book bookInstance) {
def book = new Book(title:"New Grails Book").save()
But, when I go to the URL localhost:8080/myApp/book/create and then I click "Create", I have the error
message -> /myApp/WEB-INF/grails-app/views/book/save.jsp
description -> The requested resource is not available.
When I put this code in bootStrap, it is OK, so I don't understand why it is not in the controller
When you have a hasMany property in a domain class, Grails adds a Set property to the domain class with an AST transformation (so it's actually there in the bytecode, and it's visiable to Java) to represent the collection, and when you add a belongsTo a field of that type is added. So it's as if you had this code:
class Author {
Set<Book> books
static hasMany = [books: Book]
String name
}
and
class Book {
Author author
static belongsTo = [author: Author]
String title
}
The AST xform uses the map key as the field name, so you can use any valid field name, but the convention is to do what you did.
Properties are nullable:false by default, so your code doesn't save the Book instance because you didn't set the author property. When doing this explicitly you typically don't create the Book directly, but instead add it to the Author's collection using the dynamic addToBooks method. This sets the author field back-reference and when you save the author, the book is transitively validated and saved. This is all handled for you when you have code like new Book(params).save(), and you can do it directly, e.g.
Author author = ...
def book = new Book(title:"New Grails Book", author: author).save()
If you're using a generated controller and GSPs, there should be an author id in the params map, it'll likely be author.id, so that first line would be
Author author = Author.get(params['author.id'])
but you can add
println params
at the top of the action method to see all of the submitted params.
In general you don't want to look at the return value of the save call, since it will be null if there's a validation error and there's no way to retrieve the errors. So change
def book = new Book(...).save()
to
def book = new Book(...)
book.save()
and now you can call book.hasErrors(), book.getErrors(), book.errors, etc. to see if it was successful and if not, what went wrong.
But that's not the exact problem you're seeing, just one you will when you fix your problem. There's no save.gsp, and Grails also looks for save.jsp and confusingly includes that name in the not-found message. The save method is accessed via a POST request, typically from the form generated by the create action, and it either re-displays create.gsp with the submitted data and error messages when validation fails, or redirects to the view action when the save succeeds. There's no need for a save.gsp when using the generated code.
I have a Grails 2.2.3 domain class called FundType that I am trying to map to a legacy database table. It has two fields: code and description. I would like the id to be called code anytime I use the domain class and preferably on any of the generated scaffolding. But every time I use the name key on id I get this exception:
| Error 2013-07-24 09:38:44,855 [localhost-startStop-1] ERROR context.GrailsContextLoader - Error initializing the application: Error evaluating ORM mappings block for domain [com.company.scholallow.FundType]: null
Message: Error evaluating ORM mappings block for domain [com.company.scholallow.FundType]: null
This is what my domain class consists of:
class FundType {
String id
String description
static mapping = {
id column: 'fund_code', generator: 'assigned', name: 'code'
description column: 'fund_desc'
}
}
And anytime I am using a FundType instance I would like to call code like fundTypeInstance.code and NOT fundTypeInstance.id. This will make it more user friendly for me because I'm dealing with something called code, not id.
So I would like to know is what I'd like to do possible? And what am I doing wrong in my domain class that is causing this ORM mappings error?
Edit:
Okay, so I changed my domain class to the following and I am getting a FundType not found with ID null error.
class FundType {
String code
String description
static mapping = {
id generator: 'assigned', name: 'code'
code column: 'fund_code'
description column: 'fund_desc'
}
}
I added some sql logging to see what Hibernate is doing and this is what was output: select * from ( select this_.FUND_CODE as RTVFTYP1_1_0_, this_.FUND_DESC as RTVFTYP2_1_0_ from RTVFTYP this_ ) where rownum <= ?
Use String code instead of String id in the domain class.
You are deliberately mentioning to the GORM that I want to use the property code which maps to table column fund_code whose value is assigned as the id (primary key). In that case, you just need to have the property codedefined in the domain class instead of the id.
(I'm answering the fix that worked for me for future use by other programmers)
#dmahapatro was right, I needed to add String code.
It looks like naming the id something different just doesn't play well with Grails dynamic scaffolding. I did some tests and I can still use FundType.get(code) and it will return the object just as if I passed in an id. I can also do FundType.findByCode(code).
It looks like I have to change the scaffolded controller to expect a String id instead of the default Long id. I also have to change the scaffolded list view to send fundTypeInstance.code instead of fundTypeInstance.id to the show controller, but I suspect that adding a getId() that just returns this.code will fix that.