I'd like to know which is better to use, a database view or simply persist fields in the database.
For instance, I'm initializing win_count in the following view vw_stats:
...
CASE
WHEN game.in_decision = true AND game.user_score > game.opponent_score THEN 1
ELSE 0
END AS win_count,
...
Which is then mapped to a domain class:
package com.x
class Stat {
//fields here...
static mapping = {
table 'vw_stats'
version false
}
}
Or, should I persist the field winCount in the database using this domain class and manipulate it before saving?
package com.x
class Game {
//fields here...
Short winCount = 0
static constraints = {
//constraints here...
winCount nullable: false, range: 0..99
}
def beforeInsert(){
this.beforeUpdate()
}
def beforeUpdate(){
//other manipulations here...
if inDecision and userScore > opponentScore
winCount = 1
}
}
The issue with the view I find, is that it will generate a table when running the application and then I have to manually remove the table and run the code to generate the view.
Update #1
There might be an IO cost saving by persisting them in the database instead of the view?
Update #2
Forgot to mention, I should be able to apply an aggregate function on the resulting field in a service.
A third approach is to use a derived property. Like the view, the value is calculated on the fly.
package com.x
class Game {
//fields here...
Short winCount = 0
static constraints = {
//constraints here...
winCount nullable: false, range: 0..99
}
static mapping = {
winCount formula: 'CASE WHEN in_decision = true AND user_score > opponent_score THEN 1 ELSE 0 END'
}
}
Not fully understanding the exact application you are trying to achieve, have you thought about using transients and letting the domain instance calculate it when it is actually needed? This will avoid pre-calculating data that may not even be used.
Related
For example I need to retrieve several registers in a table, and edit a field, but it takes too long to save all with a loop, does exist a better way to save?
This how I do it....
class Table
static mapping = {
table "TABLEEX"
id generator:'sequence', params:[sequence:'TABLEEX_SEQ']
}
// identificacion
String data1
String data2
}
And searching the data:
def stuff = Table.createCriteria().list{
eq("data1","1")
}
And editing and saving
stuff.each {
it.data2 = "aaa"
it.save()
}
It isn't clear why you are retrieving the objects to begin with. Is something like this what you are looking for?
Table.executeUpdate("update Table t set t.data2=:newData where t.data1=:oldData", [newData: 'BAR', oldData: 'FOO'])
EDIT
You could also do something like this...
def query = Table.where {
data1 == 'FOO'
}
int total = query.updateAll(data2:'BAR')
Hibernate (the underlying mechanism of gorm, the grails orm) does not support that.
You'll have to iterate over every element and save or implement it yourself (and that will not make it faster).
I'm using data binding with parent/child relationships in Grails 2.3.7 and am having trouble with deletes. The form has many optional children, and to keep the database tidy I'd like to purge blank (null) values. I've found some nice articles which suggest using removeAll to filter my entries but I can't get remove or removeAll to work!
For example... (Parent has 10 children, 5 are blank)
def update(Parent parent) {
parent.children.getClass() // returns org.hibernate.collection.PersistentSet
parent.children.size() // returns 10
parent.children.findAll{ it.value == null }.size() // returns 5
parent.children.removeAll{ it.value == null } // returns TRUE
parent.children.size() // Still returns 10!!!
}
I've read PersistentSet is finicky about equals() and hashCode() being implemented manually, which I've done in every domain class. What baffles me is how removeAll can return true, indicating the Collection has changed, yet it hasn't. I've been stuck on this for a couple days now so any tips would be appreciated. Thanks.
Update:
I've been experimenting with the Child hashcode and that seems to be the culprit. If I make a bare-bones hashcode based on the id (bad practice) then removeAll works, but if I include the value it stops working again. For example...
// Sample 1: Works with removeAll
int hashCode() {
int hash1 = id.hashCode()
return hash1
}
// Sample 2: Doesn't work with removeAll
int hashCode() {
int hash1 = id.hashCode()
int hash2 = value == null ? 0 : value.hashCode()
return hash1 + hash2
}
// Sample Domain classes (thanks Burt)
class Parent {
static hasMany = [children: Child]
}
class Child {
String name
String value
static constraints = {
value nullable: true
}
}
This behavior is explained by the data binding step updating data, making it dirty. (ie: child.value.isDirty() == true) Here's how I understand it.
First Grails data binding fetches the Parent and children, and the hashcode of each Child is calculated. Next, data updates are applied which makes child.value dirty (if it changed) but the Set's hashcodes remain unchanged. When removeAll finds a match it builds a hashCode with the dirty data, but that hashcode is NOT found in the Set so it can't remove it. Essentially removeAll will only work if ALL of my hashCode variables are clean.
So if the data must be clean to remove it, one solution is to save it twice. Like this...
// Parent Controller
def update(Parent parent) {
parent.children.removeAll{ it.value == null } // Removes CLEAN children with no value
parent.save(flush:true)
parent.refresh() // parent.children is now clean
parent.children.removeAll{ it.value == null } // Removes (formerly dirty) children
parent.save(flush:true) // Success!
}
This works though it's not ideal. First I must allow null values in the database, though they only exist briefly and I don't want them. And second it's kinda inefficient to do two saves. Surely there must be a better way?
hashCode and equals weirdness aren't an issue here - there are no contains calls or something similar that would use the hashCode value and potentially miss the actual data. If you look at the implementation of removeAll you can see that it uses an Iterator to call your closure on every instance and remove any where the closure result is truthy, and return true if at least one was removed. Using this Parent class
class Parent {
static hasMany = [children: Child]
}
and this Child
class Child {
String name
String value
static constraints = {
value nullable: true
}
}
and this code to create test instances:
def parent = new Parent()
5.times {
parent.addToChildren(name: 'c' + it)
}
5.times {
parent.addToChildren(name: 'c2' + it, value: 'asd')
}
parent.save()
it prints 5 for the final size(). So there's probably something else affecting this. You shouldn't have to, but you can create your own removeAll that does the same thing, and if you throw in some println calls you might figure out what's up:
boolean removeAll(collection, Closure remove) {
boolean atLeastOne = false
Iterator iter = collection.iterator()
while (iter.hasNext()) {
def c = iter.next()
if (remove(c)) {
iter.remove()
atLeastOne = true
}
}
atLeastOne
}
Invoke this as
println removeAll(parent.children) { it.value == null }
I have to keep track of sequential submissions per fiscal year. Given the fiscal year '2015' then the numbering should be '2015001, 2015002, 2015003, etc'.
I have defined a domain class to keep track of these settings:
class MYAPPConfig {
String fiscalYear
Integer requestCount
static constraints = {
fiscalYear (size: 4..4, nullable: false)
requestCount(max: 999, nullable: false)
}
}
The idea is that for a new fiscal year I will add a new record and the 'requestCount' will be reset to 0 (or 1 depending on how Grails wants to manage it).
Ideally this field should be mapped to an Oracle sequence field. If that's not possible then should I manage the increment logic in a service method?
My Grails version is 2.4.2
Thanks.
I figured it out.
I took a different route (after more googling). I added dateCreated to my model which is managed by Grails so its updated automatically. So all I needed to do is get the record with the latest date (every year we will add a new record for the coming fiscal year). Take note of the [0] at the end of the call, that flattens the array of arrays returned and allows me to deal with a single object.
My model now looks like this ( MYAPPConfig.groovy ) :
class MYAPPConfig {
String fiscalYear
Integer requestCount
Date dateCreated
static constraints = {
fiscalYear (size: 4..4, nullable: false)
requestCount(max: 999, nullable: false)
}
}
I created the following service ( ManageRequestsService.groovy )
import grails.transaction.Transactional
#Transactional
class ManageRequestService {
def getNextTrackingId() {
def latestConfig = MYAPPConfig.listOrderByDateCreated(max:1, order: "desc")[0]
def latestFiscal = latestConfig.fiscalYear
Integer sequence = latestConfig.requestCount
sequence++
latestConfig.requestCount = sequence
latestConfig.save(flush:true)
return latestFiscal + sprintf('%03d', sequence)
}
}
And in my controller ( MyTestController.groovy ) I have:
class MyTestController {
def manageRequestsService
def test() {
def trackingId = manageRequestsService.getNextTrackingId()
render "Next id is: ${trackingId}"
}
}
Giving the following output ( http://localhost:8080/MYAPP/myTest/test ):
Next id is: 2015001
Refresh page!
Next id is: 2015002
Refresh again!
Next id is: 2015003
I've read through a lot of posts and docs and must be missing something.
In my application (model below) I am having a data issue that seems to be out of my control where I have a categoryId in the join table JOBORDERCATEGORIES that has no corresponding row in the CATEGORY table. I am accessing the category data through getJobCategories() in the JobOrder. This is producing the following error when my Category table is missing a referenced row:
2012-03-07 08:02:10,223 [quartzScheduler_Worker-1] ERROR listeners.SessionBinderJobListener - Cannot flush Hibernate Sesssion, error will be ignored
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.matrixres.domain.Category#416191]
and my code is halting.
I have tried using ignoreNotFound but it is not helping me to get past the error above.
If I missed a post on the solution to this problem please link me to it otherwise thoughts are welcome on how to move forward. Perhaps there is a more direct route I will have to hammer in to achieve my goal of getting a good category list, but I am not familiar enough with the framework to know what is next. As a note, I cannot write to any of these tables.
thanks, rich
A simplified Version of my Model:
Job Order Object:
class JobOrder {
def getJobCategories() {
def cats = []
try {
def jocategories = this.categories
if(!jocategories.isEmpty() && jocategories!=null){
println "we got categories for ${this.id}"
jocategories.each { cat ->
if(cat?.parentCategoryID == 0){
if(cat.occupation != null){
cats << cat.occupation
} else {
cats << cat.name
}
}
}
}
} catch(e) {
cats << "Other Area(s)"
}
cats
}
static mapping = {
table 'dbo.JOBORDER'
version false
id generator: 'identity', column: 'JOBORDERID'
/*
* several other mapped columns deleted here
*/
categories joinTable:[name:'jobOrderCategories', column: 'categoryId', key:'jobOrderID']
}
/*
* several properties deleted here
*/
static hasMany = [categories: Category] //several other hasMany associations exist
}
Category Object:
class Category {
static mapping = {
table 'CATEGORY'
version false
id generator: 'identity', column: 'categoryID'
occupation column: 'OCCUPATION'
name column: 'NAME'
parentCategoryID column: 'PARENTCATEGORYID'
/*
* several other mapped columns deleted here
*/
jobOrders joinTable:[name:'jobOrderCategories', column: 'jobOrderID', key:'categoryId']
}
String name
String occupation
int parentCategoryID
/*
* several properties deleted here
*/
static belongsTo = [JobOrder]
static hasMany = [jobOrders:JobOrder]
}
Join Table:
class JobOrderCategories {
static mapping = {
table 'JOBORDERCATEGORIES'
version false
isDeleted column: 'ISDELETED'
jobOrderID column: 'JOBORDERID'
categoryId column: 'CATEGORYID'
}
Boolean isDeleted
Integer jobOrderID
Integer categoryId
}
These kinds of situations aren't the most fun, but I have had to deal with this kind of Roll-Your-Own ORM problems before ;) Basically what you're going to want to do here is store the object properties not typed as Object references, but as ints, and while you'll lose some of the dynamic finder things GORM makes so nifty, you'll have a fairly straight-forward means of accessing the data that doesn't involve tangling yourself up with Hibernate's innards.
Basically, this will involve ditching your hasMany and belongsTo properties on JobOrder and Category. Instead, you'll want to do things like
def myJobOrder = JobOrder.get(yourId);
def myCategoryIds = JobOrderCategories.findAllByJobOrderID(myJobOrder.id)
def myCategories = Categories.withCriteria {
in('id', myCategoryIds)
}
You can put variations of those traversals in helper methods on your classes, like for example, your getJobCategories method could become
class JobOrder {
//...
def getJobCategories() {
def myCategoryIds = JobOrderCategories.findAllByJobOrderID(this.id)
def myCategories = Categories.withCriteria {
in('id', myCategoryIds)
}
}
}
And so on. This is definitely not the prettiest thing in the world to deal with, and you lose your ability to traverse through things easily with GORM (ex a
jobOrder.withCriteria {
categories {
eq('name', blah)
}
}
becomes a executeQuery type of situation.)
But overall, its not too bad to deal with :)
Hope that helps!
My problem is simple but I could not find any GORM syntax for this.
Consider the following class:
class Article {
String text
static hasMany = [tags: String]
static constraints= {
tags(unique: true) //NOT WORKING
}
}
I want to have one unique tag name per article defined in my constraints but I cannot make it with the above syntax.
Clearly I need in DB schema something like:
create table article_tags (article_id bigint, tags_string varchar(255), unique (article_id , tags_string))
How can I do that?
PS: I am also stuck for setting constraints on tag minimum and maximum size
FYI, you can also use a custom validator in domain classes:
static constraints = {
tags(validator: {
def valid = tags == tags.unique()
if (!valid) errors.rejectValue(
"tags", "i18n.message.code", "default message")
return valid
})
At the database level, you can customize DDL generation by having the following code in grails-app/conf/hibernate/hibernate.cfg.xml:
<hibernate-mapping>
<database-object>
<create>
ALTER TABLE article_tags
ADD CONSTRAINT article_tags_unique_constraint
UNIQUE(article_id, tags_string);
</create>
<drop>
ALTER TABLE article_tags
DROP CONSTRAINT article_tags_unique_constraint;
</drop>
</database-object>
</hibernate-mapping>
Initially I looked at the joinTable mapping to see if it would support a unique key, but it won't.
The best solution I can think of is the following combination:
Manually run the SQL statement to add the unique constraint. If you have some sort of database management tool (e.g. Liquibase), that would be the ideal place.
Explicitly declare the association as a Set. This should avoid Hibernate ever running into the unique constraint, anyway.
class Article {
static hasMany = [tags: String]
Set<String> tags = new HashSet<String>()
}
An alternate solution would be to explicitly declare your child domain (Tag) and set up a many-to-many relationship, adding the unique key to the join table there using constraints. But that's not really a great solution, either. Here's a primitive example:
class Article {
static hasMany = [articleTags: ArticleTag]
}
class Tag {
static hasMany = [articleTags: ArticleTag]
}
class ArticleTag {
Article article
Tag tag
static constraints = {
tag(unique: article)
}
}
With this, though, you have to explicitly manage the many-to-many relationship in your code. It's a bit inconvenient, but it gives you full control over the relationship as a whole. You can find out the nitty gritty details here (the Membership class in the linked example is akin to the ArticleTag in mine).
Perhaps one of the gurus more familiar with GORM will chime in with a more elegant solution, but I can't find anything in the docs.
EDIT: Note that this approach does not consider a unique(article_id , tags_id) constraint. It also raises an issue with two Articles having the same tags. - Sorry.
While this is not officially documented (see the relevant parts of the Grails Reference Documentation here and here) constraints on one-to-many associations are simply ignored by GORM. This includes unique and nullable constraints, and probably any.
This can be proved by setting dbCreate="create" and next, by looking at the database schema definition. For your Article sample and the PostgreSQL database, this would be:
CREATE TABLE article_tags
(
article_id bigint NOT NULL,
tags_string character varying(255),
CONSTRAINT fkd626473e45ef9ffb FOREIGN KEY (article_id)
REFERENCES article (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT article0_tags_article0_id_key UNIQUE (article_id)
)
WITH (
OIDS=FALSE
);
As can be seen above, there are no constraints for the tags_string column.
In contrast to constraints on association fields, constraints on "normal" instance fields of domain classes do work as expected.
Thus, we'll want to have some kind of Tag, or TagHolder, domain class, and we'd need to find a pattern that still provides the Article with a clean public API.
First, we're introducing the TagHolder domain class:
class TagHolder {
String tag
static constraints = {
tag(unique:true, nullable:false,
blank:false, size:2..255)
}
}
and associate it with the Article class:
class Article {
String text
static hasMany = [tagHolders: TagHolder]
}
In order to provide a clean public API, we're adding the methods String[] getTags(), void setTags(String[]. That way, we can also call the constructor with named parameters, like, new Article(text: "text", tags: ["foo", "bar"]). We're also adding the addToTags(String) closure, which mimicks GORM's corresponding "magic method".
class Article {
String text
static hasMany = [tagHolders: TagHolder]
String[] getTags() {
tagHolders*.tag
}
void setTags(String[] tags) {
tagHolders = tags.collect { new TagHolder(tag: it) }
}
{
this.metaClass.addToTags = { String tag ->
tagHolders = tagHolders ?: []
tagHolders << new TagHolder(tag: tag)
}
}
}
It's a workaround, but there's not too much coding involved.
A drawback, we're getting an additional JOIN table. Nevertheless, this pattern allows for applying any available constraints.
Finally, a test case could look like this one:
class ArticleTests extends GroovyTestCase {
void testUniqueTags_ShouldFail() {
shouldFail {
def tags = ["foo", "foo"] // tags not unique
def article = new Article(text: "text", tags: tags)
assert ! article.validate()
article.save()
}
}
void testUniqueTags() {
def tags = ["foo", "bar"]
def article = new Article(text: "text", tags: tags)
assert article.validate()
article.save()
assert article.tags.size() == 2
assert TagHolder.list().size() == 2
}
void testTagSize_ShouldFail() {
shouldFail {
def tags = ["f", "b"] // tags too small
def article = new Article(text: "text", tags: tags)
assert ! article.validate()
article.save()
}
}
void testTagSize() {
def tags = ["foo", "bar"]
def article = new Article(text: "text", tags: tags)
assert article.validate()
article.save()
assert article.tags.size() == 2
assert TagHolder.list().size() == 2
}
void testAddTo() {
def article = new Article(text: "text")
article.addToTags("foo")
article.addToTags("bar")
assert article.validate()
article.save()
assert article.tags.size() == 2
assert TagHolder.list().size() == 2
}
}
Try this:
http://johnrellis.blogspot.com/2009/09/grails-constraints-across-relationships.html
The only way I've found to do this is to write a custom constraint and do a database check for the duplication. I don't think there is a built-in way to use a GORM constraint to accomplish this.