来自不同EntityManagerFactory,PersistentUnit的实体的关系(连接)

时间:2014-02-27 10:53:31

标签: jpa eclipselink

我正在开发一个多租户Web应用程序。

使用的技术堆栈是 Spring web MVC 3.2.x, Spring Data JPA 1.4.3, EclipseLink 2.5.1。

我需要将所有常见数据(例如国家/地区,区域设置等)保存到单独的单一数据库模式中,并将所有租户的数据(例如USERS,HISTORY)保存到单个共享数据库模式(基于判别器的模型)中。

因此,实体模型将类似于

公共主数据实体-Country

@Entity
@Table (name = "country")
public class Country implements Serializable{

@GeneratedValue(generator = "assigned-by-code")
@GenericGenerator(name = "assigned-by-code", strategy = "assigned")
private String isoCode;

private String name;
}

租户实体

@Entity
@Multitenant
@Table (name = "user")
public class User {

@Id
@Column (name = "username")
private String userName;

@Column (name = "first_name", nullable = false)
private String firstName;

@Column (name = "last_name", nullable = false)
private String lastName;

@Column (name = "password", nullable = false)
private String password;
...
}

所以,我通过为MASTER和TENANT提供了两个不同的 entityManagerFactory 来实现上述隔离

FOR TENANT

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="raoutingDataSource" />
    <property name="persistenceUnitName" value="tenant" />
    <property name="jpaVendorAdapter">
        <bean
            class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
            <property name="database" value="MYSQL" />
            <property name="generateDdl" value="true" />
            <property name="showSql" value="true" />
        </bean>
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
            <prop key="hibernate.archive.autodetection">${hibernate.archive.autodetection}</prop>
            <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
            <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
            <prop key="SetBigStringTryClob">true</prop>
        </props>
    </property>
    <property name="packagesToScan">
        <list>
            <value>com.sample.entity</value>
        </list>
    </property>
    <property name="jpaPropertyMap">
        <map>
            <entry key="eclipselink.weaving" value="false" />
        </map>
    </property>
</bean>

FOR MASTER

<bean id="entityManagerFactory_master"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="driverManagerDataSource" />
    <property name="persistenceUnitName" value="master" />
    <property name="jpaVendorAdapter">
        <bean
            class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
            <property name="database" value="MYSQL" />
            <property name="generateDdl" value="true" />
            <property name="showSql" value="true" />
        </bean>
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
            <prop key="hibernate.archive.autodetection">${hibernate.archive.autodetection}</prop>
            <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
            <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
            <prop key="SetBigStringTryClob">true</prop>
        </props>
    </property>
    <property name="packagesToScan">
        <list>
            <value>com.sample.master</value>
        </list>
    </property>
    <property name="jpaPropertyMap">
        <map>
            <entry key="eclipselink.weaving" value="false" />
        </map>
    </property>
</bean>

现在我想做的是, 我想将country.isocode作为joincolummn与 User.java 相关联,因此我将 User.Java 更改为如下

@Entity
@Multitenant
@Table (name = "user")
public class User {

@Id
@Column (name = "username")
private String userName;

@Column (name = "first_name", nullable = false)
private String firstName;

@Column (name = "last_name", nullable = false)
private String lastName;

@Column (name = "password", nullable = false)
private String password;

@OneToOne(fetch = FetchType.LAZY, targetEntity = Country.class)
@JoinColumn(name = "countryisocode", referencedColumnName = "isocode")
private Country country;

...
}

但是在服务器启动时,它会给出错误,

Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Predeployment of PersistenceUnit [tenant] failed.
Internal Exception: Exception [EclipseLink-7250] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.ValidationException
Exception Description: [class com.sample.entity.User] uses a non-entity [class com.sample.master.Country] as target entity in the relationship attribute [field country].
    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.createPredeployFailedPersistenceException(EntityManagerSetupImpl.java:1954) [eclipselink-2.5.1.jar:2.5.1.v20130918-f2b9fc5]

请不要这么做,因为我不想将MASTER表复制到每个租户的数据源中,因此我需要在一个单独的地方。

1 个答案:

答案 0 :(得分:1)

由于租户工厂中不存在国家实体,因此无法理解该参考。您需要将持久性单元合并为一个以使引用工作。

如果必须将它们分开,EclipseLink支持它所谓的复合persistencde单元,允许复合成员之间的关系。这在此描述:https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Composite_Persistence_Units 而且似乎是你想要的。这允许您为租户使用复合持久性单元,而读/写应该遍历下面的不同持久性单元定义。