为代码库设计可扩展的实体

时间:2016-08-05 10:17:52

标签: java spring hibernate maven jpa

我目前正在创建一个产品,我的意思是一个应用程序而不是它所拥有的应用程序,但它的结构可以为不同的客户端定制。

在后台使用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{...}

所以我的问题是考虑到以下限制,最好的办法是什么:

  • 没有完整的抽象数据库模型(比如有一个大的实体表和一个大的属性表)。
  • 我使用杰克逊,所以当我有继承时,我必须注释/更改超类的注释。
  • JPA / Jackson注释属于同一类,我不想尽可能复制我的模型类并保持集中。

以下是我的意思:

可扩展属性的地图:

  • 每个支持此项目的项目类型再添加一个表格。
  • 数据库可读性较差
  • 无法在数据库中应用任何约束。

背书的使用:

  • 重写具有相同包名和相同名称的实体并将其放在背书文件夹中,当然他们只能添加新字段。
  • 数据库仍然可读且干净
  • 一般来说,不建议使用背书,但它是可用的,因此值得。

注意我目前正在使用hibernate4但是如果需要我可以升级到5.如果您的解决方案是注释而不是XML,我可能会在以后切换到注释但不是今天。

任何替代贡献,即使它没有遵循我的要求,也欢迎。

1 个答案:

答案 0 :(得分:1)

在这里,我最终能够:

摆脱仅修复一个软件包的数组

将数组定义为列表:

<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,您需要添加注释@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{

}