I have a MS SQL DB (which I can't change) with 3 tables :
contact
id, name, number
c_group
id,name,email
contact_group
id, contact_id, group_id
In Grails I have 3 domain classes :
class Cgroup {
String name
String email
static constraints = {
}
static mapping = {
table name: "c_group"
version false
}
}
class Contact {
String name
String number
static constraints = {
name nullable:false
}
static mapping = {
version false
}
}
class Contact_group {
Cgroup cgroup
Contact contact
static constraints = {
}
static mapping = {
version false
cgroup_id column: "c_group_id", sqlType: "int"
}
}
I'm trying to create a list using :-
def contactInstance = Contact.get(id) //I have previously got the id
List userGroupList = Contact_group.createCriteria().list(params) {
eq("contact", contactInstance)
}
And it is throwing an error
Invalid column name 'cgroup_id'.. Stacktrace follows:
As you can see, I'm trying to map the correct column name (in the Contact_group domain class) as the table has been renamed from group to c_group in the DB, and just to complicate matters for some reason I decided to call the domain class Cgroup in my Grails app. (I can change this if needs be)
So, I'm now in a bit of a muddle about how best to create the list. Any suggestions or pointers?
In your mapping you should point to the class property (cgroup_id should be cgroup, since that is what you named it in your domain.)
Also, your column name in your table is id not c_group_id so you should set the column as id.
static mapping = {
version false
table name: "contact_group"
cgroup column: "id", sqlType: "int"
}
Related
With Grails 3.2.5, hibernate 5.1.2 core.
I have a legacy database that has several clobs in a table. In order to avoid eager fetching, in earlier versions of Grails I defined a domain class that contained only those clobs in order to make them accessed via an (apparent) association which could then be lazily fetched. A sketch of the setup:
class Comment {
String someField // eager
CommentText cmntText // lazy
static mapping = {
id column: 'COMMENT_ID', generator:'sequence', params:[sequence:'cmnt_seq']
}
In a separate domain class file:
class CommentText {
String userComment
static mapping = {
table 'COMMENT'
id generator:'assigned'
userComment sqlType:'clob'
}
As noted, clob column 'user_comment' exists in the single table 'COMMENT'.
In 3.2.5, when doing this I get an error that column 'comment_text_id' is not defined in table 'comment'. This didn't use to be the case, nor should the field have to exist.
On a similar note, in another case I define a composite domain class (a class defined in the same file as the actual domain class). In this case too I get an error about a missing id:
class A {
B b
}
class B {
String someField
}
In this case I get an error saying that field b_id is not in table 'A'. But - it's supposed to be embedded composition, it should not be there.
I'm building within Intellij if that is relevant.
With GORM 6.1 this is now possible with a single domain class
import grails.gorm.hibernate.annotation.ManagedEntity
import static grails.gorm.hibernate.mapping.MappingBuilder.*
#ManagedEntity
class Comment {
String someField
String userComment
static constraints = {
}
static final mapping = orm {
id {
generator("sequence")
params(sequence:'cmnt_seq')
}
userComment = property {
lazy(true)
column {
sqlType 'clob'
}
}
}
}
I need to map Domain classes and subclasses of a legacy database.
The model thath I need to recreate with Grails is to this tables:
Tables structure
CARD_PAYMET and CHEQUE_PAYMENT is subclasses of PAYMENT and share a composite key of two field: OrderId and PaymentId.
I try some ex scenarios, but I can´t arrive to solution. None of then recreate the same model data, and I can´t change this model.
Can any one help me?
Thanks.
Your database looks like a good fit for table-per-subclass inheritance. First, since you're using a composite primary key, your domain classes need to implement the Serializable interface. Then it's a matter of mapping each table column to a property.
import groovy.transform.EqualsAndHashCode
#EqualsAndHashCode(includes=['orderId', 'paymentId'])
class Payment implements Serializable {
int orderId
int paymentId
float amount
static mapping = {
version false
tablePerHierarchy false
id composite: ['orderId', 'paymentId']
orderId column: 'OrderId'
paymentId column: 'PaymentId'
/* Assuming case-insensitive db, so I left out 'Amount'. */
}
}
class CardPayment extends Payment {
String cardType
static mapping = {
version false
cardType column: 'CardType'
}
}
class ChequePayment extends Payment {
int checkNumber
static mapping = {
version false
checkNumber column: 'CheckNumber'
}
}
Note: In this example I used Groovy's EqualsAndHashCode AST transformation to implement Serializable.
With the domain classes in place you'll be able to do GORM polymorphic queries:
def payments = Payment.list() // All Payments (Payment, CardPayment, and ChequePayment).
def cardPayments = CardPayment.list() // Only CardPayments.
...
def nicePayments = Payment.where { amount > 1000 }.list()
I have 3 domain class and I need to define their relationships but I find no luck in defining them correctly. Can somebody rectify my code? I have always get the error unknown column pointing to an id. Please help. Below are my codes. Thank you.
Todo.groovy class
package todoScaff
import userScaff.User
import categoryScaff.Category
class Todo {
String name
String note
Date createDate
Date dueDate
Date completedDate
String priority
String status
User owner
Category category
static belongsTo = [User, Category]
static constraints = {
name(blank:false)
createDate()
priority()
status()
note(maxsize:1000, nullable:true)
completedDate(nullable:true)
dueDate(nullable:true)
}
String toString() {
name
}
}
Category.groovy class
package categoryScaff
import todoScaff.Todo
import userScaff.User
class Category {
String name
String description
User user
static belongsTo = User
static hasMany = [todos: Todo]
static constraints = {
name(blank:false)
}
String toString(){
name
}
}
User.groovy class
package userScaff
import todoScaff.Todo
import categoryScaff.Category
class User {
String userName
String fname
String lname
static hasMany = [todos: Todo, categories: Category]
static constraints = {
userName(blank:false, unique:true)
fname(blank:false)
lname(blank:false)
}
String toString(){
"$lname, $fname"
}
}
Errors
I'm fairly certain your SQL tables are incorrect.
I created a small project with your 3 domain classes and used the grails command schema-export; after removing everything that doesn't relate to the domain relationship, the structure should look like this:
create table category (
id bigint,
user_id bigint,
);
create table todo (
id bigint,
category_id bigint,
owner_id bigint,
);
create table user (
id bigint
);
If your table structure doesn't resemble this, you should either change it to match or use the static mapping = {} closure in the domain class to indicate the proper column names.
Static Mapping
In Todo:
static mapping = {
owner column: 'owner_id' // change owner_id to match your owner column within todo
category column: 'category_id' // change category_id to match your category column within todo
}
In Category:
static mapping = {
owner user: 'user_id' // change user_id to match your user column within category
}
I'm trying to get my head around GORM and relational mapping. The relationships are working fine but there is one problem. I can't seem too ensure that every MailAddress added to MailingList has a unique address. What would be the must efficient way to do this?
Note: There is no unique constraint on MailAddress.address. Identical addresses can exist in the same table.
class MailAddress {
String name
String email
static belongsTo = MailingList
static constraints = {
name blank:true
email email:true, blank:false
}
}
class MailingList {
String name
static hasMany = [addresses:MailAddress]
static mapping = {
addresses cascade: 'all-delete-orphan'
}
static constraints = {
name blank:false
}
}
As mentioned in the comments by #ibaralf the answer is a custom validator.
The MailingList class needed to validate if all addresses (MailAddress) have a unique e-mailaddress.
I added this constraint to the MailingList class and it worked.
static constraints = {
name blank:false
addresses(validator: {
if (!it) {
// validates to TRUE if the collection is empty
// prevents NULL exception
return true
}
// Grab a collection with all e-mailaddresses in the list
def addressCollection = it*.email
// Compare to a collection with only unique addresses
return addressCollection == addressCollection.unique()
})
}
More info can be found here http://grails.org/doc/2.2.0/ref/Constraints/validator.html
There is a unique constraint you can add:
static constraints = {
name blank:true
email email:true, blank:false, unique: true
}
=> put the unique constraint on the email variable (unique: true). This would prevent identical email addresses to be saved in the table.
I have an abstract Event class which holds properties shared by all Event types and beneath it I have two subclasses with one table per class (MasterEvent and ParentEvent).
The problem is that in MasterEvent the id column is of type "number" and in the ParentEvent it is of type "varchar2".
This means that when I try to run the app I get :
Caused by HibernateException: Wrong column type in * for column event_id. Found: number, expected: varchar2(255 char).
Note that this is a legacy database, so there's no chance in changing the column types on the database level.
The sample code below should help in understanding better:
Event.groovy
package a
abstract class Event {
String id
static mapping = {
tablePerHierarchy "false"
id column: "id"
}
}
ParentEvent.groovy
package a
class ParentEvent extends Event {
static mapping = {
id column: "id"
}
}
MasterEvent.groovy
package a
class MasterEvent extends Event {
static mapping = {
id column: "id", type: "number"
}
}
I have tried putting type: number all sorts of combinations, but it's always giving me the same error.
Is there anyway to either cast the sqlType directly to String or have grails ignore this validation?
type: 'number' does not work because it is not a hibernate basic type
Try changing it to type: 'long' (or whatever fits your legacy DB)
Also see the docs: Link
Edit after your comment
I don't know what you are doing in your abstract class, but perhaps you can do something like that and use the getter in your abstract class (not tested!):
class MasterEvent extends Event {
Long longId
static mapping = {
longId column: "id"
}
#Override
String getId() {
longId?.toString()
}
}
Did you try to define id field in subclasses? "String id" in Parent event and "long id" in Master event?
You could try a custom Hibernate UserType which takes the number id column and converts it to a String. This would then go in the MasterEvent class:
static mapping = {
id column: "id", type: NumberToStringType
}