1

我在使用 @MapKey 注释使用 Map 映射 @ManyToMany 关系时遇到问题,该关系包含目标实体属性作为映射条目键和目标实体对象本身作为映射条目值。

数据库模式(没有 PK、FK 和唯一约束)是:

应用表:

CREATE TABLE "APM_APPLICATION" (
    "ID_APPLICATION" BIGINT NOT NULL,
    "NAME" VARCHAR(64) NOT NULL,
    "DESCRIPTION" VARCHAR(1024));

功能表:

CREATE TABLE "APM_FUNCTION" (
    "ID_FUNCTION" BIGINT NOT NULL,
    "ID_APPLICATION" BIGINT NOT NULL,
    "NAME" VARCHAR(64) NOT NULL,
    "DESCRIPTION" VARCHAR(1024));

参数表:

CREATE TABLE "APM_PARAM" (
    "ID_PARAM" BIGINT NOT NULL,
    "ID_APPLICATION" BIGINT NOT NULL,
    "NAME" VARCHAR(64) NOT NULL,
    "DESCRIPTION" VARCHAR(1024),
    "VALUE" VARCHAR(64));

函数和参数连接表(带有复合 PK):

CREATE TABLE "APM_FUNCTION_PARAM" (
    "ID_FUNCTION" BIGINT NOT NULL,
    "ID_PARAM" BIGINT NOT NULL);

域模型(没有构造函数、getter、setter 和 JPA id 生成设置)是:

申请实体:

