动态添加实体类属性与数据库表字段

打印 上一主题 下一主题

主题 933|帖子 933|积分 2801

  1. <?xml version="1.0"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <!-- Generated 2019-1-28 17:18:39 by Glory -->
  5. <hibernate-mapping>
  6.     <class name="com.glory.pms.sheet.model.AbstractSheetData" abstract="true">
  7.         <id name="objectRrn" column="OBJECT_RRN" type="long">
  8.             <generator class="native" />
  9.         </id>
  10.         
  11.         <version name="lockVersion" column="LOCK_VERSION" type="long"></version>
  12.         
  13.         <property name="orgRrn" column="ORG_RRN" type="long"></property>
  14.         <property name="isActive" column="IS_ACTIVE" type="yes_no"></property>
  15.         <property name="created" column="CREATED"></property>
  16.         <property name="createdBy" column="CREATED_BY" type="string"></property>
  17.         <property name="updated" column="UPDATED"></property>
  18.         <property name="updatedBy" column="UPDATED_BY" type="string"></property>
  19.         
  20.         <property name="style" column="STYLE" type="int"></property>
  21.         <property name="sheetId" column="SHEET_ID" type="string"></property>
  22.         <property name="sheetName" column="SHEET_NAME" type="string"></property>
  23.         <property name="sheetDesc" column="SHEET_DESC" type="string"></property>
  24.         <property name="sheetVersion" column="SHEET_VERSION" type="long"></property>        
  25.         <property name="equipmentId" column="EQUIPMENT_ID" type="string"></property>
  26.         <property name="equipmentType" column="EQUIPMENT_TYPE" type="string"></property>
  27.         <property name="partName" column="PART_NAME" type="string"></property>
  28.         <property name="recipe" column="RECIPE" type="string"></property>
  29.         <property name="stepName" column="STEP_NAME" type="string"></property>
  30.       
  31.         <property name="group1" column="GROUP1" type="string"></property>
  32.         <property name="group2" column="GROUP2" type="string"></property>
  33.         <property name="group3" column="GROUP3" type="string"></property>
  34.         <property name="group4" column="GROUP4" type="string"></property>
  35.         <property name="sheetType" column="SHEET_TYPE" type="string"></property>
  36.         <property name="sheetTemplate" column="SHEET_TEMPLATE" type="string"></property>
  37.         <property name="type" column="TYPE" type="string"></property>        
  38.         <property name="standardNumber" column="STANDARD_NUMBER" type="string"></property>
  39.         <property name="standardTime" column="STANDARD_TIME" type="string"></property>
  40.         <property name="checkCount" column="CHECK_COUNT" type="long"></property>
  41.         <property name="comments" column="COMMENTS" type="string"></property>
  42.         <property name="judge1" column="JUDGE1" type="string"></property>
  43.         <property name="judge2" column="JUDGE2" type="string"></property>
  44.         <property name="owner" column="OWNER" type="string"></property>
  45.         
  46.         <property name="currentItemSeq" column="CURRENT_ITEM_SEQ" type="java.lang.Long"></property>
  47.                
  48.         <bag name="checkDatas" inverse="false" lazy="true" cascade="all-delete-orphan" table="PMS_SHEET_CHECKT_LINE_DATA">
  49.             <key>
  50.                 <column name="SHEET_DATA_RRN" />
  51.             </key>
  52.             <one-to-many class="com.glory.pms.sheet.model.SheetCheckTableLineData" />
  53.         </bag>
  54.         <bag name="dataDatas" inverse="false" lazy="true" cascade="all-delete-orphan" table="PMS_SHEET_DATAS_LINE_DATA">
  55.             <key>
  56.                 <column name="SHEET_DATA_RRN" />
  57.             </key>
  58.             <one-to-many class="com.glory.pms.sheet.model.DatasLineData" />
  59.         </bag>
  60.         <bag name="edcSetDatas" inverse="false" lazy="true" cascade="all-delete-orphan" table="PMS_SHEET_EDCSETS_LINE_DATA">
  61.             <key>
  62.                 <column name="SHEET_DATA_RRN" />
  63.             </key>
  64.             <one-to-many class="com.glory.pms.sheet.model.EdcSetsLineData" />
  65.         </bag>
  66.         <bag name="partDatas" inverse="false" lazy="true" cascade="all-delete-orphan" table="PMS_SHEET_PARTS_LINE_DATA">
  67.             <key>
  68.                 <column name="SHEET_DATA_RRN" />
  69.             </key>
  70.             <one-to-many class="com.glory.pms.sheet.parts.model.PartsLineData" />
  71.         </bag>
  72.         <bag name="imageDatas" inverse="false" lazy="true" cascade="all-delete-orphan" table="PMS_SHEET_DATAS_IMAGE_DATA">
  73.             <key>
  74.                 <column name="SHEET_DATA_RRN" />
  75.             </key>
  76.             <one-to-many class="com.glory.pms.sheet.model.ImageData" />
  77.         </bag>
  78.         
  79.         <dynamic-component name="udf">
  80.                 </dynamic-component>
  81.     </class>
  82. </hibernate-mapping>
