求教Hibernate级联操作自动更新的难题!

cceyjames 2009-06-11 03:16:02
很头疼,搞到凌晨三点了还没解决,请大家帮我看看,至少让我这8个小时死得其所。

表:
product.java
private Set<ProductAttributeValue> productAttributeValues = new HashSet<ProductAttributeValue>(0) ;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "product")
public Set<ProductAttributeValue> getProductAttributeValues()
{
return this.productAttributeValues ;
}

productAttributeValue.java //产品属性的值private Product product ;
private CategoryAttribute categoryAttribute ;
private String attributeValue ;

两者是一对多的关系。

现在操作product,在productAction中save的时候,出现这样的问题:

如果是新添加的product:
//因为要对应到前台若干个属性,所以这里采用List方便映射
private List<ProductAttributeValue> productAttributeValue ;
//productAttributeValue对应到前台若干个input,他们表示产品的多个属性
for (int i = 0; i < productAttributeValue.size(); i++)
{
productAttributeValue.get(i).setProduct(entity) ; //如果是添加产品,则必须在此设置product的id
}
//转换list为set
Set<ProductAttributeValue> productAttributeValues = new HashSet<ProductAttributeValue>(productAttributeValue) ;
entity.setProductAttributeValues(productAttributeValues) ;

OK,到目前为止,一切正常,产品能够添加成功,属性也因为hibernate的级联自动添加了。

问题在于:
如果我现在添加了一个产品,只有一个属性。那么我再修改这个产品的时候,修改了产品属性,我们可爱的hibernate级联操作竟然不是update而是insert了一个新的属性,sql的痕迹如下:
Hibernate:
insert
into
xunma.product_attribute_value
(attribute_value, attribute_id, product_id)
values
(?, ?, ?)
Hibernate:
update
xunma.product
set
category_id=?,
create_time=?,
descript=?,
name=?,
read_count=?,
trade_id=?
where
id=?

不仅insert了,还update。莫明其妙啊。


然后我一路追凶,发现如果在prepareModel()中,对productAttributeValue的值进行更改,则可以避免这种情况:
productAttributeValue = new ArrayList<ProductAttributeValue>(entity.getProductAttributeValues()) ;
在prepareModel中,我让productAttributeValue(这个东东对应到前台jsp中的若干个属性input)的值设置为从数据库中获取之前的属性的值。然后再save()的时候:
Set<ProductAttributeValue> productAttributeValues = new HashSet<ProductAttributeValue>(productAttributeValue) ;
entity.setProductAttributeValues(productAttributeValues) ;
这样,就不会出现修改属性的时候不是update而是insert了。我不知道为什么。但事实是这样,这是疑问一

然而,随之而来的问题出现了:
由于prepareModel()中我调整了productAttributeValue的值,比如之前productAttributeValue长度为3,表示有3个属性,而我当前修改了这个产品的分类,因为不同的分类拥有不同的属性,新的分类可能之后2个属性。这样就出问题了:

问题1:save之后,product以前的3个属性,其中有2个修改为新的属性了,而还有多的1个属性还存在,它这是不应该属于这个世界。

问题2:如果当前有2个属性,然后修改分类之后有了3个属性,就会出现这样的错误:
identifier of an instance of com.xunma.entity.product.CategoryAttribute was altered from 9 to 10;
网上说这个错误和id的类型有关,事实上我这里的所有主键id都是同一个类型和策略。所以排除这个可能。

小弟对hibernate和struts2了解不够深入,还请大家帮我看看,这些原因到底是什么造成的,该如何解决。不慎感激!
...全文
263 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
cceyjames 2009-06-19
  • 打赏
  • 举报
回复
没有人遇到过类似的问题吗?怎么解决啊。
cceyjames 2009-06-17
  • 打赏
  • 举报
回复
我在总结一下两个问题:

问题一:
如果我没有在prepareModel()中:
ProductAttributeValue = new ArrayList <ProductAttributeValue>(entity.getProductAttributeValues())
而是在input()中:
productAttributeValue = new ArrayList<ProductAttributeValue>(entity.getProductAttributeValues()) ;

则会出现Update的同时还Insert了的问题。

而如果是在prepareModel()中操作,则可以解决本问题。但却引发第二个问题。


问题二:
为了解决问题一,我在prepareModel()中操作ProductAttributeValue。却引发下面的问题:

