我正在开发一个多租户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表复制到每个租户的数据源中,因此我需要在一个单独的地方。
答案 0 :(得分:1)
由于租户工厂中不存在国家实体,因此无法理解该参考。您需要将持久性单元合并为一个以使引用工作。
如果必须将它们分开,EclipseLink支持它所谓的复合persistencde单元,允许复合成员之间的关系。这在此描述:https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Composite_Persistence_Units 而且似乎是你想要的。这允许您为租户使用复合持久性单元,而读/写应该遍历下面的不同持久性单元定义。