【问题标题】:Design extensible entities for a codebase为代码库设计可扩展实体
【发布时间】:2016-08-05 10:17:51
【问题描述】:

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

在后台使用 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 注释在同一个类上,我不想尽可能重复我的模型的类并保持集中。

这是我的想法:

可扩展属性映射:

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

背书的用法:

  • 重写同包同名的实体,放到endorsed文件夹中,当然只能添加新的字段。
  • 数据库仍然可读且干净
  • 一般来说,不推荐使用 endorsed,但它是可用的,因此值得。

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

欢迎任何替代贡献,即使它不符合我的要求。

【问题讨论】:

    标签: java spring hibernate maven jpa


    【解决方案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)
    

    如果您现在因为无法修改超类而无法修改,您可能有 2 种选择:

    • 使用 Hibernate 的 XML 配置
    • 获取源(原始的或来自 .class)或类,添加注释,将其包装到 jar 中并将其放入服务器/应用程序的背书文件夹中,不要忘记启用认可的机制。

    严格来说,使用 endorsed 是危险的,必须非常小心。对于那些不知道的人,当您启用背书机制时,您指定一个文件夹,其中所有 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{
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-01
      • 2014-01-25
      • 1970-01-01
      • 2013-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多