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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 08:05:59  来源:igfitidea点击:

hibernate update JPA foreign key

javahibernatejpa

提问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 updatableand insertableto falsein the Embeddableclass, and remove them from the join columns

设置updatableinsertablefalseEmbeddable类,并删除它们从连接列

回答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”的解决方法