在grails中保存具有多对多关系的对象
我正在尝试保存具有多对多关系的对象。一个 SellingCompany 可以拥有多个帐户,一个帐户可以与多个 SellingCompanies 关联。因此,SellingCompaniesAccount 中存储的表之间存在多对多关系
我的 Account_Info 域如下:
class AccountInfo {
static mapping ={
table 'AccountInfo'
version false
//id column:'accountInfoID'
}
String evi_pass_phrase
String evi_username
String security_key
// to make sure fields show up in a particular order
static constraints = {
//accountInfoID(insert:false,update:false)
evi_pass_phrase()
evi_username()
security_key()
}
static hasMany = [sellingcompaniesaccount:SellingCompaniesAccount]
String toString() {
return "${evi_username}"
}
}
我的 SellingCompanies 域如下:
class SellingCompanies
{
static mapping = {
table 'SellingCompanies'
version false
}
String name
//static belongsTo = AccountInfo
//static hasMany = [accounts: AccountInfo]
static hasMany = [sellingcompaniesaccount:SellingCompaniesAccount]
static constraints = {
name(blank:false, validator:
{ val, obj ->
def similarSellingCompanies = SellingCompanies.findByNameIlike(val)
return !similarSellingCompanies || (obj.id == similarSellingCompanies.id)
})
}
//String toString() { name }
}
保存多对多关系的表如下:
class SellingCompaniesAccount {
static constraints = {
// ensure the group of sellingCompaneis and accountInfo values are unique
agency_name(unique:['sellingCompanies','accountInfo'])
}
int agency_id
String agency_name
String consultant_id
String code
Boolean isActive
String iata
ContactInfo contactinfo
static belongsTo = [sellingCompanies:SellingCompanies, accountInfo:AccountInfo]
}
}
create.gsp 文件中的表单包含实际上迭代所有不同的 SellingCompanies 并显示为复选框的代码。
<g:form action="save" method="post">
<div class="dialog">
<table width="500px" border="0px" color="red">
<tbody>
<tr class="prop">
<td valign="top" class="name"><label for="accountInfo"><g:message
code="sellingCompaniesAccount.accountInfo.label"
default="Account Info" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'accountInfo', 'errors')}">
<g:select name="accountInfo.id"
from="${content_hub_admin.AccountInfo.list()}" optionKey="id"
value="${sellingCompaniesAccountInstance?.accountInfo?.id}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="sellingCompanies"><g:message
code="sellingCompaniesAccount.sellingCompanies.label"
default="Selling Companies" /></label></td>
<td valign="top"
class="">
<g:each in="${content_hub_admin.SellingCompanies.list()}" var="item" status="i">
${++i}. ${item.name} <g:checkBox name="sellingcompanies_${++i-1}" optionKey="id" value="${item.id}" /> <br>
</g:each>
<!-- end here by rsheyeah -->
</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="code"><g:message
code="sellingCompaniesAccount.code.label" default="Code" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'code', 'errors')}">
<g:textField name="code"
value="${sellingCompaniesAccountInstance?.code}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="agency_name"><g:message
code="sellingCompaniesAccount.agency_name.label"
default="Agencyname" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'agency_name', 'errors')}">
<g:textField name="agency_name"
value="${sellingCompaniesAccountInstance?.agency_name}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="isActive"><g:message
code="sellingCompaniesAccount.isActive.label" default="Is Active" /></label>
</td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'isActive', 'errors')}">
<g:checkBox name="isActive"
value="${sellingCompaniesAccountInstance?.isActive}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="agency_id"><g:message
code="sellingCompaniesAccount.agency_id.label" default="Agencyid" /></label>
</td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'agency_id', 'errors')}">
<g:textField name="agency_id"
value="${fieldValue(bean: sellingCompaniesAccountInstance, field: 'agency_id')}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="iata"><g:message
code="sellingCompaniesAccount.iata.label" default="Iata" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'iata', 'errors')}">
<g:textField name="iata"
value="${sellingCompaniesAccountInstance?.iata}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="consultant_id"><g:message
code="sellingCompaniesAccount.consultant_id.label"
default="Consultantid" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'consultant_id', 'errors')}">
<g:textField name="consultant_id"
value="${sellingCompaniesAccountInstance?.consultant_id}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="contactinfo"><g:message
code="sellingCompaniesAccount.contactinfo.label"
default="Contactinfo" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'contactinfo', 'errors')}">
<g:select name="contactinfo.id"
from="${content_hub_admin.ContactInfo.list()}" optionKey="id"
value="${sellingCompaniesAccountInstance?.contactinfo?.id}" /></td>
</tr>
</tbody>
</table>
</div>
<div class="buttons"><span class="button"><g:submitButton
name="create" class="save"
value="${message(code: 'default.button.create.label', default: 'Create')}" /></span>
</div>
</g:form>
最后是处理保存和列表功能的控制器。
class SellingCompaniesAccountController {
private static Logger log = Logger.getLogger(SellingCompaniesAccountController.class)
//def index = { }
//def scaffold = true
def index = { redirect(action:list,params:params) }
//To limit access to controller actions based on the HTTP request method.
def allowedMethods = [save:'POST']
//create.gsp exists
def create = {
render(view:"create")
}
//edit.gsp exists
//def edit = {}
//list.gsp exists
def list = {
[ sellingCompaniesAccountInstanceList: SellingCompaniesAccount.list( max:15) ]
}
//show.gsp exists
//def show={}
//save.gsp exists
def save = {
log.info "Saving: " + params.toString()
println("Saving: " + params.toString())
def sellingCompaniesAccount = params.sellingCompaniesAccount
println(sellingCompaniesAccount)
def sellingCompanies = params.sellingCompanies
log.info "sellingCompanies: " + sellingCompanies
println(sellingCompanies)
def sellingCompaniesAccountInstance = new SellingCompaniesAccount(name: params.name)
println(params.name)
params.each {
if (it.key.contains("_sellingcompanies"))
//sellingCompaniesAccountInstance.sellingCompaniesId << SellingCompanies.get((it.key - "sellingcompanies_") as Integer)
if (it.key.contains("sellingcompanies_"))
sellingCompaniesAccountInstance.sellingCompaniesId << SellingCompanies.get((it.key - "sellingcompanies_") as Integer)
}
log.info sellingCompaniesAccountInstance
if (sellingCompaniesAccountInstance.save(flush: true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'sellingCompaniesAccountInstance.label', default: 'sellingCompaniesAccountInstance'), sellingCompaniesAccountInstance.id])}"
redirect(action: "show", id: sellingCompaniesAccountInstance.id)
log.info sellingCompaniesAccountInstance
}
else {
render(view: "create", model: [sellingCompaniesAccountInstance: sellingCompaniesAccountInstance])
}
}
}
现在,由于出现像 _ sellcompanies_1 等这样的空隐藏值,我收到以下错误:
错误日志:
Saving: ["accountInfo.id":"1", "accountInfo":["id":"1"], "_sellingcompanies_5":"", "_isActive":"", "code":"test", "agency_name":"test", "sellingcompanies_4":"4", "sellingcompanies_5":"5", "create":"Create", "isActive":"on", "iata":"test", "agency_id":"test", "contactinfo.id":"1", "contactinfo":["id":"1"], "consultant_id":"test", "sellingcompanies_2":"2", "_sellingcompanies_1":"", "sellingcompanies_3":"3", "_sellingcompanies_2":"", "_sellingcompanies_3":"", "sellingcompanies_1":"1", "_sellingcompanies_4":"", "action":"save", "controller":"sellingCompaniesAccount"]
null
null
null
2011-03-15 17:13:44,620 [http-8080-2] ERROR org.codehaus.groovy.grails.web.errors.GrailsExceptionResolver - For input string: "_5"
java.lang.NumberFormatException: For input string: "_5"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
at java.lang.Integer.parseInt(Integer.java:449)
at java.lang.Integer.valueOf(Integer.java:554)
at content_hub_admin.SellingCompaniesAccountController$_closure4_closure5.doCall(content_hub_admin.SellingCompaniesAccountController:70)
at content_hub_admin.SellingCompaniesAccountController$_closure4.doCall(content_hub_admin.SellingCompaniesAccountController:66)
at content_hub_admin.SellingCompaniesAccountController$_closure4.doCall(content_hub_admin.SellingCompaniesAccountController)
at java.lang.Thread.run(Thread.java:680)
首先,隐藏值来自哪里,这种方法可以很好地提交中的多对多关系信息吗? SellingCompaniesAccount 控制器类。任何更好的技术来做到这一点。
create.gsp 在浏览器中解析为:
提前致谢
I am trying to save objects having Many-Many Relationship . A SellingCompany can have many Accounts and an Account can be associated with many SellingCompanies. So there is a many-many relationship between the tables stored in SellingCompaniesAccount
My Account_Info domain is as follows:
class AccountInfo {
static mapping ={
table 'AccountInfo'
version false
//id column:'accountInfoID'
}
String evi_pass_phrase
String evi_username
String security_key
// to make sure fields show up in a particular order
static constraints = {
//accountInfoID(insert:false,update:false)
evi_pass_phrase()
evi_username()
security_key()
}
static hasMany = [sellingcompaniesaccount:SellingCompaniesAccount]
String toString() {
return "${evi_username}"
}
}
My SellingComapanies domain is as follows:
class SellingCompanies
{
static mapping = {
table 'SellingCompanies'
version false
}
String name
//static belongsTo = AccountInfo
//static hasMany = [accounts: AccountInfo]
static hasMany = [sellingcompaniesaccount:SellingCompaniesAccount]
static constraints = {
name(blank:false, validator:
{ val, obj ->
def similarSellingCompanies = SellingCompanies.findByNameIlike(val)
return !similarSellingCompanies || (obj.id == similarSellingCompanies.id)
})
}
//String toString() { name }
}
The table that holds the Many-Many relationship is as follows:
class SellingCompaniesAccount {
static constraints = {
// ensure the group of sellingCompaneis and accountInfo values are unique
agency_name(unique:['sellingCompanies','accountInfo'])
}
int agency_id
String agency_name
String consultant_id
String code
Boolean isActive
String iata
ContactInfo contactinfo
static belongsTo = [sellingCompanies:SellingCompanies, accountInfo:AccountInfo]
}
}
The form in the create.gsp file contains the code that actually iterates over all the different SellingCompanies and displays as a check-box.
<g:form action="save" method="post">
<div class="dialog">
<table width="500px" border="0px" color="red">
<tbody>
<tr class="prop">
<td valign="top" class="name"><label for="accountInfo"><g:message
code="sellingCompaniesAccount.accountInfo.label"
default="Account Info" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'accountInfo', 'errors')}">
<g:select name="accountInfo.id"
from="${content_hub_admin.AccountInfo.list()}" optionKey="id"
value="${sellingCompaniesAccountInstance?.accountInfo?.id}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="sellingCompanies"><g:message
code="sellingCompaniesAccount.sellingCompanies.label"
default="Selling Companies" /></label></td>
<td valign="top"
class="">
<g:each in="${content_hub_admin.SellingCompanies.list()}" var="item" status="i">
${++i}. ${item.name} <g:checkBox name="sellingcompanies_${++i-1}" optionKey="id" value="${item.id}" /> <br>
</g:each>
<!-- end here by rsheyeah -->
</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="code"><g:message
code="sellingCompaniesAccount.code.label" default="Code" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'code', 'errors')}">
<g:textField name="code"
value="${sellingCompaniesAccountInstance?.code}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="agency_name"><g:message
code="sellingCompaniesAccount.agency_name.label"
default="Agencyname" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'agency_name', 'errors')}">
<g:textField name="agency_name"
value="${sellingCompaniesAccountInstance?.agency_name}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="isActive"><g:message
code="sellingCompaniesAccount.isActive.label" default="Is Active" /></label>
</td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'isActive', 'errors')}">
<g:checkBox name="isActive"
value="${sellingCompaniesAccountInstance?.isActive}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="agency_id"><g:message
code="sellingCompaniesAccount.agency_id.label" default="Agencyid" /></label>
</td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'agency_id', 'errors')}">
<g:textField name="agency_id"
value="${fieldValue(bean: sellingCompaniesAccountInstance, field: 'agency_id')}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="iata"><g:message
code="sellingCompaniesAccount.iata.label" default="Iata" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'iata', 'errors')}">
<g:textField name="iata"
value="${sellingCompaniesAccountInstance?.iata}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="consultant_id"><g:message
code="sellingCompaniesAccount.consultant_id.label"
default="Consultantid" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'consultant_id', 'errors')}">
<g:textField name="consultant_id"
value="${sellingCompaniesAccountInstance?.consultant_id}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="contactinfo"><g:message
code="sellingCompaniesAccount.contactinfo.label"
default="Contactinfo" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'contactinfo', 'errors')}">
<g:select name="contactinfo.id"
from="${content_hub_admin.ContactInfo.list()}" optionKey="id"
value="${sellingCompaniesAccountInstance?.contactinfo?.id}" /></td>
</tr>
</tbody>
</table>
</div>
<div class="buttons"><span class="button"><g:submitButton
name="create" class="save"
value="${message(code: 'default.button.create.label', default: 'Create')}" /></span>
</div>
</g:form>
Lastly the controller which handles the save and list functions.
class SellingCompaniesAccountController {
private static Logger log = Logger.getLogger(SellingCompaniesAccountController.class)
//def index = { }
//def scaffold = true
def index = { redirect(action:list,params:params) }
//To limit access to controller actions based on the HTTP request method.
def allowedMethods = [save:'POST']
//create.gsp exists
def create = {
render(view:"create")
}
//edit.gsp exists
//def edit = {}
//list.gsp exists
def list = {
[ sellingCompaniesAccountInstanceList: SellingCompaniesAccount.list( max:15) ]
}
//show.gsp exists
//def show={}
//save.gsp exists
def save = {
log.info "Saving: " + params.toString()
println("Saving: " + params.toString())
def sellingCompaniesAccount = params.sellingCompaniesAccount
println(sellingCompaniesAccount)
def sellingCompanies = params.sellingCompanies
log.info "sellingCompanies: " + sellingCompanies
println(sellingCompanies)
def sellingCompaniesAccountInstance = new SellingCompaniesAccount(name: params.name)
println(params.name)
params.each {
if (it.key.contains("_sellingcompanies"))
//sellingCompaniesAccountInstance.sellingCompaniesId << SellingCompanies.get((it.key - "sellingcompanies_") as Integer)
if (it.key.contains("sellingcompanies_"))
sellingCompaniesAccountInstance.sellingCompaniesId << SellingCompanies.get((it.key - "sellingcompanies_") as Integer)
}
log.info sellingCompaniesAccountInstance
if (sellingCompaniesAccountInstance.save(flush: true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'sellingCompaniesAccountInstance.label', default: 'sellingCompaniesAccountInstance'), sellingCompaniesAccountInstance.id])}"
redirect(action: "show", id: sellingCompaniesAccountInstance.id)
log.info sellingCompaniesAccountInstance
}
else {
render(view: "create", model: [sellingCompaniesAccountInstance: sellingCompaniesAccountInstance])
}
}
}
Now, I am getting the following error, due to the empty hidden values appearing like _sellingcompanies_1 etc.:
Error Logs:
Saving: ["accountInfo.id":"1", "accountInfo":["id":"1"], "_sellingcompanies_5":"", "_isActive":"", "code":"test", "agency_name":"test", "sellingcompanies_4":"4", "sellingcompanies_5":"5", "create":"Create", "isActive":"on", "iata":"test", "agency_id":"test", "contactinfo.id":"1", "contactinfo":["id":"1"], "consultant_id":"test", "sellingcompanies_2":"2", "_sellingcompanies_1":"", "sellingcompanies_3":"3", "_sellingcompanies_2":"", "_sellingcompanies_3":"", "sellingcompanies_1":"1", "_sellingcompanies_4":"", "action":"save", "controller":"sellingCompaniesAccount"]
null
null
null
2011-03-15 17:13:44,620 [http-8080-2] ERROR org.codehaus.groovy.grails.web.errors.GrailsExceptionResolver - For input string: "_5"
java.lang.NumberFormatException: For input string: "_5"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
at java.lang.Integer.parseInt(Integer.java:449)
at java.lang.Integer.valueOf(Integer.java:554)
at content_hub_admin.SellingCompaniesAccountController$_closure4_closure5.doCall(content_hub_admin.SellingCompaniesAccountController:70)
at content_hub_admin.SellingCompaniesAccountController$_closure4.doCall(content_hub_admin.SellingCompaniesAccountController:66)
at content_hub_admin.SellingCompaniesAccountController$_closure4.doCall(content_hub_admin.SellingCompaniesAccountController)
at java.lang.Thread.run(Thread.java:680)
First of all, where does the hidden values come from and is this approach fine to commit the the Many-Many relationship info in SellingCompaniesAccount controller class. Any better technique of doing this.
The create.gsp resolves to this in the browser:
Thanks in advance
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果其他人也遇到同样的问题,那么 Daniel 的上述答案绝对正确,只需从 Grails 2.7.8 开始添加一些更改即可。
所有复选框都将具有相同的
value = "${item.id}"
和name =" sellcompanies"
,如下所示:最好的部分是,在当前版本的 Grails 中,您不会需要使用
flatten() as string
正如 Daniel 提到的,这些值将自动由 Grails 处理并保留在连接表中。您需要确定的是您的复选框具有正确的名称和值。希望有帮助!
If anyone else is having the same problem then the above answer by Daniel is absolutely correct just adding few changes as of Grails 2.7.8.
All checkbox will have same
value = "${item.id}"
andname ="sellingcompanies"
as shown below:Best part is that in present version of Grails you wont need to use
flatten() as string
as mentioned by Daniel and these values will automatically be handled by Grails and will be persisted in the join table. All you need to be sure is your checkbox have correct name and value.Hope it helps!
问题出在这段代码上:
表单帖子中的参数是:
循环中的测试首先检查参数键是否包含“_ sellingcompanies< /em>”然后什么也不做;该部分的第二部分检查参数键是否包含“salescompanies_”,然后尝试从该参数值中提取后缀数字。对于键值为“_ sellcompanies_5”的参数,第一个测试和第二个测试的计算结果都为 true,因此在第二个测试中,您要从参数键值,您剩下“_5”,然后您尝试将其计算为整数。不幸的是,“_5”不是有效的整数值,因此您给出了错误。
您可以通过执行以下操作非常简单地解决此问题:
处理此问题的更好方法可能是更改您的 gsp 为每个销售公司提供相同的命名参数,然后循环遍历实际应用的值。像这样的事情:
然后在您的控制器中执行如下操作:
这应该确保您正确评估从模型对象传递的适当值,而不是侵入检索方法。
希望这有帮助!
The problem is with this piece of code:
Your params from the form post are:
The test in your loop first checks if the parameter key contains "_sellingcompanies" and then it does nothing; the second part of that checks if the parameter key contains "sellingcompanies_" and then it tries to pull the suffixed number off of that parameter value. In the case of a parameter with the key value "_sellingcompanies_5", both the first test and the second test evaluate to true, so in the second test, you are subtracting the "sellingcompanies_" off of the parameter key value and you are left with "_5", which you are then trying to evaluate to an Integer. Unfortunately, "_5" is not a valid Integer value, and hence your given error.
You can solve this very simply by doing the following:
Probably a better way to handle this though would be to change your gsp to give the same named parameter for each of the sellingcompanies, and then looping through the actual applied values. Something like this:
Then in your controller do something like this:
This should ensure that you are properly evaluating the appropriate values that have been passed from your model object, and not hacking in a retrieval method.
Hope this helps!