I m new user in Grails. I don't know how to write projection query. Here is my code. please anyone help me for grails projection query.
From Join table I want to find username which is consist in user table
Given the following example domains:
class User{
transient springSecurityService
String username
String password
boolean enabled
boolean accountExpired
boolean accountLocked
boolean passwordExpired
static mapping = {
table 't04t001'
id column: 'f_account_id'
username column: 'f_username', length: 10
password column: 'f_password', length: 100
enabled column: 'f_account_active'
accountExpired column: 'f_account_expired'
accountLocked column: 'f_account_locked'
passwordExpired column: 'f_password_expired'
version column: 'f_revision'
}
}
class Role{
String role
static mapping = {
table 't04t003'
id column : 'f_role_id'
role column : 'f_role'
version column : 'f_revision'
cache true
}
}
class UserRole implements Serializable {
User user
Role role
static mapping = {
table 't04j002'
id composite : ['role', 'user']
role column :'k_role_id'
user column :'k_user_id'
version false
}
}
I can't figure out how to build the criteria to find all the user. I tried the following:
def criteria = UserRole.createCriteria()
def list = criteria.list {
projections {
user{
ilike('username', 'omar')
}
}
}
In console mode i have seen this query with message
Hibernate:
select
this_.k_role_id as k1_3406_0_,
this_.k_user_id as k2_3406_0_
from
t04j002 this_
where
(
lower(user_alias1_.f_username) like ?
)
However, it says Unknown column 'user_alias1_.f_username' in 'where clause'.
But i cant figure out this(user_alias1_) alias
I am not exactly clear on what you want to retrieve from which table. So based on your criteria query, here is how to retrieve user from the UserRole table.
def criteria = UserRole.createCriteria()
def result = criteria.list {
projections {
property("user")
}
user {
eq("username", "omar") //strict match
}
}
return result;
What this does is it builds a list that has the user.username as omar. By default the result will be the UserRole object, but to get user object instead we used projections.
Update:
Your Domain class seems a bit out of grails conventions. Why not user something like this?
//User.groovy
class User {
String username
// ..
static constraints = {
username blank: false, nullable: false, maxSize: 10
}
}
//UserRole.groovy
// Since you are using composite keys, we have to implement serializable. But is there a need for composite key in your situtation?
class UserRole implements Serializable {
User user
// ..
}
In you service class
// UserRoleService.groovy
def getUser(String username) {
def criteria = UserRole.createCriteria()
def result = criteria.get {
projections {
property("user")
}
user {
ilike("username", username)
}
}
return result;
}
And to call the service method from controller,
// ExampleController.groovy
class ExampleController {
def userRoleService
def index() {}
def someThing(String username) {
def user = userRoleService.getUser(username)
println user?.username
// ..
}
}
Related
I'm trying to create (my first ever) taglib in grails and I sort of got it down except that I'm trying to return a list/array of strings that has to be displayed in a table but all I'm getting is the first value of the list. Here's what I got:
Taglib:
def roles = { attrs, body ->
def user = User.get(attrs.user)
if(user) {
def roles = Role.findAllById(UserRole.findByUser(user).roleId)
def realRoles = []
roles.each { role ->
realRoles.add(role.authority.substring(5))
}
out << realRoles
out << body()
}
}
Html:
<table class="table table-bordered table-hover" style="background-color: #FFFFFF;">
<thead>
<tr style="background-color: #007FB2;color: #FFFFFF;">
<th>ID</th>
<th>Name</th>
<th>Surname</th>
<th>Email</th>
<th>Roles</th>
<th>Delete</th>
</tr>
</thead>
<g:each in="${users}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.firstName}</td>
<td>${user.surname}</td>
<td>${user.username}</td>
<td><g:roles user="${user.id}" /></td>
<td><button type="button" class="gen-btn btn-red" style="width: 100%;" onclick="confDelete(${user.id}, '${user.firstName}');">Delete</button></td>
</tr>
</g:each>
</table>
What's supposed to happen is when the taglib gets called it's supposed to go get all the roles associated with the user id then take each role's authority property and cut the preceding "ROLE_" off of it. So ROLE_ADMIN just becomes ADMIN and ROLE_USER just becomes USER, etc. What I'd ideally like is a list that I can loop through in the gsp but I realise that's alot to ask so if I can just get help getting a comma separated list roles back from my taglib I'd really appreciated it.
To make it more clear, what I'm currently getting back is literally [ROLE_ADMIN] regardless of whether the user has more roles than that. What I want is a complete list of roles, e.g. ROLE_ADMIN, ROLE_MANAGER, ROLE_WHATEVER.
Thanks in advance
EDIT Here are my User, Role and UserRole domain classes. I am using Spring security to generate these classes.
User:
package fake.package.name
class User {
transient springSecurityService
String username
String password
String firstName
String surname
Date dateCreated
boolean enabled
boolean accountExpired
boolean accountLocked
boolean passwordExpired
static constraints = {
username blank: false, unique: true
password blank: false
firstName blank: false
surname blank: false
}
static mapping = {
password column: '`password`'
}
Set<Role> getAuthorities() {
UserRole.findAllByUser(this).collect { it.role } as Set
}
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
password = springSecurityService.encodePassword(password)
}
String toString(){
if((firstName)&&(surname))
firstName+ " " +surname
else
username
}
}
Role:
package fake.package.name
class Role {
String authority
static mapping = {
cache true
}
static constraints = {
authority blank: false, unique: true
}
}
UserRole:
package fake.package.name
import org.apache.commons.lang.builder.HashCodeBuilder
class UserRole implements Serializable {
User user
Role role
boolean equals(other) {
if (!(other instanceof UserRole)) {
return false
}
other.user?.id == user?.id &&
other.role?.id == role?.id
}
int hashCode() {
def builder = new HashCodeBuilder()
if (user) builder.append(user.id)
if (role) builder.append(role.id)
builder.toHashCode()
}
static UserRole get(long userId, long roleId) {
find 'from UserRole where user.id=:userId and role.id=:roleId',
[userId: userId, roleId: roleId]
}
static UserRole create(User user, Role role, boolean flush = false) {
new UserRole(user: user, role: role).save(flush: flush, insert: true)
}
static boolean remove(User user, Role role, boolean flush = false) {
UserRole instance = UserRole.findByUserAndRole(user, role)
if (!instance) {
return false
}
instance.delete(flush: flush)
true
}
static void removeAll(User user) {
executeUpdate 'DELETE FROM UserRole WHERE user=:user', [user: user]
}
static void removeAll(Role role) {
executeUpdate 'DELETE FROM UserRole WHERE role=:role', [role: role]
}
static mapping = {
id composite: ['role', 'user']
version false
}
}
Do note that because this a work project and not a personal one I'm not allowed to edit the code in here. Also, I changed the package names to fake.package.name, that's not the actual names of the packages.
I don't know, where your User<->Role Association comes from (Spring Security Framework?) or if it's coded by yourself. And you gave no examples of your domain-classes.
Let me explain another solution a bit:
If you design your domain-classes somehow like the following:
package xyz
class User {
String name
static hasMany = [authorities: Role]
static constraints = {
}
}
---------------
package xyz
class Role {
static belongsTo = User
String name
static hasMany = [user: User]
static constraints = {
}
}
a third table 'user_roles' will be created for saving the many-to-many relation/association
With this configuration, you could shorten your taglib to:
def roles = { attrs, body ->
def user = User.get(attrs.user)
if(user) {
def realRoles = []
user.authorities.each { r ->
realRoles.add(r.authority.substring(5))
}
out << realRoles.join(', ')
out << body()
}
}
Thanks to susi I managed to figure this thing out. Here: def roles = Role.findAllById(UserRole.findByUser(user).roleId) I only returned the first UserRole so I couldn't possibly bring back more than one Role. This is what my taglib method looks like now. I realize it might not be the most efficient and if someone would like to improve on it feel free.
def roles = { attrs, body ->
def user = User.get(attrs.user)
if(user) {
def userRoles = UserRole.findAllByUser(user)
def roles = []
def realRoles = []
userRoles.each { ur ->
roles.add(Role.findById(ur.roleId))
}
roles.each { r ->
realRoles.add(r.authority.substring(5))
}
realRoles = realRoles.join(", ")
println(realRoles)
out << realRoles
out << body()
}
}
Starting after the line if(user) {:
I find all the userRoles associated with the user Id
Then I loop through the list of userRoles and add each ur to a roles list
Then I loop through the list of Roles and add the formatted role's authority to the realRoles list
Lastly I use realRoles = realRoles.join(", ") to remove the square brackets around eachrealRoles` item
the root cause is the line
def roles = Role.findAllById(UserRole.findByUser(user).roleId)
because the UserRole.findByUser(user) just returnes 1 UserRole, roles is just 1 Role object inside a List, instead of all the User's Role objects.
change it to
def roles = Role.findAllByIdInList(UserRole.findAllByUser(user)*.roleId)
as you seem to be relatively new to Grails, a quick explanation: you can use dynamic finders (find*) in 2 ways: findBy* and findAllBy*. as the naming of the methods suggest, findBy* returns 1 object or null. findAllBy* returns a collection of objects.
Additionally, you can combine the property-names with operations (in our case "InList") to specify the type of search operation.
In this example we first retrieve a list of UserRole objects, transform this list into a list of role ids (using the spread operator "*.") and with these ids retrieve all Role objects having one of these IDs.
as the UserRole class contains a Role property directly, of course we can directly write
def roles = UserRole.findAllByUser(user)*.role
keep in mind, that this list could possibly contain null if the property role on UserRole class was nullable (it's not the case here, but if any reader has a similar problem but with a nullable association).
summarized, what you are doing in the taglib at the moment, could be written as:
out << UserRole.findAllByUser(user)*.role.collect({ it.substring(5) }).join(', ')
and just a little comment to the previous answer from susi:
that code shows a direct many-to-many association between User and Role.
as Burt Beckwith wrote some time ago https://mrpaulwoods.wordpress.com/2011/02/07/implementing-burt-beckwiths-gorm-performance-no-collections/
this can have impact on performance as every new User that gets added to a Role requires hibernate to load all existing User-Role-Connections, which could cause trouble when you have lots of users.
That's why the scaffolded domain classes of spring-security-core (since 2.x version iirc) now explicitely define the UserRole-class.
This is my domain.
import org.apache.commons.lang.builder.EqualsBuilder
import org.apache.commons.lang.builder.HashCodeBuilder
class ManageVehicle implements Serializable {
String vehicle
Short truckKey=0
Short siteID
Integer unitID
String wheelConfig
String model
Short period=0
String usid=UUID.randomUUID().toString()
//static belongsTo = [wheelConfig : TTType,model:TTModel]
int hashCode() {
def builder = new HashCodeBuilder()
builder.append truckKey
builder.append siteID
builder.toHashCode()
}
boolean equals(other) {
if (other == null) return false
def builder = new EqualsBuilder()
builder.append truckKey, other.truckKey
builder.append siteID, other.siteID
builder.isEquals()
}
static mapping = {
table 'TF_Truck'
version false
truckKey column :'TruckKey'
siteID column :'SiteID'
id composite:['truckKey','siteID']
vehicle column: 'TruckID' ,sqlType: 'nvarchar'
wheelConfig column: 'TypID',sqlType: 'tinyint'
model column: 'ModID' ,sqlType: 'tinyint'
unitID column: 'McmID',sqlType: 'tinyint'
period column: 'Period'
usid generator: 'assigned', column:'USID', sqlType:'uniqueidentifier'
}
static constraints = {
period nullable: false
truckKey nullable: false
siteID nullable: false
}
}
And my save method in controller is .
def save(ManageVehicle manageVehicleInstance) {
manageVehicleInstance.siteID=RequestContextHolder.currentRequestAttributes().getAttribute("SiteID",RequestAttributes.SCOPE_SESSION) as Short
if (manageVehicleInstance == null) {
notFound()
return
}
if (manageVehicleInstance.hasErrors()) {
respond manageVehicleInstance.errors, view:'create'
return
}
manageVehicleInstance.save(flush:true,failOnError: true)
request.withFormat {
form {
flash.message = message(code: 'default.created.message', args: [message(code: 'manageVehicle.label', default: 'Manage Vehicle'), manageVehicleInstance.id])
redirect manageVehicleInstance
}
'*' { respond manageVehicleInstance, [status: CREATED] }
}
}
while i am applying save operation on this domain i am getting following exception .
Message: Cannot redirect for object [ManageVehicle : (unsaved)] it is not a domain or has no identifier. Use an explicit redirect instead
Suggest me some solution.
I guess that Grails cannot handle composite id in redirection.
I have two options in my mind to fix that case
Create id field for your domain class by removing id line from mappings
Implement custom redirection for your saved object like
redirect(action:'show', params:[truckKey:manageVehicleInstance.truckKey,siteID:manageVehicleInstance.siteID])
With 2. option you may also have to implement custom object loading into 'show' action using composite id like def manageVehicleInstance = ManageVehicle.findByTruckKeyAndSiteID(params.truckKey, params.siteID).
I'm having problems trying to update a record on a Grails 2.3.7 Project
I don't want to modify the fields that are part of the composite Id, I just want to update the other fields.
Just one class, few properties, but every time I try to update, it throws me the "not unique error" when this lines runs:
personInstance.validate()
if (personInstance.hasErrors()) {
respond personInstance.errors, view:'create'
return
}
My class looks like this:
class Person implements Serializable {
static constraints = {
name(unique: lastName)
}
static mapping = {
id generator: 'assigned'
id composite: ["name", "lastName"]
}
//Override equals and hashcode methods
boolean equals(other) {
if (!(other instanceof Person)) {
return false
}
other.name == name && other.lastName == lastName
}
int hashCode() {
def builder = new HashCodeBuilder()
builder.append name
builder.append lastName
builder.toHashCode()
}
String name
String lastName
String description
}
And the controller action:
def update() {
def personInstance = Person.get(new Person(name:params.name, lastName:params.lastName))
personInstance.properties = params
personInstance.validate()
if (personInstance.hasErrors()) {
respond personInstance.errors, view:'create'
return
}
personInstance.save flush:true
request.withFormat {/*etc...*/}
}
When I use validate(), it throws me a Grails unique key error, when I avoid validation its a BD not unique PK error.
Is like Grails didn't know if I want to do an insert or an update when I personInstance.validate().
Is there a way to manage this in a correct way that I'm not seeing?
Or am I forced to avoid validation?
am I doing something wrong?
Thanks in advance.
I believe the GORM mapping DSL expects just one id definition. Try combining your two id lines into just this one:
id generator: 'assigned', composite: ["name", "lastName"]
Also, in addition to implementing Serializable, your domain class should override equals and hashCode, as described under "Composite Primary Keys" here: http://grails.org/doc/latest/guide/single.html#identity
I got an error with grails query.
I had class User:
class User {
String username
String passwordHash
static hasMany = [roles: Role, permissions: String, clients: User, owners: User]
}
When i do query:
def addClient() {
def principal = SecurityUtils.subject?.principal
User currentUser = User.findByUsername(principal);
if (request.method == 'GET') {
User user = new User()
[client: currentUser, clientCount: User.countByOwners(currentUser)]
}
}
Grails say that:
Parameter "#1" is not set; SQL statement: select count(*) as y0_ from user this_ where this_.id=? [90012-164]
Why?
Looks like currentUser is null.
BTW, I wonder, why you count by owners, but specify a user as the owners? According to your hasMany definition, owners is a collection.
I have two domain-classes. One is a "Partner" the other is a "Customer". A customer can be a part of a Partner and a Partner can have 1 or more Customers:
class Customer {
Integer id
String name
static hasOne = [partner:Partner]
static mapping = {
partner joinTable:[name:'PartnerMap',column:'partner_id',key:'customer_id']
}
}
class Partner {
Integer id
static hasMany = [customers:Customer]
static mapping = {
customers joinTable:[name:'PartnerMap',column:'customer_id',key:'partner_id']
}
}
However, whenever I try to see if a customer is a part of a partner, like this:
def customers = Customer.list()
customers.each {
if (it.partner) {
println "Partner!"
}
}
I get the following error:
org.springframework.dao.InvalidDataAccessResourceUsageException: could not execute query; SQL [select this_.customer_id as customer1_162_0_, this_.company as company162_0_, this_.display_name as display3_162_0_, this_.parent_customer_id as parent4_162_0_, this_.partner_id as partner5_162_0_, this_.server_id as server6_162_0_, this_.status as status162_0_, this_.vertical_market as vertical8_162_0_ from Customer this_]; nested exception is org.hibernate.exception.SQLGrammarException: could not execute query
It looks as if Grails is thinking partner_id is a part of the Customer query, and it's not... It is in the PartnerMap table, which is supposed to find the customer_id, then get the Partner from the corresponding partner_id.
Anyone have any clue what I'm doing wrong?
Edit: I forgot to mention I'm doing this with legacy database tables. So I have a Partner, Customer and PartnerMap table. PartnerMap has simply a customer_id and partner_id field.
Given the way 1-many works when you want a join table, I think it's not possible with standard GORM to make it bidirectional and access a Customer's Partner. But you can map the join table with a domain class and access things that way:
Customer:
class Customer {
String name
def getPartner() {
PartnerMap.findByCustomer(this)?.partner
}
}
Partner:
class Partner {
String name
def getCustomers() {
PartnerMap.findAllByPartner(this)*.customer
}
}
PartnerMap:
import org.apache.commons.lang.builder.HashCodeBuilder
class PartnerMap implements Serializable {
Partner partner
Customer customer
boolean equals(other) {
if (!(other instanceof PartnerMap)) {
return false
}
other.partner?.id == partner?.id &&
other.customer?.id == customer?.id
}
int hashCode() {
def builder = new HashCodeBuilder()
if (partner) builder.append(partner.id)
if (customer) builder.append(customer.id)
builder.toHashCode()
}
static PartnerMap get(long partnerId, long customerId) {
find 'from PartnerMap where partner.id=:partnerId and customer.id=:customerId',
[partnerId: partnerId, customerId: customerId]
}
static PartnerMap create(Partner partner, Customer customer, boolean flush = false) {
new PartnerMap(partner: partner, customer: customer).save(flush: flush, insert: true)
}
static boolean remove(Partner partner, Customer customer, boolean flush = false) {
PartnerMap instance = PartnerMap.findByPartnerAndCustomer(partner, customer)
instance ? instance.delete(flush: flush) : false
}
static void removeAll(Partner partner) {
executeUpdate 'DELETE FROM PartnerMap WHERE partner=:partner', [partner: partner]
}
static void removeAll(Customer customer) {
executeUpdate 'DELETE FROM PartnerMap WHERE customer=:customer', [customer: customer]
}
static mapping = {
id composite: ['customer', 'partner']
version false
table 'PartnerMap'
}
}
Since you're not using hasMany, you lose the addToXXX dynamic method, but you can call PartnerMap.create() to relate two instances. You also lose the collection and back-ref in the domain classes, but I added utility methods for those.