我目前正在创建一个产品,也就是说,我指的是一个应用程序,它拥有自己的应用程序,但它的结构可以为不同的客户定制。
在后台使用spring可以很容易地更改所使用的服务/控制器的实现,我只是重载了bean。客户端我有angularJS,我可能需要重组模块并提供更多的灵活性,但这也应该很容易。
我真正唯一的问题在于JPA/Hibernate实体。
以下是我的配置:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="packagesToScan">
<array>
<value>com.xxx.entity</value>
</array>
</property>
<property name="persistenceUnitName" value="MyPU" />
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="jpaDialect" ref="jpaDialect" />
<property name="jpaProperties">
<props>
<prop key="hibernate.show_sql">${hibernate.show.sql}</prop>
<prop key="hibernate.format_sql">${hibernate.show.sql}</prop>
<prop key="hibernate.dialect">${database.dialect}</prop>
<prop key="hibernate.hbm2ddl.auto">validate</prop>
<!-- <prop key="hibernate.transaction.factory_class">org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory</prop> -->
</props>
</property>
<!-- <property name="sharedCacheMode" value="ENABLE_SELECTIVE" /> -->
</bean>
现在,让我们考虑产品中的以下对象,并为两个不同的客户进行两次定制:
// let's say this one is the basic one
class People{ String firstName;String lastName;}
// one client want us to add one felds
class PeopleClientOne extends People{String address;}
// another define two type of very different people while having one more field common for them
class People{String firstName; String lastName;String anotherField;}
class PeopleOneClientTwo extends People{...}
class PeopleTwoClientTwo extends People{...}
因此,我的问题是,考虑到以下限制,处理这一问题的最佳方式是什么:
- 没有完整的抽象数据库模型(比如有一个实体的大表和一个属性的大表)
- 我正在使用jackson,所以当我有继承时,我必须注释/更改超类的注释
- JPA/Jackson注释在同一个类上,我不想尽可能复制我的模型的类,并使其集中
以下是我的想法:
可扩展属性映射:
- 为支持此功能的每个项目类型再添加一个表
- 数据库可读性较差
- 无法在数据库中应用任何约束
背书用途:
- 重写具有相同包和相同名称的实体,并将其放在背书文件夹中,当然,它们只能添加新字段
- 数据库仍然可读且干净
- 一般来说,不建议使用背书,但它是可用的,所以它是有价值的
注意,我目前正在使用hibernate4,但如果需要,我可以升级到5。同样,如果您的解决方案是使用注释而不是XML,我可能稍后会改用注释,但今天不会。
欢迎任何替代贡献,即使它不符合我的要求。
我最终能够做到的是:
去掉只固定到一个包的数组
将数组定义为列表:
<util:list id="entityPackages">
<value>my.package.com.entity</value>
</util:list>
注入时使用EL转换为数组:
<property name="packagesToScan" value="#{entityPackages.toArray()}"/>
必要时在自定义客户端模块中过载:
<!-- either import file containing the old one or use profile -->
<util:list id="entityPackages">
<value>my.package.com.entity</value><!-- need to repeat that -->
<value>my.package.com.subentity</value>
</util:list>
因此,即使不在同一个包中,您也可以添加新的实体。
向实体添加继承
为了添加属性/扩展对象,您需要添加继承性。对于层次结构JOIN继承,只要将唯一约束和外键正确添加到适当的字段中,就不会花费太多。
@Inheritance(strategy=InheritanceType.JOINED)
如果你现在不能,因为你不能修改超类,你可能有两个选择:
- 使用Hibernate的XML配置
- 获取源(原始或.class)或类,添加注释,将其封装到一个jar中,并将其放入服务器/应用程序的认可文件夹中,不要忘记启用认可机制
严格来说,使用背书是危险的,必须非常小心。对于那些不知道的人,当您启用认可机制时,您可以指定一个文件夹,在每个类之前,将加载中的所有jar文件。即使是JRE,也可以确保该文件夹中的类将是加载到其他JAR中的类。
Jackson:使用mix-in进行继承
通常,对于Jackson,您需要添加注释@JsonSubTypes,这会迫使您将母类与子类链接起来。为了使这个类独立于这个孩子,你可以使用mixIn注释:
// make that bean get injected the instance of jackson's mapper and called either during the initialization or just after the spring's application context was loaded.
public afterStartup() {
myJackSonMapper.addMixIn(Site.class, SiteInheritanceMixIn.class);
}
// considering here that SubSite is inheriting from Site
@JsonSubTypes({
@JsonSubTypes.Type(value = SubSite.class, name = "subSite")
})
public abstract class SiteInheritanceMixIn{
}