hibernate更新JPA外键

com*_*tta 9 java hibernate jpa

我的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;

}
Run Code Online (Sandbox Code Playgroud)

如果我更改为'name'并调用merge,它可以更新到数据库,但是当我更改issDivision并调用merge时,它不会更新数据库.怎么解决这个?


是否与之相关,因为我使用的是embededId(复合主键)?

更新

如果我设置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$1.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$1.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.
Run Code Online (Sandbox Code Playgroud)

Art*_*ald 15

好的,我们等着瞧

您的异常(和着名)消息是

repeated column in mapping for entity:
column: SURVEY_NUM (should be mapped with insert="false" update="false")
Run Code Online (Sandbox Code Playgroud)

SURVEY_NUM专栏在哪里?

issDivision字段存储称为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;
Run Code Online (Sandbox Code Playgroud)

现在,请参阅以下映射(请参阅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;
    }

}
Run Code Online (Sandbox Code Playgroud)

现在让我们这样做

Account account = new Account();
account.setId(127359);
account.setAccountNumber(null);

entityManager.persist(account);
Run Code Online (Sandbox Code Playgroud)

Hibernate会问你

我应该坚持哪个属性是否两个属性共享同一列?正如我所看到的,id属性存储一个非null值,accountNumber存储一个空值.

我应该执行这样的查询???

INSERT INTO ACCOUNT (ACCOUNT_NUMBER, ACCOUNT_NUMBER) VALUES (127359, NULL);
Run Code Online (Sandbox Code Playgroud)

它没有任何意义.因此,它是一个错误的SQL查询;

因此,你会看到这个好消息

重复列...等等,等等,等等...(应使用insert ="false"update ="false"进行映射)

所以我想你的复合主键IssTESTPK也存储一个名为SURVEY_NUM的列.并且复合主键属性定义为insert ="false"update ="false" 并不是一个好主意.避免很多头痛.

请记住:当多个属性共享同一列时,将其中一个定义为insertable = false,updatable = false.没有别的.

我认为你的复合主键类看起来应该是这样的

@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
    }

}
Run Code Online (Sandbox Code Playgroud)

UPDATE


继续之前

Hibernate 不支持自动生成复合主键

必须在保存之前提供其值.记在心上

让我们看一下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

}
Run Code Online (Sandbox Code Playgroud)

在保存员工之前,您必须提供其值.如上所述,Hibernate不支持自动生成复合主键

2ºHibernate不允许更新(复合)主键它没有意义.

其值不能为空

因此,根据上述情况,我们的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

}
Run Code Online (Sandbox Code Playgroud)

如上所述

当多个属性共享同一列时,将其中一个定义为insertable = false,updatable = false.没有其他的

但是,我们不能标记复合主键属性作为插入=假,可更新=假,因为Hibernate使用它来拯救我们的实体

由于Hibernate将使用名为surveyNumber(及其SURVEY_NUMBER列)的复合主键属性对数据库执行SQL操作,我们需要将@ManyToOne除法属性(及其外键列名为SURVEY_NUMBER)重写为insertable = false,可更新= 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;
Run Code Online (Sandbox Code Playgroud)

当你有一个复合外键时,我们不能混合 insertable-non-insertable或updatable-non-updatable.

就像是

@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;
Run Code Online (Sandbox Code Playgroud)

否则,Hibernate会抱怨

不允许在属性中混合可插入和不可插入的列

因此,它的复合外键列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;
Run Code Online (Sandbox Code Playgroud)

由于我们不能再更新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;
Run Code Online (Sandbox Code Playgroud)

好的,我们等着瞧.假设我们有以下DIVISION TABLE

DIVISION TABLE
DIVISION_CODE     SURVEY_NUMBER
1                 10
2                 11
3                 12
4                 13
5                 14
Run Code Online (Sandbox Code Playgroud)

记住:Division和Employee之间存在外键约束

你想到了

由于insertable = false,updatable = false,我无法更改除法属性.但我可以更改divisionCode属性(及其DIVISION_CODE列)作为更改名为DIVISION_CODE的外键列的方法

您执行以下代码

employee.setDivisionCode(7);

哇,见上面的DIVISION_TABLE.DIVISION_CODE列中的某些值是否等于7?

答案很清楚:没有(你会看到一个约束违规)

所以,这是一个不一致的映射.Hibernate不允许它.

问候,