Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

hibernate update JPA foreign key

My jpa looks like below

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?


does it related to because i'm using embededId (composite primary keys) ?

updated

if i set upadted=true, i get below error

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.
like image 571
cometta Avatar asked Mar 18 '10 07:03

cometta


2 Answers

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 ?

issDivision field stores a foreign key column called 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)

@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

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.

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;

Because of that, you see this nice message

repeated column... blah, blah, blah... (should be mapped with insert="false" update="false")

So i suppose your compound primary key called IssTESTPK also stores a column called SURVEY_NUM. And it is not a good idea you define a compound primary key property as insert="false" update="false". Avoid a lot of headache.

Keep in mind: when more than one property shares the same column, define one of them as insertable=false, updatable=false. Nothing else.

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 support automatic generation of compound primary key

You must provide its values before saving. Keep it in mind

Let's see Employee compound primary key

@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

}

Before saving an Employee, You must provide its values. As said above, Hibernate does not support automatic generation of compound primary key

Hibernate does not allow you update a (compound) primary key It does not make sense.

Its values can not be null

So, According to described above, our EmployeeId can be written as

@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

But we can not mark a compound primary key property as insertable=false, updatable=false because of Hibernate uses it to save our Entity

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

// 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;

When you have a compound foreign key, we can not mix insertable-non-insertable or updatable-non-updatable.

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

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 marked as insertable=false, updatable=false to avoid the exception shown above

// 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

// 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_CODE     SURVEY_NUMBER
1                 10
2                 11
3                 12
4                 13
5                 14

remember: There is a foreign key constraint between Division and Employee

You think of

I can not change division property because of insertable=false, updatable=false. But i can change divisionCode property (and its DIVISION_CODE column) as a way to change the foreign key column called DIVISION_CODE

You do the following code

employee.setDivisionCode(7);

Wow, see above in DIVISION_TABLE. Is There some value in DIVISION_CODE column equal to 7 ?

answer is clear: no (You will see a CONSTRAINT VIOLATION)

So, it is an inconsistent mapping. Hibernate does not allow it.

regards,

like image 85
9 revs Avatar answered Nov 03 '22 02:11

9 revs


It is because you are using updatable = false. Removing it might lead other problems, which I can't assume not knowing your complete mapping.

Set the updatable and insertable to false in the Embeddable class, and remove them from the join columns

like image 2
Bozho Avatar answered Nov 03 '22 00:11

Bozho