外键必须与引用的主键具有相同的数字列

时间:2015-01-19 20:18:42

标签: java hibernate foreign-keys hibernate-mapping composite-key

我有以下实体。

EntityA

package misc;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class EntityA implements Serializable {

    private static final long serialVersionUID = -6740580086652468946L;

    @Id
    private int counter; 

}

EntityB

package misc;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class EntityB implements Serializable {

    private static final long serialVersionUID = -1919957640470940855L;

    @Id
    private String name;
}

EntityC

package misc;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class EntityC implements Serializable {

    private static final long serialVersionUID = -4676412878199289178L;

    @Id
    private String name;
}

HistoricalEntity

package misc;

import java.io.Serializable;
import java.util.List;

import javax.persistence.CollectionTable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OrderColumn;
import javax.persistence.UniqueConstraint;

@Entity
public class HistoricalEntity implements Serializable {

    private static final long serialVersionUID = 4889420648637014496L;

    @Id
    @ManyToOne
    @JoinColumn
    private EntityA entityA;

    @Id
    @ManyToOne
    @JoinColumn
    private EntityB entityB;

    @ManyToMany
    @CollectionTable(uniqueConstraints = { @UniqueConstraint(columnNames = {
            "HISTORICAL_ENTITY_ENTITY_A", "HISTORICAL_ENTITY_ENTITY_B",
            "HISTORY_ID", }) })
    @OrderColumn(name = "POSITION")
    private List<EntityC> history;
}

...和配置为空测试...

package misc;

import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
public class TestConfiguration {

    @Bean
    public DataSource dataSource() {
        final BasicDataSource dataSource = new BasicDataSource();
        dataSource.setUrl("jdbc:postgresql://localhost:5432/mydb");
        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setUsername("postgres");
        dataSource.setPassword("postgres");

        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        final LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setDataSource(dataSource());
        emf.setPackagesToScan("pl.xtra_klasa.utils.total.test");
        final Map<String, Object> propertyMap = emf.getJpaPropertyMap();
        propertyMap.put(org.hibernate.cfg.Environment.SHOW_SQL, true);
        propertyMap.put(org.hibernate.cfg.Environment.FORMAT_SQL, true);
        propertyMap.put(org.hibernate.cfg.Environment.HBM2DDL_AUTO, "create");
        propertyMap.put(org.hibernate.cfg.Environment.DIALECT,
                "org.hibernate.dialect.PostgreSQLDialect");
        propertyMap.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");

        final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        emf.setJpaVendorAdapter(vendorAdapter);

        return emf;
    }

    @Bean
    public EntityManager entityManager(EntityManagerFactory entityManagerFactory) {
        return entityManagerFactory.createEntityManager();
    }

    @Bean
    public JpaTransactionManager transactionManager() {
        final JpaTransactionManager tm = new JpaTransactionManager(
                entityManagerFactory().getObject());
        return tm;
    }
}

Junit测试

package misc;

import javax.transaction.Transactional;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfiguration.class)
@Transactional
public class MyTest {

    @Test
    public void test() {

    }
}

测试以以下异常结束,我不知道为什么会这样。

  

外键(FK_85k7tuu55uee70x387oekb6gj:historical_entity_history [historical_entity]))必须与引用的主键具有相同的列数(historical_entity [entityb,entitya])

我必须补充说一切正常但我决定切换到org.hibernate.cfg.ImprovedNamingStrategy并删除了我用@Table(name = "TABLE_NAME")给表格的所有显式名称。

我现在已经挣扎了几个多小时了。我怎样才能找到问题所在?

修改

仅使用history注释属性@ElementCollection会导致同样的错误。

...
@ElementCollection
private List<EntityC> history;
...

1 个答案:

答案 0 :(得分:0)

我自己找到了解决方案。需要明确指定连接列。

@ManyToMany
    @CollectionTable(joinColumns = { @JoinColumn(name = "entityA"),
            @JoinColumn(name = "entityB") }, uniqueConstraints = { @UniqueConstraint(columnNames = {
            "entityA", "entityB", "history" }) })
    @OrderColumn(name = "POSITION")
    private List<EntityC> history;