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

Foreign key must have the same number columns as the referenced primary key

我有以下实体。

实体A

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; 

}

实体B

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

实体C

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

历史实体

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

联合测试

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() {

    }
}

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

Foreign key (FK_85k7tuu55uee70x387oekb6gj:historical_entity_history [historical_entity])) must have same number of columns as the referenced primary key (historical_entity [entityb,entitya])

我必须补充一点,一切正常,但我决定切换到 org.hibernate.cfg.ImprovedNamingStrategy 并删除了我为带有 @Table(name = "TABLE_NAME").

的表指定的所有显式名称

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

编辑

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

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

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

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