The domain class 'Message' displayed below:
String name
String age
User user
From the following code, i retrieve all the messages that was posted. But what i really want is to display is all messages of a particular user. For example : user.firstName. How can i do it?
def messages = Message.listOrderByDate(order: 'asc', max:1000)
println messages.firstName + " BOOOOOOO "
[messages:messages.reverse()]
So instead of displaying all the data i want to filter the data based on users name. How can this be done ?
It could be queried in the following way:
def results = Message.withCriteria {
user {
eq("name", theUserNameYouWantToQuery)
}
order("date", "asc")
maxResults(1000)
}
This example could be extended to grouping etc. See http://grails.org/doc/latest/ref/Domain%20Classes/createCriteria.html
create as hasMany relationship from user to Message like this:
class User {
static hasMany = [messages: Message]
}
class Message{
String name
String age
static belongsTo [user: User]
}
and then use myUser.messages to fetch all Messages from this user.
Related
So I'm currently building a Grails application, and I am trying to allow users to create posts, and those posts have comments as well.
The association I am trying to create is something like:
A User can have 0 to many Posts, and
Posts can have 0 to many Comments
I tried to set this up with my domain classes as the following:
class User implements Serializable{
static hasMany = [created_posts: Post]
String username
String password
}
class Post {
static hasMany = [comments: Comment]
String description
}
class Comment {
String comment_body
static belongsTo = [post: Post]
}
I tried playing with the mappedBy property as well, like the following:
class User implements Serializable{
static hasMany = [created_posts: Post]
static mappedBy = [created_posts: 'creator']
String username
String password
}
class Post {
static hasMany = [comments: Comment]
static belongsTo = [creator: User]
String description
}
class Comment {
String comment_body
static belongsTo = [post: Post]
}
But it would still not work. The code I am using to create this is the following:
Post new_post = new Post(description: "testing").save()
User user = User.get(springSecurityService.principal.id)
user.addToCreated_posts(new_post).save()
Unfortunately, Grails cannot seem to execute the last line, and sometimes it would not compile at all, giving a :bootRun error.
I am unsure of how to create the proper associations between the Users, Posts, and Comments. Any help is welcome and greatly appreciated!
EDIT: my current code: (compiles, but does not save to user's set)
class User implements Serializable{
static hasMany = [createdPosts: Post]
static mappedBy = [createdPosts: 'creator']
String username
String password
}
class Post {
static hasMany = [comments: Comment]
static belongsTo = [creator: User]
String description
static constraints = { creator nullable: true }
}
class Comment {
String comment_body
static belongsTo = [post: Post]
}
The code I am using in my controller:
User user = User.get(springSecurityService.principal.id)
Post new_post = new Post(description: "testing description", creator: user).save()
user.save()
System.out.println("post saved to user? " + user.getCreatedPosts())
Output is:
post saved to user? []
The issue is more than likely the fact that the save() function does not return the object that was saved.
Initially, I changed your code to:
Post new_post = new Post(description: "testing")
new_post.save()
User user = User.get(springSecurityService.principal.id)
user.addToCreated_posts(new_post).save()
Which did compile. But, there is one issue remaining - instead of running user.addToCreated_posts, you'll have to set the property creator of new_post, since you did not explicitly specify that it can be null when you created your Post domain class. Therefore, the call to save() will silently fail.
If you change the code to:
User user = User.get(springSecurityService.principal.id)
Post new_post = new Post(description: "testing", creator: user)
new_post.save()
Your code should work.
It is also probably a good idea to replace all of your calls to save() with save(failOnError: true), so that when a save fails, an exception is thrown. You can catch this exception, and extract error messages from the exception. Usually, the error messages are related to failed validations for the domain object.
This was tested on Grails 3.2.7, using H2 (the default, in-memory database packaged with Grails).
EDIT Here's an updated snippet based on the update that was made to the question.
You are running:
User user = User.get(springSecurityService.principal.id)
Post new_post = new Post(description: "testing description", creator: user).save()
user.save()
System.out.println("post saved to user? " + user.getCreatedPosts())
I would change this to:
User user = User.get(springSecurityService.principal.id)
Post new_post = new Post(description: "testing description", creator: user)
new_post.save(failOnError: true)
System.out.println("post saved to user? " + user.getCreatedPosts())
The reason I would do this is because you aren't saving the user - you are saving the post. When you get created posts for a user, Grails searches the database for posts where the creator_id column is set to the id of the user you called the method from.
Also, I would make sure that you did not remove the following line from User.groovy:
static hasMany = [createdPosts: Post]
static mappedBy = [createdPosts: "creator"]
You will absolutely need the following in Post.groovy:
static belongsTo = [creator: User]
This way, Grails knows how it is supposed to look up Posts for a user.
Once you make these changes, make sure you start with a fresh DB - for H2 in memory, this should be done automatically, so long as you are using the development environment - and restart your server.
I'm breaking my head into wall about this logic, I have 2 domains, User and RSS. When the user add a RSS, I have to compare if it's not duplicated URL in my db by given another URL in the same user.
class RSS {
Long id
String link
static belongsTo = [user:User]
}
class User {
Long id
Long uid //facebook
String name
static hasMany = [rss:RSS]
}
def addRSS(){
//logic url is valid or not
...
def user = User.findByUid(data.id) //get User uid and then by this uid, i can get the all RSS url
//and compare like if(db_url == given_url) ...
}
I tried many ways and I had no success.
You could also use one of the findOrSaveWhere or findOrCreateWhere methods
def url = 'some url from user' //data.url I would assume
def user = User.findByUid(data.id)
RSS.findOrSaveWhere(url: url, user: user)
If it's in the db it will fetch it for you if not it will create it for you. The documentation explains the difference between the *Save* and *Create*
Simple join would do I think:
def existing = RSS.withCriteria{
eq 'link', url
eq 'user.id', userId
}
or
def existing = RSS.withCriteria{
eq 'link', url
user{ eq 'uid', uidd }
}
if( existing ) return
else doSave()
I have a User class which has a List field namely pt. This field is not initialized when User register his account. But when user goes this controller action :
def updatePt() {
//performs some action
def user = User.get(springSecurityService.principal.id) //find the user
user.pt = []
//on certain conditions i put values into user.pt like this
user.pt << "E"
//at last I save it
user.save()
}
But using user/show action via scaffolding I found that pt field is not saved on users object. Where I'm making a mistake?
You have to provide a static mapping in the Users domain class so that Grails knows the field must be persisted:
class User {
static hasMany = [pt: String]
}
It's possible because of validation error. Try with
if (!user.save()) {
log.error('User not saved')
user.errors.each {
log.error('User error: $it')
}
}
PS or you can use println instead of log.error
I have a grails application that has a service that creates reports. The report is defined as:
class Report {
Date createDate
String reportType
List contents
static constraints = {
}
}
The service generates a report and populates contents as a list that is returned by createCriteria.
My problem is that my service claims to be saving the Report, no errors turn up, logging says that its all there, but when I go to call show from the controller on that report, it says contents is null.
Another relevant bit, my Service is called by an ActiveMQ message queue. The message originating from my report controller.
Controller:
class ReportController {
def scaffold = Report
def show = {
def rep = Report.get(params.id)
log.info("Report is " + (rep? "not null" : "null")) //says report is not null
log.info("Report content is " + (rep.contents? "not null" : "null")) //always says report.contents is null.
redirect(action: rep.reportType, model: [results: rep.contents, resultsTotal: rep.contents.size()])
}
}
My service that creates the report:
class ReportService {
static transactional = false
static expose = ['jms']
static destination = "Report"
void onMessage(msg)
{
this."$msg.reportType"(msg)
}
void totalQuery(msg)
{
def results = Result.createCriteria().list {
//This returns exactly what i need.
}
Report.withTransaction() {
def rep = new Report(createDate: new Date(), reportType: "totalQuery", contents: results)
log.info("Validation results: ${rep.validate()}")
if( !rep.save(flush: true) ) {
rep.errors.each {
log.error(it)
}
}
}
}
Is there something obvious that I'm missing here? My thought is that since all my unit tests work, that the hibernate context is not being passed through the message queue. But that would generate Exceptions wouldn't it? I've been beating my head on this problem for days, so a point in the right direction would be great.
Thanks,
You can't define an arbitrary List like that, so it's getting ignored and treated as transient. You'd get the same behavior if you had a def name field, since in both cases Hibernate doesn't know the data type, so it has no idea how to map it to the database.
If you want to refer to a collection of Results, then you need a hasMany:
class Report {
Date createDate
String reportType
static hasMany = [contents: Result]
}
If you need the ordered list, then also add in a List field with the same name, and instead of creating a Set (the default), it will be a List:
class Report {
Date createDate
String reportType
List contents
static hasMany = [contents: Result]
}
Your unit tests work because you're not accessing a database or using Hibernate. I think it's best to always integration test domain classes so you at least use the in-memory database, and mock the domain classes when testing controllers, services, etc.
I'm having an issue with grails. I have a domain that looks like:
class Book {
static belongsTo = Author
String toString() { title }
Author bookAuthor
String title
String currentPage
static constraints = {
bookAuthor()
title(unique:true)
currentPage()
}
}
The main thing to note is that I have title(unique:true) to avoid from adding the same book twice. However, this is causing issues. In the controller I have created:
def populate = {
def bookInstance = new Book()
def dir = 'C:/currentBooks.txt'
def bookList
bookList = readFile(dir) //read file and push values into bookList
int numOfBooks = bookList.size()
numOfBooks.times {
bookInstance.setBookAuthor(bookList.author[it])
bookInstance.setTitle(bookList.title[it])
bookInstance.setCurrentPage(bookList.title[it])
bookInstance.save()
}
}
I call populate to read a file and populate the database with new Books. The problem is that I want to update it with new values. For instance, lets say that the book already exists in the database but I have read farther into the book and want to change the currentPage so the data is changed in the file and populate is called but doesn't update the page because the title already exists.
Can someone explain how to update the results with the new values?
First of all, you need a key for your Book domain object. You have the title marked as unique, which suggests you want to use that to uniquely identify a Book. I'd recommend against that (what happens when two books have the same title?) and use the id grails provides by default. That means you'll have to store the id in your currentBooks.txt in addition to your other fields.
Once you've got an id, you can try loading an existing record from the database. If not, create a new one. For Example:
def dir = 'C:/currentBooks.txt'
def bookList
bookList = readFile(dir) //read file and push values into bookList
int numOfBooks = bookList.size()
numOfBooks.times {
def bookInstance.get(bookList.id[it])
if (!bookInstance) {
bookInstance = new Book()
}
bookInstance.setBookAuthor(bookList.author[it])
bookInstance.setTitle(bookList.title[it])
bookInstance.setCurrentPage(bookList.title[it])
bookInstance.save()
}
Alternatively, you could use the title as the id. This is a bad idea as indicated above, but it saves having to keep track of a separate id and change the format of currentBooks.txt. With Book defined as below, you could call Book.get(bookList.title[it]):
class Book {
static belongsTo = Author
String toString() { title }
Author bookAuthor
String title
String currentPage
static constraints = {
bookAuthor()
title(unique:true)
currentPage()
}
static mapping = {
id name: 'title', generator: 'assigned'
}
}