假设productAttributeValue长度为1,表示有1个属性,而我当前修改了这个产品的分类,新的分类有2个属性(因为不同的分类拥有不同的属性)。这样,以前的1个属性和新的2个属性,在update的时候就会出现了如下错误:
identifier of an instance of com.xunma.entity.product.CategoryAttribute was altered from 9 to 10;
cceyjames 2009-06-17
  • 打赏
  • 举报
回复
floger你的意思是必须先查询出来?要select或者get?

我现在在prepareModel()中:
ProductAttributeValue = new ArrayList<ProductAttributeValue>(entity.getProductAttributeValues()) ;
就正常了,就是说不会同时存在UPDATE和INSERT。

可是这样做了之后,就会导致第二个问题的出现。郁闷。


floger 2009-06-17
  • 打赏
  • 举报
回复
其实,如果你的数据已经存在的话,你要是想更新而且使用的HIEBERTNATE的话,就要先把这条数据查询出来后在使用UPdate的方法,不然就会执行一个insert语句。
cceyjames 2009-06-17
  • 打赏
  • 举报
回复
前几天没心思弄,所以耽误了几天。

Landor2004,你解释的两个问题,我没听明白。不好意思。

第一个问题,我是在修改产品属性,而修改之后不是update,而是insert。这就不对了。你的意思是productAttributeValues不能存在数据?

第二个问题,你说的是将后台修改为collection吗?
cceyjames 2009-06-11
  • 打赏
  • 举报
回复
不好意思,文字太多,我重新排下版。

下午有面试,大家祝福我吧。谢谢!!!!


哇噻,一觉醒来,这么多回复,哈哈,好高兴啊。

Dantin:
问题1:entity里的多余ProductAttributeValue可以设成NULL,只要对应的值改变了,那么就会更新了多余的ProductAttributeValue可以设成null?请问这个需要手动设置吗?多余的因该自动消失才符合业务逻辑。即使成为null了,也是冗余数据。

问题2:这种错误,往往都是由于数据库中的类型和 javaBean 中类型不匹配产生的。
我也怀疑如此,但苦于不知道怎么找证据。


daisycool :
我怀疑是你修改ProductAttributeValue的代码有问题。你只贴出了添加新属性的代码,把修改的代码也放上来看看。

有关修改的代码如下:
Action中:

//问题就与下面的prepareModel中productAttributeValue赋值有关。


@Override
protected void prepareModel() throws Exception
{
if (id != null)
{
entity = productManager.get(id) ;
productAttributeValue = new ArrayList<ProductAttributeValue>(entity.getProductAttributeValues()) ;

}
else
{
entity = new Product() ;
}
}

//input其实也很平常,没什么特别的。下面的input与问题似乎无关。
@Override
public String input() throws Exception
{
allTrades = tradeManager.getAll() ;
selectedTradeId = entity.getTradeId() ;
allCategories = categoryManager.getAll() ;
if (id != null)
{
selectedCategoryId = entity.getCategory().getId() ;
}
return INPUT ;
}


@Override
public String save() throws Exception
{
entity.setTrade(tradeManager.get(selectedTradeId)) ;
entity.setCategory(categoryManager.get(selectedCategoryId)) ;
if (id == null)
{
for (int i = 0; i < productAttributeValue.size(); i++)
{
//如果属于新建产品,则需要在productAttributeValue设置它的product
productAttributeValue.get(i).setProduct(entity) ;
}
}
//转换list为set
Set<ProductAttributeValue> productAttributeValues = new HashSet<ProductAttributeValue>(productAttributeValue) ;
entity.setProductAttributeValues(productAttributeValues) ;
entity.setCreateTime(new java.util.Date()) ;
productManager.save(entity) ;
addActionMessage("保存产品成功") ;
return RELOAD ;
}

productAttributeValue的get/set,略。(普通的get/set)
... ...


