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(Defaul开发者_开发知识库tSingletonBeanRegistry.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.
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 ?
1º 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
}
1º Before saving an Employee, You must provide its values. As said above, Hibernate does not support automatic generation of compound primary key
2º Hibernate does not allow you update a (compound) primary key It does not make sense.
3º 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;
4º 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,
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
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
精彩评论