@Entity
@Table(name = "APM_APPLICATION")
public class Application {

@Id
@Column(name = "ID_APPLICATION")
private Long id;

@Column(name = "NAME")
private String name;

@Column(name = "DESCRIPTION")
private String description;

@OneToMany(mappedBy = "application")
@MapKey(name = "name")
private Map<String, Parameter> parameters = new HashMap<String, Parameter>();

@OneToMany(mappedBy = "application")
@MapKey(name = "name")
private Map<String, Function> functions = new HashMap<String, Function>();

// constructors, getters and setters omitted

public String toString() {
    return "Application[id='" + id +"', " +
            "name='" + name + "', " +
            "parametersCount='" +parameters.size() + "', " +
            "functionsCount='" + functions.size() + "']";
}

功能实体:

@Entity
@Table(name = "APM_FUNCTION")
public class Function {

@Id
@Column(name = "ID_FUNCTION")
private Long id;

@ManyToOne
@JoinColumn(name = "ID_APPLICATION")
private Application application;

@Column(name = "NAME")
private String name;

@Column(name = "DESCRIPTION")
private String description;

@ManyToMany
@JoinTable(name = "APM_FUNCTION_PARAM",
        joinColumns = @JoinColumn(name = "ID_FUNCTION"),
        inverseJoinColumns = @JoinColumn(name = "ID_PARAM"))
@MapKey(name = "name")
private Map<String, Parameter> parameters = new HashMap<String, Parameter>();

// constructors, getters and setters omitted

public String toString() {
    return "Function[id='" + id + "', " +
            "application='" + application + "', " +
            "name='" + name +"', " +
            "parametersCount='" + parameters.size() + "']";
}

参数实体:

@Entity
@Table(name = "APM_PARAM")
public class Parameter {

@Id
@Column(name = "ID_PARAM")
private Long id;

@ManyToOne
@JoinColumn(name = "ID_APPLICATION")
private Application application;

@ManyToMany(mappedBy = "parameters")
@MapKey(name = "name")
private Map<String, Function> functions = new HashMap<String, Function>();

@Column(name = "NAME")
private String name;

@Column(name = "VALUE")
private String value;

// constructors, getters and setters omitted

public String toString() {
    return "Parameter[id='" + id + "', " +
            "application='" + application + "', " +
            "name='" + name + "', " +
            "value='" + value + "', " +
            "functionsCount='" + functions.size() + "']";
}

其他配置是:

pom.xml:

<dependencies>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>${slf4j.version}</version>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jul-to-slf4j</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <!-- Spring Framework -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!-- Spring Data JPA -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-jpa</artifactId>
        <version>1.3.0.RELEASE</version>
    </dependency>
    <!-- Spring ORM -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>org.springframework.orm</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
        <scope>test</scope>
    </dependency>
    <!-- Apache Commons Connection Pool -->
    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.4</version>
    </dependency>
    <!-- JPA api -->
    <dependency>
        <groupId>org.hibernate.javax.persistence</groupId>
        <artifactId>hibernate-jpa-2.0-api</artifactId>
        <version>1.0.1.Final</version>
    </dependency>
    <!-- Apache Derby -->
    <dependency>
        <groupId>org.apache.derby</groupId>
        <artifactId>derby</artifactId>
        <version>${derby.version}</version>
    </dependency>
    <!-- Hibernate -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>${hibernate.version}</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>${hibernate.version}</version>
    </dependency>
</dependencies>
<properties>   
    <spring.version>3.2.3.RELEASE</spring.version>
    <derby.version>10.10.1.1</derby.version>
    <hibernate.version>4.1.9.Final</hibernate.version>
    <xdef.version>2.0.53.88</xdef.version>
</properties>

持久性.xml:

<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <persistence-unit name="apmPersistenceUnit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.DerbyTenSevenDialect"/>
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.globally_quoted_identifiers" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

弹簧master.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xsi:schemaLocation=" http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context.xsd">
    <import resource="spring-jpa.xml" />
    <context:property-placeholder location="META-INF/configuration.properties" />
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${apm.db.driverClassName}"/>
        <property name="url" value="${apm.db.url}"/>
        <property name="initialSize" value="${apm.db.connPoolInitialSize}"/>
        <property name="maxActive" value="${apm.db.connPoolMaxActive}"/>
    </bean>
</beans>

弹簧-jpa.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
   xmlns:jpa="http://www.springframework.org/schema/data/jpa"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
    <jpa:repositories base-package="cz.syntea.apm.repository" />
    <bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
      depends-on="dbCreator">
        <property name="dataSource" ref="dataSource"/>
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
        </property>
    </bean>
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    <tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
</beans>

一切正常,直到我尝试访问 Function 对象中的参数映射。

执行此测试代码后(JUnit + Spring Test,appService 是服务层组件,使用 Spring Data JpaRepository):

@Test
@Transactional
public void testApplicationInfo() {
    List<Application> apps = appService.getApplications();
    for (Application app : apps) {
        System.out.println("Application: " + app);
        System.out.println("Parameters:" );
        for (Parameter p : app.getParameters().values()) {
            System.out.println("Parameter: " + p);
        }
        System.out.println("Functions:");
        for (Function f : app.getFunctions().values()) {
            System.out.println("Function: " + f); // exception is thrown in toString method, that tries to access parameters map
            for (Parameter p : f.getParameters().values()) {
                System.out.println("Parameter: " + p);
            }
        }
    }
}

我收到以下异常:

Hibernate: select applicatio0_."ID_APPLICATION" as ID1_6_, applicatio0_."DESCRIPTION" as DESCRIPT2_6_, applicatio0_."NAME" as NAME3_6_ from "APM_APPLICATION" applicatio0_
Hibernate: select parameters0_.ID_APPLICATION as ID5_6_2_, parameters0_."ID_PARAM" as ID1_5_2_, parameters0_."NAME" as formula3_2_, parameters0_."ID_PARAM" as ID1_5_1_, parameters0_.ID_APPLICATION as ID5_5_1_, parameters0_."NAME" as NAME2_5_1_, parameters0_."RELATION" as RELATION3_5_1_, parameters0_."DATA_TYPE" as DATA4_5_1_, parameters0_.ID_VALUE as ID6_5_1_, value1_."ID_VALUE" as ID1_1_0_, value1_."BOOLEAN_SET" as BOOLEAN2_1_0_, value1_."DOUBLE_VALUE" as DOUBLE3_1_0_, value1_."INTEGER_VALUE" as INTEGER4_1_0_ from "APM_PARAM" parameters0_ left outer join "APM_VALUE" value1_ on parameters0_.ID_VALUE=value1_."ID_VALUE" where parameters0_.ID_APPLICATION=?
Hibernate: select functions0_.ID_APPLICATION as ID4_6_1_, functions0_."ID_FUNCTION" as ID1_7_1_, functions0_."NAME" as formula2_1_, functions0_."ID_FUNCTION" as ID1_7_0_, functions0_.ID_APPLICATION as ID4_7_0_, functions0_."DESCRIPTION" as DESCRIPT2_7_0_, functions0_."NAME" as NAME3_7_0_ from "APM_FUNCTION" functions0_ where functions0_.ID_APPLICATION=?
Application: Application[id='25050', name='TST_APP_1', parametersCount='2', functionsCount='2']
Parameters:
Hibernate: select functions0_.ID_PARAM as ID2_5_2_, functions0_.ID_FUNCTION as ID1_11_2_, function1_."ID_FUNCTION" as ID1_7_0_, function1_.ID_APPLICATION as ID4_7_0_, function1_."DESCRIPTION" as DESCRIPT2_7_0_, function1_."NAME" as NAME3_7_0_, applicatio2_."ID_APPLICATION" as ID1_6_1_, applicatio2_."DESCRIPTION" as DESCRIPT2_6_1_, applicatio2_."NAME" as NAME3_6_1_ from "APM_FUNCTION_PARAM" functions0_ inner join "APM_FUNCTION" function1_ on functions0_.ID_FUNCTION=function1_."ID_FUNCTION" left outer join "APM_APPLICATION" applicatio2_ on function1_.ID_APPLICATION=applicatio2_."ID_APPLICATION" where functions0_.ID_PARAM=?
Parameter: Parameter[id='25051', application='Application[id='25050', name='TST_APP_1', parametersCount='2', functionsCount='2']', name='b', value='20', functionsCount='1']
Hibernate: select functions0_.ID_PARAM as ID2_5_2_, functions0_.ID_FUNCTION as ID1_11_2_, function1_."ID_FUNCTION" as ID1_7_0_, function1_.ID_APPLICATION as ID4_7_0_, function1_."DESCRIPTION" as DESCRIPT2_7_0_, function1_."NAME" as NAME3_7_0_, applicatio2_."ID_APPLICATION" as ID1_6_1_, applicatio2_."DESCRIPTION" as DESCRIPT2_6_1_, applicatio2_."NAME" as NAME3_6_1_ from "APM_FUNCTION_PARAM" functions0_ inner join "APM_FUNCTION" function1_ on functions0_.ID_FUNCTION=function1_."ID_FUNCTION" left outer join "APM_APPLICATION" applicatio2_ on function1_.ID_APPLICATION=applicatio2_."ID_APPLICATION" where functions0_.ID_PARAM=?
Parameter: Parameter[id='25050', application='Application[id='25050', name='TST_APP_1', parametersCount='2', functionsCount='2']', name='a', value='100', functionsCount='2']
Functions:
SQL Error: 20000, SQLState: 42X04
Column 'A5.PARAMETERS0_.NAME' is either not in any table in the FROM list or appears within a join specification and is outside the scope of the join specification or appears in a HAVING clause and is not in the GROUP BY list. If this is a CREATE or ALTER TABLE  statement then 'A5.PARAMETERS0_.NAME' is not a column in the target table.

看起来 Hibernate 试图在 Map 本身的 @MapKey 注释中指定名称的属性,而不是目标实体。

是否可以使用具有目标实体属性的 Map 作为 ManyToMany 关系中的键?我怎样才能实现它?

感谢您的时间。

4

0 回答 0