前台jsp中,根据产品的分类自动创建若干个input类型的产品属性让用户输入、提交。相关代码:
function initInput()
{
for(var i=0;i<attributeList.length;i++)
{
var id = parseInt(attributeList[i][0]);
var n = attributeList[i][1];
var t = attributeList[i][2];

var opt = document.createElement('input');
opt.setAttribute('name','productAttributeValue['+i+'].attributeValue');

var idOpt = document.createElement('input');
idOpt.setAttribute('type','hidden');
idOpt.setAttribute('name','productAttributeValue['+i+'].categoryAttribute.id');
idOpt.setAttribute('value',id);

var productIdOpt = document.createElement('input');
productIdOpt.setAttribute('type','hidden');
productIdOpt.setAttribute('name','productAttributeValue['+i+'].product.id');
productIdOpt.setAttribute('value',$("#id").val());

var inputDiv=document.createElement('div');
//取出每一个属性的分类并单独显示
if(i>0)
{
if( t!=attributeList[i-1][2])
{
inputDiv.innerHTML+="<br>";
inputDiv.innerHTML+="<font color=red>"+t+"</font>:<hr>";
}
}
else
{
inputDiv.innerHTML+="<br>";
inputDiv.innerHTML+="<font color=red>"+t+"</font>:<hr>";
}
inputDiv.innerHTML+=n+":";
inputDiv.appendChild(opt);
inputDiv.appendChild(idOpt);
inputDiv.appendChild(productIdOpt);

document.getElementById('attributeDiv').appendChild(inputDiv);
iMaxLength++;
}
}


Landor2004:哇卡卡,你还记得我吗?这是上一个问题的继续。
如果我现在添加了一个产品,只有一个属性。那么我再修改这个产品的时候,修改了产品属性,我们可爱的hibernate级联操作竟然不是update而是insert了一个新的属性。
只能是当保存product的时候,productAttributeValues存在数据,所以执行了update和insert 。
很奇怪哦,难道如你所说这是合理的?正常的?

identifier of an instance of com.xunma.entity.product.CategoryAttribute was altered from 9 to 10;
类型需要检查,比如数据库是bigint而你的类型是int,包括那个字段的类型都检查一下
数据库字段类型和action这边都是正常的、一致的,之所以这么说是因为我没有做任何人为的类型修改。只有一点:就是productAttributeValues我在product的eneity文件中设置的是set类型,而在action中我与前台映射的是list类型,在action中我做了一次转换。是否与此有关???

在actioin中用list是出于你上次的建议,方便与前台的映射。事实上我曾尝试过用set类型,但总是失败。

cceyjames 2009-06-11
  • 打赏
  • 举报
回复
哇噻,一觉醒来,这么多回复,哈哈,好高兴啊。

Dantin:
问题1:entity里的多余ProductAttributeValue可以设成NULL,只要对应的值改变了,那么就会更新了
多余的ProductAttributeValue可以设成null?请问这个需要手动设置吗?多余的因该自动消失才符合业务逻辑。即使成为null了,也是冗余数据。

问题2:这种错误,往往都是由于数据库中的类型和 javaBean 中类型不匹配产生的
我也怀疑如此,但苦于不知道怎么找证据。


daisycool :
我怀疑是你修改ProductAttributeValue的代码有问题。你只贴出了添加新属性的代码,把修改的代码也放上来看看。

有关修改的代码如下:
Action中:

//问题就与下面的prepareModel中productAttributeValue赋值有关。
@Override
protected void prepareModel() throws Exception
{
if (id != null)
{
entity = productManager.get(id) ;
productAttributeValue = new ArrayList<ProductAttributeValue>(entity.getProductAttributeValues()) ;

}
else
{
entity = new Product() ;
}
}

//input其实也很平常,没什么特别的。下面的input与问题似乎无关。
@Override
public String input() throws Exception
{
allTrades = tradeManager.getAll() ;
selectedTradeId = entity.getTradeId() ;
allCategories = categoryManager.getAll() ;
if (id != null)
{
selectedCategoryId = entity.getCategory().getId() ;
}
return INPUT ;
}


@Override
public String save() throws Exception
{
entity.setTrade(tradeManager.get(selectedTradeId)) ;
entity.setCategory(categoryManager.get(selectedCategoryId)) ;
if (id == null)
{
for (int i = 0; i < productAttributeValue.size(); i++)
{
//如果属于新建产品,则需要在productAttributeValue设置它的product
productAttributeValue.get(i).setProduct(entity) ;
}
}
//转换list为set
Set<ProductAttributeValue> productAttributeValues = new HashSet<ProductAttributeValue>(productAttributeValue) ;
entity.setProductAttributeValues(productAttributeValues) ;
entity.setCreateTime(new java.util.Date()) ;
productManager.save(entity) ;
addActionMessage("保存产品成功") ;
return RELOAD ;
}

productAttributeValue的get/set,略。(普通的get/set)
... ...


