I'm using Grails version 2.4.4 and postgresql. When I run app, I see error message Cannot get property 'myname' on null object. I know that table is not empty and database connected correctly, because I can upload and see data using scaffolding.
domain class code:
class My_table {
//Integer id
String myname
static constraints = {}
}
Controller code:
class My_tableController {
def index() {
def my_table = My_table.list()
[my_table:my_table]
}
My index.gsp file:
<g:select name="name" from="${my_table}"/><br/>
<label>${my_table.myname} </label><br/>
In the form that I see the error happens in this line: <label>${my_table.myname} </label><br/>.
Here you are calling the property name out of context
<g:select name="name" from="${my_table}"/><br/>
<label>${my_table.myname} </label><br/>
You get an error because my_table is a list of My_table instances and does not have a property named name.
A way to fix this problem could be:
<select name="name">
<g:each in="${my_table}" var="table">
<option value="${table.name}">${table.name}</option>
</g:each>
</select>
Also remember label tags are not valid inside a select, as you can read Permitted content Zero or more <option> or <optgroup> elements. in https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select
Table is a reserved word in most databases. Either change the name of the class and property or map them to something else that is not reserved:
static mapping = {
table 'my_table'
}
Adding the column named table to a table named table is problematic because in the static mapping block a method named table already exists. Newer versions of grails have an ORM mapping block that is more flexible.
static mapping = {
table 'my_table'
table column: 'my_table' // unsure if this would work since it overlaps with the other mapping method
}
Related
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
I would like to add new column in my existed table using domain class but it is not happening. if I use create-drop in application.yml file then it works but at the same time I lost my data. I needed to keep the data as well as I needed to add new column by updating domain class property in grails 3.3.0 and SQL Server 2012
package com.alumni
class Student
{
String studentId
String studentName
String age
static constraints = {
}
}
Database is not updating with column address
It's unclear from your code snippet, but it sounds like you're trying to add
String address
Note that columns are made NOT NULL by default, so if data exists in the table, it's attempting to add a non-nullable column, with null data on every row, so the column will fail to create. You need to allow this column to be nullable in the constraints block as well
static constraints = {
address(nullable: true)
}
If you add that, the column should create successfully.
In my Grails 2.5.X app, I have a domain class that looks like this:
class FormData {
String submittedFields
Boolean submitted
static constraints = {
submittedFields nullable: true
}
static mapping = {
// can I do something here to map submitted to a generated
// column of the form_data table
}
}
I would like to map the submitted property to a generated column of the form_data table, i.e. a column that would be created by the SQL statement
alter table form_data add submitted tinyint
GENERATED ALWAYS AS (if(submitted_fields is null,0,1));
Specifically, this generated column should be created when I create the schema from the domain model, e.g. by running the schema-export script.
A consequence of submitted being mapped to a generated column is that the corresponding domain class property should be read-only, or at least, assigning a value to it should have no effect.
If you want to handle the value of the column on database side only, and dont want it to be inserted or updated from grails/hibernate side. you can make the column as insertable:false updatetable:false
static mapping = {
submitted insertable:false, updateable:false
}
Now, even if the value is changed in grails, the new value will not be updated in database.
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 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.