复制代码
Hibernate框架中用于对象关系映射(ORM)的配置文件,称为hibernate mapping file。这个文件界说了Java类与数据库表之间的映射规则,以及如何将类的属性映射到表的列。下面是对该文件的一些关键点剖析:


  • DTD声明:文件开头指定了利用的文档类型界说(DTD),这是为了确保文件遵循Hibernate 3.0映射文件的标准格式。
    元素:界说了一个名为com.glory.pms.sheet.model.AbstractSheetData的抽象类。这意味着它不会直接对应于数据库中的一个表,而是作为其他具体类的基类利用。
  • id元素:界说了主键属性objectRrn,并指定了利用native生成器策略来生成主键值。native意味着Hibernate会根据底层数据库选择最合适的主键生成策略(比方,对于MySQL可能是auto_increment,对于SQL Server可能是identity等)。
  • version元素:界说了乐观锁定版本字段lockVersion,这有助于在并发情况中检测和避免冲突。
  • property元素:为类中的每个非集合型属性界说了映射。每个property元素都指定了对应的数据库列名、Java类型以及可选的类型转换器(如type="yes_no"表现布尔值以’Y’/'N’的形式存储在数据库中)。
  • bag元素:界说了四个一对多关联的集合属性(checkDatas, dataDatas, edcSetDatas, partDatas, imageDatas)。bag是一个无序的集合,允许重复项,并且这里设置了cascade=“all-delete-orphan”,这意味着对父实体执行的全部操作(生存、更新、删除)都会级联到子实体,并且假如从集合中移除子实体,则会主动删除这些子实体。lazy="true"意味着这些集合默认是懒加载的,只有在实际访问时才会加载数据。
  • dynamic-component元素:提供了一种方式来动态地界说组件属性。在这个例子中,udf似乎被预留出来用于用户界说字段,但具体的字段没有在此文件中界说,可能是在代码中通过其他机制添加的。
    此映射文件还包含了一些特定的业务逻辑相关的属性,好比装备信息(equipmentId, equipmentType)、工单信息(sheetId, sheetName)、检查信息(checkCount)等,这些反映了应用步伐的具体需求。
二、dynamic-component元素

dynamic-component 元素在 Hibernate 中允许你界说一个可以动态添加或移除属性的组件。这意味着,你可以根据运行时条件来决定哪些字段应该被映射到数据库中,而不需要在编译时固定这些字段。这为应用步伐提供了更大的机动性,特别是在处理那些具有可变布局的数据模子时非常有用。
动态组件的工作原理


  • 配置文件中的声明

    • 在你的Hibernate映射文件中,利用 <dynamic-component> 标签来声明一个动态组件。
    • 你可以指定这个组件的名字(如udf),它将作为一个属性出如今对应的Java类中。
    • 在XML中,你不需要为每个可能的字段界说具体的 <property> 元素;相反,它们将在运行时通过其他机制添加。

  • 运行时属性设置

    • 属性可以在运行时通过提供键值对的方式添加到 dynamic-component 对应的实体对象中。
    • 这通常涉及到创建一个 Map<String, Object> 来存储这些动态属性,其中键是属性名,值是属性值。
    • 当生存实体时,Hibernate会遍历这个 Map 并主动将这些键值对映射到数据库表中的相应列。

  • 数据库表设计

    • 数据库表需要能够顺应这种动态性。一种常见的做法是利用EAV(Entity-Attribute-Value)模式,即为每个实体创建三列:实体ID、属性名和属性值。
    • 别的,也可以预先界说一组可能的字段,并在需要时仅添补那些实际利用的字段。

  • Java类中的表现

    • 在Java类中,dynamic-component 通常表现为一个 Map<String, Object> 或者自界说的类型,该类型可以序列化/反序列化为这样的 Map。
    • 比方,假如你有一个名为 udf 的 dynamic-component,那么在Java类中可能会有一个 Map<String, Object> udf; 字段。

  • 示例代码
  1. public class AbstractSheetData {
  2.     // ... other fields ...
  3.    
  4.     private Map<String, Object> udf = new HashMap<>();
  5.    
  6.     // Getter and setter for the dynamic component
  7.     public Map<String, Object> getUdf() {
  8.         return udf;
  9.     }
  10.     public void setUdf(Map<String, Object> udf) {
  11.         this.udf = udf;
  12.     }
  13.    
  14.     // ... other methods ...
  15. }
复制代码

  • 动态添加字段
  1. AbstractSheetData sheetData = new AbstractSheetData();
  2. sheetData.getUdf().put("customField1", "value1");
  3. sheetData.getUdf().put("customField2", 123L);
  4. // Save or update the entity with these dynamic fields
  5. session.saveOrUpdate(sheetData);
复制代码

  • 查询支持

    • 利用 HQL (Hibernate Query Language) 或 Criteria API 查询包含 dynamic-component 的实体时,可以通过 udf['key'] 的形式访问动态字段。

注意事项



  • 性能考虑:由于动态组件引入了额外的复杂性和机动性,它们可能会带来一定的性能开销,尤其是在查询优化方面。确保你的应用确实需要这种级别的机动性。
  • 维护难度:随着动态字段数量的增长,维护和理解数据模子可能会变得更加困难。文档化哪些字段是可以动态添加的是很重要的。
  • 验证与束缚:对于动态字段,实现验证逻辑和数据库束缚(如非空检查、唯一性等)可能更加复杂,因为它们不是静态界说的。
总之,dynamic-component 提供了一种强盛的方式来处理具有高度可变布局的数据模子,但应当谨慎利用,权衡其带来的机动性与潜在的复杂性和性能本钱。
三、mybatis 如何实现这个功能

MyBatis 是一个流行的持久层框架,它简化了Java应用步伐与数据库之间的交互。不像Hibernate那样提供完整的对象关系映射(ORM)解决方案,MyBatis 更加轻量级,允许开发者编写自界说SQL查询,同时提供了映射效果到Java对象的功能。
要在 MyBatis 中实现类似于 Hibernate dynamic-component 的功能——即动态添加或移除字段的能力——你可以接纳以下几种方法:
1. 利用 Map 来处理动态字段

最简朴的方法是直接利用 Java 的 Map<String, Object> 来表现具有动态属性的对象。你可以在你的 Mapper XML 文件中配置 SQL 查询来顺应这种布局。
示例:



  • Java 类:不需要为每个可能的动态字段界说特定的属性,而是用 Map<String, Object> 来生存这些字段。
  1. public class SheetData {
  2.     private Long objectRrn;
  3.     private Map<String, Object> dynamicFields = new HashMap<>();
  4.    
  5.     // Getters and Setters
  6. }
复制代码


  • Mapper XML:在插入或更新时,可以利用 <foreach> 标签遍历 dynamicFields 并构造动态的 SET 或 INSERT 语句。
  1. <insert id="insertSheetData" parameterType="SheetData">
  2.     INSERT INTO sheet_data (object_rrn, <include refid="dynamicColumns"/>)
  3.     VALUES (#{objectRrn}, <include refid="dynamicValues"/>)
  4. </insert>
  5. <!-- 定义动态列 -->
  6. <sql id="dynamicColumns">
  7.     <foreach collection="dynamicFields.keySet()" item="key" open="," separator="," close="">
  8.         ${key}
  9.     </foreach>
  10. </sql>
  11. <!-- 定义动态值 -->
  12. <sql id="dynamicValues">
  13.     <foreach collection="dynamicFields.entrySet()" item="entry" open="," separator="," close="">
  14.         #{entry.value}
  15.     </foreach>
  16. </sql>
复制代码
2. 动态 SQL 构建

MyBatis 支持通过 <if>, <choose>, <when>, <otherwise>, <trim>, <where>, <set>, 和 <foreach> 等标签构建动态 SQL。这使得你可以根据传入参数机动地调整 SQL 语句。
比方,在更新操作中,你可以只包含那些被修改过的字段:
  1. <update id="updateSheetData" parameterType="SheetData">
  2.     UPDATE sheet_data
  3.     <set>
  4.         <if test="dynamicFields.containsKey('customField1')">
  5.             custom_field_1 = #{dynamicFields.customField1},
  6.         </if>
  7.         <if test="dynamicFields.containsKey('customField2')">
  8.             custom_field_2 = #{dynamicFields.customField2},
  9.         </if>
  10.         <!-- 其他动态字段 -->
  11.     </set>
  12.     WHERE object_rrn = #{objectRrn}
  13. </update>
复制代码
3. 利用插件扩展 MyBatis

对于更复杂的场景,你可以考虑开发自界说插件或者拦截器来增强 MyBatis 的功能。插件可以用于拦截和修改执行的 SQL 语句、参数映射等。然而,这种方法通常需要更深入的理解和技术能力,并且应该谨慎评估其必要性和维护本钱。
4. 利用注解方式

假如倾向于利用注解而不是 XML 映射文件,MyBatis 也支持通过注解界说 SQL 语句。固然这种方式不如 XML 那样直观地支持复杂的动态 SQL 构建,但可以通过编程逻辑生成 SQL 语句并联合 @SelectProvider, @UpdateProvider 等注解来实现。
总结

MyBatis 提供了多种方式来处理动态字段的问题,从简朴的 Map<String, Object> 联合动态 SQL 到更复杂的插件开发。选择哪种方法取决于项目标具体需求和技术栈的选择。对于大多数情况来说,利用 Map 和内置的动态 SQL 构建工具就充足了。假如你的应用步伐确实需要更高的机动性,那么可以考虑进一步探索 MyBatis 的高级特性。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

祗疼妳一个

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表