Mapping a generated database column to a Grails domain class property - grails

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.

Related

How can I update my existing database table using domain class in grails 3.3.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.

Retrieving value from database in Grails

I have one column named invoice_number in my database.
class Customer {
String name
int invoiceNumber
static constraints = {
name(blank: false)
invoiceNumber(unique: true)
}
}
There is no field for invoice_number in index.gsp file.
<g:form controller="customer">
Name:<br>
<g:textField name="name"></g:textField><br>
<g:actionSubmit value="Submit" action="Save"></g:actionSubmit><br>
</g:form>
I want to generate one invoice number and increase it with a difference of 5. For example, when first customer submits form, the invoice number may be generated as 105. When the second customer submits form, the invoice number should be 110. They should be saved in a database and they must be unique.
Then, I want to retrieve the invoice number from database for the customer who submits the form, and then, pass that invoice number to another gsp file.
How can I do it?
You need to add the logic of generating / incrementing the invoiceNumber in controller's action to which you are giving call on form submit.
Might get you started on your way (but I still don't get the 1 invoice per customer part, or incrementing by 5).
Yes, as mentioned by Abhinandan, you can put logic of ID creation in the controller, but a more reusable route might be to create a custom key generator, and point your class to use this generator for record IDs.
Suppose we have:
package tester2
class Custo {
String id // string just to show it can be anything
String name
// tell GORM about your id attribute, and setup your 'id' column
// to use a custom id generator function
static mapping = {
id column:"id", generator:"tester2.CustomIdGenerator"
}
}
then in src/groovy/tester2/CustomIdGenerator.groovy
package tester2
import org.hibernate.id.IdentifierGenerator
import org.hibernate.engine.spi.SessionImplementor
class CustomIdGenerator implements IdentifierGenerator {
public synchronized Serializable generate(SessionImplementor session, Object obj) {
// here's where you would have to access some persistent store to
// remember your last generated ID, fetch the last value, add 5,
// and store that new value to be ready for the next call
// -- don't know what DBMS you intend to use, but some have
// sequence support that will let you tinker with the starting
// point and increment. Maybe it's a simple as setting up a
// customized sequence and asking for nextVal()
//
// for this example, I just use a random UUID
return UUID.randomUUID().toString()
}
}

GORM and Composite Keys in associations

I have legacy database and some tables have composite ids
class Client {
String id
static hasMany = [
settings: Setting
]
static mapping = {
id column: 'client_id', generator: 'assigned'
}
}
class Setting {
Client client
String nodeId
String ccyPairPattern
Character qualifier
static mapping = {
id composite: ['client', 'nodeId', 'pattern', 'qualifier']
}
}
I want to delete entry from GORM association:
client.get('1').removeFromSettings(settingToRemove)
// settingToRemove.delete(flush: true)
// delete-orphans does not help
This always raises exception after flush
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) :
This happens because method removeFrom* sets client property to null and generates query to delete with clientId = null as client is part of composite key
What is the best solution in this case. Looks like GORM has poor support for composite keys or my mapping is incorrect.
When you use hasMany without a belongsTo on the many side, in other words a unidirectional association, you get a join table. For example...
class PurchaseOrder {
static hasMany = [items: Item]
}
class Item { }
Would yield three database tables: purchase_order, item, and purchase_order_items. The purchase_order_items table would contain two columns: purchase_order_id and item_id. You can read more about join tables here.
Since you're dealing with a legacy database, I think the best solution is not to use addTo*() and removeFrom*().
Ensure you don't have a join table. If you have a join table, remove the hasMany association.
You'll need to add/remove Setting instances manually.
Example:
def client = Client.get(1)
// Adding a setting
def setting = new Setting(client: client, nodeId: blah...)
setting.save()
// Removing a setting
/*
The prototype is used to search for a domain instance with a composite primary key.
So simply set the composite key properties accordingly.
*/
def prototype = new Setting(client: client, nodeId: blah...)
def setting = Setting.get(prototype)
setting.delete()
Lacking a hasMany association, you won't be able to access a client's settings via the client.settings property. Instead you'd have to query for them like this:
def settings = Setting.findByClient(client)
A consequence of using a legacy database is that if the database doesn't align with that GORM/Hibernate expects it will be limited in what it can do for you.

grails validation when fetching rows

Is it possible to fetch a default value in grails if a column is null? If I were to represent following query via grails domain object then how could I achieve it:
SELECT IFNULL(empsalary,0.00) from Employee;
Domain object:
class Employee{
Integer id,
Float empsalary
static constraints = {
id unique: true, blank:false
empsalary nullable:true
}
}
making empsalary nullable false isn't an option due to existing data
validator on empsalary seems to work when inserting rows but not while data fetch
we can consider writing say getEmpSalary() method on domain and perform check there but there are several other fields we need to do this so trying to avoid massive code changes
If you want a default value to come out of the database without having to code anything into your classes, I suggest you update every row where it is null and set it to 0 in the database. If data is getting inserted from another application and that application is allowing a null value, put a 'DEFAULT 0' on your database column.
Grails also offers an "afterLoad" event which is run when a domain object gets loaded from the database. See the documentation here: http://grails.org/doc/2.3.7/guide/GORM.html.
I think you can do this with HQL:
def salary = Employee.executeQuery('SELECT COALESCE(empsalary, 0.0) FROM Employee')[0]
See this SO Question.
Please try setting Float empsalary = 0.0 in your domain object.

Modify params before saving domain object

I needed a domain class that held a list of Strings. It seems fairly well-known that GORM can't handle this, so I've worked around it. At first I tried using getters and setters in the domain class, but that caused problems. Then I found on Stack Overflow a way to use afterLoad() and beforeValidate() to rewrite properties as shown below. This has worked well to allow me to turn the List into a String for persistence and back to a List for use in the app.
class Entries {
// persisted to database
String _entry
// exposed to app
List entry
static transients = ['entry'] //don't try to persist the List
def afterLoad() {
// split the String from the database into a List
entry = _entry?.split('\\|')
}
def beforeValidate() {
// join the List into a String for persisting
_entry = entry.join('|')
}
static constraints = {
_entry maxSize:4000
}
}
This works fine programmatically. The only problem is that the Grails scaffolding can't deal with this, even if I try to enter a pipe-delimited string. I understand the reason why is that the scaffolding creates a form field for _entry, so entry is null when it tries to save the object. And beforeValidate() relies on a List of Strings to work.
I tried to get around this in the controller, by setting params.entry = params._entry, prior to the call to new Entries(params). [I recognize that this is not a perfect solution, but this was my first pass at getting the form working.] And then I added a test in beforeValidate() to set entry = _entry if entry was null. Basically:
EntriesController.groovy:
params.entry = params._entry // I added this line
def entriesInstance = new Entries(params)
Entries.groovy:
def beforeValidate() {
if( entry == null ) entry = _entry // I added this line
_entry = entry.join('|')
}
I thought that would allow me to enter pipe-delimited strings into the scaffolded Create Entries form and get something into the database.
To my surprise, though, I found that both entry and _entry were null in beforeValidate(), even though I printed and verified that params contained both keys in the controller. I don't understand why this happens. How did my adding a new key to params result in nulls arriving in the domain class?
The follow-up question is, of course, what's the right way to make the scaffolded Create Entries form accept a pipe-delimited String that makes it into the database?
I needed a domain class that held a list of Strings. It seems fairly well-known that GORM can't handle this, so I've worked around it.
I don't agree with you here
class Xyz {
static hasMany = [entries: String]
}
Should create a seperate table to hold your list of strings (It will actually be a Set). Here are the docs

Resources