Hibernate обновление JPA внешнего ключа - PullRequest
9 голосов
/ 18 марта 2010

Мой 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;

}

если я внесу изменение в '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.

Ответы [ 3 ]

15 голосов
/ 23 марта 2010

Ну, посмотрим

Ваше исключительное (и известное) сообщение:

repeated column in mapping for entity:
column: SURVEY_NUM (should be mapped with insert="false" update="false")

Где находится столбец 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;

Теперь посмотрите следующее отображение ( см. 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;
    }

}

Теперь давайте сделаем следующее

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

entityManager.persist(account);

Hibernate попросит вас

Какое свойство мне следует сохранить , если оба свойства имеют один и тот же столбец ??? И, как я вижу, свойство id хранит ненулевое значение, а accountNumber - нулевое значение.

Должен ли я выполнить такой запрос ???

INSERT INTO ACCOUNT (ACCOUNT_NUMBER, ACCOUNT_NUMBER) VALUES (127359, NULL);

Это не имеет смысла. Следовательно, это неправильный запрос SQL;

Из-за этого вы видите это милое сообщение

повторный столбец ... бла, бла, бла ... (должен отображаться с помощью insert = "false" update = "false")

Итак, Полагаю, ваш составной первичный ключ с именем IssTESTPK также хранит столбец с именем SURVEY_NUM. И не очень хорошая идея вы определяете составное свойство первичного ключа как insert = "false" update = "false". Избегайте сильной головной боли.

Имейте в виду: если более одного свойства совместно используют один и тот же столбец, определите одно из них как inserttable = 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
    }

}

UPDATE


Перед тем как продолжить

Hibernate не поддерживает автоматическое создание составного первичного ключа

Вы должны предоставить свои значения перед сохранением. Имейте это в виду

Посмотрим составной первичный ключ сотрудника

@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

}

Перед сохранением сотрудника, вы должны предоставить его значения. Как сказано выше, Hibernate не поддерживает автоматическую генерацию составного первичного ключа

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

}

Как сказал

когда более одного свойства совместно используют один и тот же столбец, определите одно из них как inserttable = false, updatable = false. Ничего другого

Но мы не можем пометить составное свойство первичного ключа как вставляемое = ложное, обновляемое = ложное , поскольку Hibernate использует его для сохранения нашей сущности

Поскольку Hibernate будет использовать наше составное свойство первичного ключа, называемое surveyNumber (и его столбец SURVEY_NUMBER), для выполнения SQL-операции с базой данных, нам нужно переписать наше свойство деления @ManyToOne (и его столбец внешнего ключа с именем SURVEY_NUMBER) как вставляемый = 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;

Если у вас есть составной внешний ключ, мы не можем смешивать вставляемый-не вставляемый или обновляемый-не обновляемый.

Что-то вроде

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

В противном случае Hibernate будет жаловаться

Смешивание вставляемых и не вставляемых столбцов в свойстве недопустимо

Из-за этого столбец составного внешнего ключа с именем 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;

Поскольку мы больше не можем обновлять столбец 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;

Ну, посмотрим. Предположим, у нас есть следующая таблица DIVISION

DIVISION TABLE
DIVISION_CODE     SURVEY_NUMBER
1                 10
2                 11
3                 12
4                 13
5                 14

помните: между отделом и сотрудником существует ограничение по внешнему ключу

Вы думаете о

Не могу изменить свойство деления из-за вставляемого = ложного, обновляемого = ложного. Но я могу изменить свойство DivisionCode (и его столбец DIVISION_CODE) как способ изменить столбец внешнего ключа с именем DIVISION_CODE

Вы делаете следующий код

* +1147 * employee.setDivisionCode (7);

Ух ты, смотри выше в DIVISION_TABLE. Есть ли какое-то значение в столбце DIVISION_CODE, равное 7?

ответ ясен: нет (вы увидите ОГРАНИЧЕНИЕ НАРУШЕНИЯ)

Итак, это противоречивое отображение . Hibernate не позволяет этого.

С уважением,

2 голосов
/ 18 марта 2010

Это потому, что вы используете updatable = false. Удаление может привести к другим проблемам, которые я не могу предположить, не зная вашего полного отображения.

Установите updatable и insertable на false в классе Embeddable и удалите их из столбцов соединения

1 голос
/ 19 марта 2010

Я нашел обходной путь, но мне нравится слышать от вас все, можно ли использовать эту технику. я изменяю класс сущности на ..

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

}

Я добавил свойство 'DivisionCode', хотя оно дублировалось внутри объекта issDivision. но это обходной путь, который я могу обновить 'DIVISION_CODDE' при слиянии

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...