legacy tables: none-id field named id - grails

I am using a legacy database which has a field "usernr" as the primary key and a field "id" holding the user name. I added a mapping to get the usernr setup as the identifying property:
class User {
Long usernr
String id
String password
static mapping = {
id name: 'usernr', generator: 'sequence', params: [sequence: 'usernr']
}
}
But a scaffolding controller is not showing the id field itself. Since "id" seems to be a reserved keyword for mapping, I'm not sure if there is any way to get this setup correctly. Any suggestions?

Related

Grails data won't bind if the id column is renamed

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

Cannot change domain class property value in Grails

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.

grails: primary key from view

I have a requirement to create Employee and Role domain with ids which should be inserted at the time of creation and not manually inserted via code.
Following is attached code for Role.groovy
package pocgrails1
class Role{
String roleId
String roleName
static hasMany = [RoleACL]
static mapping = {id generator: 'assigned',name:"roleId",type:'string'}
static constraints = {
roleName blank:false
roleId blank:false
}
String toString(){
return id
}
}
I am having issues while generating the primary key from the view.
By default the scaffold does not generates the field to insert the primary key value.
I already have looked into several blogs and posts but none were of much help.
What should be the correct approach for this.
Many thanks in advance.
Rohit
Hard to understand what do you want to do. If you want to change primary id key define it as String id and change mapping:
class Role{
String id
static mapping = {
id column: 'role_id', generator: 'assigned'
}
}
When you save object an id will be generated for you and persisted to database, no need to create anything manually

How do I change the name of a Grails domain class id field?

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.

How to change primary key column in grails?

I have a domain class having an Integer variable 'code'. my requirement is to make 'code', primary key column for that domain and also auto increment and to remove the default 'id' column from the table created for that doamin.thnks
use this :
static mapping = {
id name: 'code'
}
more informations here :
http://grails.org/doc/2.0.x/ref/Database%20Mapping/id.html
Domain classes in Grails by default dictate the way they are mapped to the database using sensible defaults. You can customize these with the ORM Mapping DSL.
Customizes the way the identifier for a domain class is generated with id.
See the Grails documentation for id.
static mapping = {
id column: 'code', type: 'integer'
}

Resources