前台jsp中,根据产品的分类自动创建若干个input类型的产品属性让用户输入、提交。相关代码:
function initInput()
{
for(var i=0;i<attributeList.length;i++)
{
var id = parseInt(attributeList[0]);
var n = attributeList[i][1];
var t = attributeList[i][2];

var opt = document.createElement('input');
opt.setAttribute('name','productAttributeValue['+i+'].attributeValue');

var idOpt = document.createElement('input');
idOpt.setAttribute('type','hidden');
idOpt.setAttribute('name','productAttributeValue['+i+'].categoryAttribute.id');
idOpt.setAttribute('value',id);

var productIdOpt = document.createElement('input');
productIdOpt.setAttribute('type','hidden');
productIdOpt.setAttribute('name','productAttributeValue['+i+'].product.id');
productIdOpt.setAttribute('value',$("#id").val());

var inputDiv=document.createElement('div');
//取出每一个属性的分类并单独显示
if(i>0)
{
if( t!=attributeList[i-1][2])
{
inputDiv.innerHTML+="<br>";
inputDiv.innerHTML+="<font color=red>"+t+"</font>:<hr>";
}
}
else
{
inputDiv.innerHTML+="<br>";
inputDiv.innerHTML+="<font color=red>"+t+"</font>:<hr>";
}
inputDiv.innerHTML+=n+":";
inputDiv.appendChild(opt);
inputDiv.appendChild(idOpt);
inputDiv.appendChild(productIdOpt);

document.getElementById('attributeDiv').appendChild(inputDiv);
iMaxLength++;
}
}


Landor2004:哇卡卡,你还记得我吗?这是上一个问题的继续。
[i]如果我现在添加了一个产品,只有一个属性。那么我再修改这个产品的时候,修改了产品属性,我们可爱的hibernate级联操作竟然不是update而是insert了一个新的属性

只能是当保存product的时候,productAttributeValues存在数据,所以执行了update和insert

很奇怪哦,难道如你所说这是合理的?正常的?

identifier of an instance of com.xunma.entity.product.CategoryAttribute was altered from 9 to 10;
类型需要检查,比如数据库是bigint而你的类型是int,包括那个字段的类型都检查一下

数据库字段类型和action这边都是正常的、一致的,之所以这么说是因为我没有做任何人为的类型修改。只有一点:就是productAttributeValues我在product的eneity文件中设置的是set类型,而在action中我与前台映射的是list类型,在action中我做了一次转换。是否与此有关???

在actioin中用list是出于你上次的建议,方便与前台的映射。事实上我曾尝试过用set类型,但总是失败。

無名VF 2009-06-11
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 Landor2004 的回复:]
如果我现在添加了一个产品,只有一个属性。那么我再修改这个产品的时候,修改了产品属性,我们可爱的hibernate级联操作竟然不是update而是insert了一个新的属性

只能是当保存product的时候,productAttributeValues存在数据,所以执行了update和insert

identifier of an instance of com.xunma.entity.product.CategoryAttribute was altered from 9 to 10;
类型需要检查,比如数据库是bigint而你的类型是int,包括那个…
[/Quote]
UP
Landor2004 2009-06-11
  • 打赏
  • 举报
回复
如果我现在添加了一个产品,只有一个属性。那么我再修改这个产品的时候,修改了产品属性,我们可爱的hibernate级联操作竟然不是update而是insert了一个新的属性

只能是当保存product的时候,productAttributeValues存在数据,所以执行了update和insert

identifier of an instance of com.xunma.entity.product.CategoryAttribute was altered from 9 to 10;
类型需要检查,比如数据库是bigint而你的类型是int,包括那个字段的类型都检查一下
daisycool 2009-06-11
  • 打赏
  • 举报
回复
我怀疑是你修改ProductAttributeValue的代码有问题。你只贴出了添加新属性的代码,把修改的代码也放上来看看。
Dantin 2009-06-11
  • 打赏
  • 举报
回复
问题1:entity里的多余ProductAttributeValue可以设成NULL,只要对应的值改变了,那么就会更新了
问题2:这种错误,往往都是由于数据库中的类型和 javaBean 中类型不匹配产生的
网络科技 2009-06-11
  • 打赏
  • 举报
回复
对hibernate也不是很熟,帮顶了
Landor2004 2009-06-11
  • 打赏
  • 举报
回复
呵呵,记得呀

第一个问题,就是productAttributeValues肯定存在数据了,所以就执行了insert

第二个问题是前台是list,后台莫不如直接改成collection了,呵呵,

67,515

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