Java 休眠更新JPA外键
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2468106/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
hibernate update JPA foreign key
提问by cometta
My jpa looks like below
我的 jpa 如下所示
public class TESTClass implements Serializable {
...
private String name;
@EmbeddedId
protected IssTESTPK issTESTPK;
@ManyToOne(optional=false)
@JoinColumns({
@JoinColumn(name="DIVISION_CODE", referencedColumnName="DIVISION_CODE", nullable=false, insertable=false, updatable=false),
@JoinColumn(name="SURVEY_NUM", referencedColumnName="SURVEY_NUM", nullable=false, insertable=false, updatable=false)})
private IssDivision issDivision;
}
if i make change to 'name' and call merge, it able to update into database, but when i change issDivision, and call merge, it doesnt update database. how to solve this?
如果我更改'name'并调用merge,它能够更新到数据库中,但是当我更改issDivision并调用merge时,它不会更新数据库。如何解决这个问题?
does it related to because i'm using embededId (composite primary keys) ?
是否与因为我使用 embededId(复合主键)有关?
updated
更新
if i set upadted=true, i get below error
如果我设置 upadted=true,则会出现以下错误
ERROR - ContextLoader.initWebApplicationContext(215) | Context initialization fa
iled
org.springframework.beans.factory.BeanCreationException: Error creating bean wit
h name 'sessionFactory' defined in ServletContext resource [/WEB-INF/application
Context.xml]: Invocation of init method failed; nested exception is org.hibernat
e.MappingException: Repeated column in mapping for entity: com.compay.test.model
.TESTClass column: SURVEY_NUM (should be mapped with insert="false" update="fa
lse")
at org.springframework.beans.factory.support.AbstractAutowireCapableBean
Factory.initializeBean(AbstractAutowireCapableBeanFactory.java:1338)
at org.springframework.beans.factory.support.AbstractAutowireCapableBean
Factory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)
at org.springframework.beans.factory.support.AbstractAutowireCapableBean
Factory.run(AbstractAutowireCapableBeanFactory.java:409)
at java.security.AccessController.doPrivileged(Native Method)
at org.springframework.beans.factory.support.AbstractAutowireCapableBean
Factory.createBean(AbstractAutowireCapableBeanFactory.java:380)
at org.springframework.beans.factory.support.AbstractBeanFactory.getOb
ject(AbstractBeanFactory.java:264)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistr
y.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBe
an(AbstractBeanFactory.java:261)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean
(AbstractBeanFactory.java:185)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean
(AbstractBeanFactory.java:164)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.
preInstantiateSingletons(DefaultListableBeanFactory.java:423)
at org.springframework.context.support.AbstractApplicationContext.finish
BeanFactoryInitialization(AbstractApplicationContext.java:728)
at org.springframework.context.support.AbstractApplicationContext.refres
h(AbstractApplicationContext.java:380)
at org.springframework.web.context.ContextLoader.createWebApplicationCon
text(ContextLoader.java:255)
at org.springframework.web.context.ContextLoader.initWebApplicationConte
xt(ContextLoader.java:199)
at org.springframework.web.context.ContextLoaderListener.contextInitiali
zed(ContextLoaderListener.java:45)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContex
t.java:3843)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4
342)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase
.java:791)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:77
1)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:525)
at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.ja
va:627)
at org.apache.catalina.startup.HostConfig.deployDescriptors(HostConfig.j
ava:553)
at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:488
)
at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1149)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java
:311)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(Lifecycl
eSupport.java:117)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:719)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443
)
at org.apache.catalina.core.StandardService.start(StandardService.java:5
16)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:710
)
at org.apache.catalina.startup.Catalina.start(Catalina.java:578)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.
采纳答案by Arthur Ronald
Well, Let's see
走着瞧
Your exception (and famous) message is
你的例外(和著名的)信息是
repeated column in mapping for entity:
column: SURVEY_NUM (should be mapped with insert="false" update="false")
Where does SURVEY_NUM column live ?
SURVEY_NUM 列在哪里?
1oissDivision field stores a foreign key column called SURVEY_NUM
1oissDivision 字段存储一个名为 SURVEY_NUM 的外键列
@ManyToOne
@JoinColumns({
@JoinColumn(name="DIVISION_CODE", referencedColumnName="DIVISION_CODE", insertable=false, updatable=false),
@JoinColumn(name="SURVEY_NUM", referencedColumnName="SURVEY_NUM", insertable=false, updatable=false)})
private IssDivision issDivision;
Now, see the following mapping (see both id and accountNumber shares the same column)
现在,查看以下映射(查看 id 和 accountNumber 共享同一列)
@Entity
public class Account {
private Integer id;
private Integer accountNumber;
@Id
@Column(name="ACCOUNT_NUMBER")
public Integer getId() {
return this.id;
}
@Column(name="ACCOUNT_NUMBER")
public Integer getAccountNumber() {
return this.accountNumber;
}
}
Now let's do as follows
现在让我们做如下
Account account = new Account();
account.setId(127359);
account.setAccountNumber(null);
entityManager.persist(account);
Hibernate will ask to you
Hibernate 会问你
Which property should i persist whether both properties shares the same column??? And as i can see, id property stores a non-null value and accountNumber a null value.
无论两个属性共享同一列,我应该坚持哪个属性???正如我所看到的, id 属性存储一个非空值, accountNumber 一个空值。
Should i perform a query like this one ???
我应该执行这样的查询吗???
INSERT INTO ACCOUNT (ACCOUNT_NUMBER, ACCOUNT_NUMBER) VALUES (127359, NULL);
It does not make sense. Therefore, it is a wrong SQL query;
它没有任何意义。因此,这是一个错误的 SQL 查询;
Because of that, you see this nice message
正因为如此,你会看到这个好消息
repeated column... blah, blah, blah... (should be mapped with insert="false" update="false")
重复列......等等,等等,等等......(应该用 insert="false" update="false" 映射)
So i supposeyour compound primary key called IssTESTPK also stores a column called SURVEY_NUM. And it is not a good ideayou define a compound primary key property as insert="false" update="false". Avoid a lot of headache.
所以我想你的复合主键 IssTESTPK 也存储了一个名为 SURVEY_NUM 的列。而这是不是一个好主意,你定义一个复合主键属性作为插入=“假”更新=“假”。避免很多头痛。
Keep in mind: when more than one property shares the same column, define one of them as insertable=false, updatable=false. Nothing else.
请记住:当多个属性共享同一列时,请将其中之一定义为 insertable=false、updatable=false。没有别的。
I think your compound primary key class should looks like this one
我认为你的复合主键类应该是这样的
@Embeddable
public class IssTESTPK implements Serializable {
// Ops... Our missing field which causes our Exception (repeated column... blah, blah, blah...)
@Column(name="SURVEY_NUM", nullable=false)
private Integer property;
private Integer otherProperty;
private Integer anotherProperty;
// required no-arg constructor
public IssTESTPK() {}
// You must implement equals and hashcode
public boolean equals(Object o) {
if(o == null)
return false;
if(!(o instanceof IssTESTPK))
return false;
IssTESTPK other = (IssTESTPK) o;
if(!(getProperty().equals(other.getProperty())))
return false;
if(!(getOtherProperty().equals(other.getOtherProperty())))
return false;
if(!(getAnotherProperty().equals(other.getAnotherProperty())))
return false;
return true;
}
// NetBeans or Eclipse will worry about it
public int hashcode() {
// hashcode code goes here
}
}
UPDATE
更新
Before going on
在继续之前
Hibernate does not supportautomatic generation of compound primary key
Hibernate不支持自动生成复合主键
You mustprovide its values before saving. Keep it in mind
您必须在保存前提供其值。记在心上
Let's see Employee compound primary key
让我们看看Employee复合主键
@Embeddable
public class EmployeeId implements Serializable {
@Column(name="EMPLOYEE_NUMBER")
private String employeeNumber;
@Column(name="SURVEY_NUMBER")
private BigInteger surveyNumber;
// getter's and setter's
// equals and hashcode
}
1oBefore saving an Employee, You mustprovide its values. As said above, Hibernate does not support automatic generation of compound primary key
1o在保存员工之前,您必须提供其值。如上所述,Hibernate 不支持自动生成复合主键
2oHibernate does not allow you update a (compound) primary key It does not make sense.
2oHibernate 不允许您更新(复合)主键它没有意义。
3oIts values can not be null
3o其值不能为空
So, According to described above, our EmployeeId can be written as
所以,根据上面的描述,我们的 EmployeeId 可以写成
@Embeddable
public class EmployeeId implements Serializable {
@Column(name="EMPLOYEE_NUMBER", nullable=false, updatable=false)
private String employeeNumber;
@Column(name="SURVEY_NUMBER", nullable=false, updatable=false)
private BigInteger surveyNumber;
// getter's and setter's
// equals and hashcode
}
As said
正如所说
when more than one property shares the same column, define one of them as insertable=false, updatable=false. Nothing else
当多个属性共享同一列时,将其中一个定义为 insertable=false、updatable=false。没有其他的
But we can not mark a compound primary key property as insertable=false, updatable=false because of Hibernate uses it to save our Entity
但是我们不能将复合主键属性标记为 insertable=false, updatable=false因为 Hibernate 使用它来保存我们的实体
As Hibernate will use our compound primary key property called surveyNumber (and its SURVEY_NUMBER column) to perform SQL operation on database, we need to re-write our @ManyToOne division property (and its foreign key column called SURVEY_NUMBER) as insertable=false, updatable=false
由于 Hibernate 将使用我们称为surveyNumber 的复合主键属性(及其 SURVEY_NUMBER 列)对数据库执行 SQL 操作,因此我们需要将我们的 @ManyToOne 除法属性(及其名为 SURVEY_NUMBER 的外键列)重写为 insertable=false,可更新=假
// Employee.java
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumns({
@JoinColumn(name="DIVISION_CODE", referencedColumnName="DIVISION_CODE"),
@JoinColumn(name="SURVEY_NUMBER", referencedColumnName="SURVEY_NUMBER", insertable=false, updatable=false)})
private Division division;
4oWhen you have a compound foreign key, we can not mixinsertable-non-insertable or updatable-non-updatable.
4o当你有一个复合外键时,我们不能混合插入-不可插入或可更新-不可更新。
Something like
就像是
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumns({
// I can be updatable
@JoinColumn(name="DIVISION_CODE", referencedColumnName="DIVISION_CODE", insertable=false),
// And i can be insertable
@JoinColumn(name="SURVEY_NUMBER", referencedColumnName="SURVEY_NUMBER", updatable=false)})
private Division division;
Otherwise, Hibernate will complain
否则,Hibernate 会抱怨
Mixing insertable and non insertable columns in a property is not allowed
不允许在属性中混合可插入和不可插入的列
Because of that, Its compound foreign key column called DIVISION_CODE should also be markedas insertable=false, updatable=false to avoid the exception shown above
因此,其名为 DIVISION_CODE 的复合外键列也应标记为 insertable=false、updatable=false 以避免出现上述异常
// Employee.java
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumns({
@JoinColumn(name="DIVISION_CODE", referencedColumnName="DIVISION_CODE", insertable=false, updatable=false),
@JoinColumn(name="SURVEY_NUMBER", referencedColumnName="SURVEY_NUMBER", insertable=false, updatable=false)})
private Division division;
As we can not update DIVISION_CODE column anymore, our division property behaves like a constant. You, then, think of creating a new property called divisionCode to change, someway, DIVISION_CODE column, as follows
由于我们不能再更新 DIVISION_CODE 列,我们的除法属性就像一个常量。然后,您可以考虑创建一个名为 DivisionCode 的新属性来更改 DIVISION_CODE 列,如下所示
// Employee.java
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumns({
@JoinColumn(name="DIVISION_CODE", referencedColumnName="DIVISION_CODE", insertable=false, updatable=false),
@JoinColumn(name="SURVEY_NUMBER", referencedColumnName="SURVEY_NUMBER", insertable=false, updatable=false)})
private Division division;
// Wow, now i expect i can change the value of DIVISION_CODE column
@Column(name="DIVISION_CODE")
private BigInteger divisionCode;
Well, Let's see. Suppose we have the following DIVISION TABLE
走着瞧。假设我们有以下 DIVISION TABLE
DIVISION TABLE
DIVISION_CODE SURVEY_NUMBER
1 10
2 11
3 12
4 13
5 14
remember: There is a foreign key constraint between Division and Employee
记住: Division 和 Employee 之间有外键约束
You think of
你想到
I can not change divisionproperty because of insertable=false, updatable=false. But i can change divisionCodeproperty (and its DIVISION_CODE column) as a way to change the foreign key column called DIVISION_CODE
由于可插入=假,可更新=假,我无法更改除法属性。但是我可以更改 DivisionCode属性(及其 DIVISION_CODE 列)作为更改名为 DIVISION_CODE 的外键列的一种方式
You do the following code
你执行以下代码
employee.setDivisionCode(7);
员工.setDivisionCode(7);
Wow, see above in DIVISION_TABLE. Is There some value in DIVISION_CODE column equal to 7 ?
哇,见上面的 DIVISION_TABLE。DIVISION_CODE 列中是否有等于 7 的值?
answer is clear: no (You will see a CONSTRAINT VIOLATION)
答案很明确:否(您将看到 CONSTRAINT VIOLATION)
So, it is an inconsistent mapping. Hibernate does not allow it.
所以,这是一个不一致的映射。Hibernate 不允许。
regards,
问候,
回答by Bozho
It is because you are using updatable = false
. Removing it might lead other problems, which I can't assume not knowing your complete mapping.
这是因为您正在使用updatable = false
. 删除它可能会导致其他问题,我不能假设不知道您的完整映射。
Set the updatable
and insertable
to false
in the Embeddable
class, and remove them from the join columns
设置updatable
并insertable
以false
在Embeddable
类,并删除它们从连接列
回答by cometta
i found a workaround, but i like to hear from you all whether is ok to use this technique. i modify the entity class to..
我找到了一个解决方法,但我想听听大家是否可以使用这种技术。我将实体类修改为..
public class TESTClass implements Serializable {
...
private String name;
@EmbeddedId
protected IssTESTPK issTESTPK;
@JoinColumns({@JoinColumn(name = "DIVISION_CODE", referencedColumnName = "DIVISION_CODE", nullable = false , insertable = false, updatable = false), @JoinColumn(name = "SURVEY_NUM", referencedColumnName = "SURVEY_NUM", nullable = false, insertable = false, updatable = false)})
@ManyToOne(optional = false)
private IssDivision issDivision; //since this is not merge
@Basic(optional = false)
@Column(name = "DIVISION_CODE")
private String divisionCode; //i add this
}
i added 'divisionCode' property, even though it duplicated inside issDivision object. but this is the workaround i able to update 'DIVISION_CODDE' when during merge
我添加了 'divisionCode' 属性,即使它在 issDivision 对象中重复。但这是我能够在合并期间更新“DIVISION_CODDE”的解决方法