在@ManyToMany关系中使用@MapKey和Map(JPA + Hibernate 4.1.9)

时间:2013-07-12 12:30:45

标签: hibernate jpa map

使用@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>

的persistence.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对象中的参数map之前,Everyhing工作正常。

执行此测试代码后(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关系中的键?我怎样才能实现它?

感谢您的时间。

0 个答案:

没有答案